\n )\n}\n",[59,36838,36839,36848,36853,36885,36889,36900,36916,36921,36925,36932,36947,36952],{"__ignoreMap":57},[62,36840,36841,36843,36846],{"class":64,"line":65},[62,36842,21046],{"class":68},[62,36844,36845],{"class":122}," CustomerList",[62,36847,206],{"class":72},[62,36849,36850],{"class":64,"line":76},[62,36851,36852],{"class":85}," // Customer type is automatically generated by Hilla\n",[62,36854,36855,36858,36861,36864,36866,36869,36872,36874,36877,36879,36882],{"class":64,"line":82},[62,36856,36857],{"class":68}," const",[62,36859,36860],{"class":72}," [",[62,36862,36863],{"class":149},"customers",[62,36865,976],{"class":72},[62,36867,36868],{"class":149},"setCustomers",[62,36870,36871],{"class":72},"] ",[62,36873,146],{"class":68},[62,36875,36876],{"class":122}," useState",[62,36878,760],{"class":72},[62,36880,36881],{"class":122},"Customer",[62,36883,36884],{"class":72},"[]>([]);\n",[62,36886,36887],{"class":64,"line":89},[62,36888,79],{"emptyLinePlaceholder":13},[62,36890,36891,36894,36896,36898],{"class":64,"line":95},[62,36892,36893],{"class":122}," useEffect",[62,36895,797],{"class":72},[62,36897,21525],{"class":68},[62,36899,126],{"class":72},[62,36901,36902,36905,36908,36910,36913],{"class":64,"line":101},[62,36903,36904],{"class":72}," CustomerService.",[62,36906,36907],{"class":122},"getCustomers",[62,36909,3229],{"class":72},[62,36911,36912],{"class":122},"then",[62,36914,36915],{"class":72},"(setCustomers);\n",[62,36917,36918],{"class":64,"line":107},[62,36919,36920],{"class":72}," }, []);\n",[62,36922,36923],{"class":64,"line":113},[62,36924,79],{"emptyLinePlaceholder":13},[62,36926,36927,36929],{"class":64,"line":129},[62,36928,2599],{"class":68},[62,36930,36931],{"class":72}," (\n",[62,36933,36934,36936,36939,36942,36944],{"class":64,"line":134},[62,36935,1896],{"class":72},[62,36937,36938],{"class":149},"ComboBox",[62,36940,36941],{"class":122}," items",[62,36943,146],{"class":68},[62,36945,36946],{"class":72},"{customers} />\n",[62,36948,36949],{"class":64,"line":156},[62,36950,36951],{"class":72}," )\n",[62,36953,36954],{"class":64,"line":161},[62,36955,379],{"class":72},[22,36957,36958],{},"I really enjoyed this presentation and I have seen enough to know that I want to put together a demo for Hilla.",[26,36960,1499],{"id":1498},[22,36962,36963],{},"I know it's not easy to get to conferences with budgets these days but if you have to pick 1 or 2 out of the year to go to this has to be on your list. Not only is Barcelona one of the most beautiful cities I have ever been to but this conference has to be on your bucket list. The speakers are top-notch, the sessions are amazing and Sergi does an amazing job at putting on one of the best conferences I have ever been to. At the end of the conference they announced the dates for next year's conference and I hope I will be there and I hope to see you all of there as well.",[22,36965,36966],{},[653,36967],{"alt":36968,"src":36969},"Next year","/images/blog/2024/06/03/spring_io_2025.jpg",[1527,36971,36972],{},"html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .suaqH, html code.shiki .suaqH{--shiki-default:#22863A;--shiki-dark:#85E89D;--shiki-sepia:#22863A}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .spoOn, html code.shiki .spoOn{--shiki-default:#E36209;--shiki-dark:#FFAB70;--shiki-sepia:#E36209}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}html pre.shiki code .s4-Ip, html code.shiki .s4-Ip{--shiki-default:#6A737D;--shiki-dark:#6A737D;--shiki-sepia:#6A737D}",{"title":57,"searchDepth":76,"depth":76,"links":36974},[36975,36978,36979,36980,36981,36982,36988],{"id":36052,"depth":76,"text":36053,"children":36976},[36977],{"id":36068,"depth":82,"text":36069},{"id":36094,"depth":76,"text":36095},{"id":36109,"depth":76,"text":36110},{"id":36149,"depth":76,"text":36150},{"id":36165,"depth":76,"text":36166},{"id":36172,"depth":76,"text":36173,"children":36983},[36984,36985,36986,36987],{"id":36245,"depth":82,"text":36246},{"id":36278,"depth":82,"text":36279},{"id":36607,"depth":82,"text":36608},{"id":36817,"depth":82,"text":36818},{"id":1498,"depth":76,"text":1499},{"_id":36990,"path":36991,"title":36992,"description":36993,"meta":36994,"body":37000},"content/blog/2024/01/21/java-champion.md","/blog/2024/01/21/java-champion","I'm a Java Champion","On Monday, January 8th, I woke up expecting just another day. Little did I know that this would become a day etched in my memory forever. In this post I would like to tell you about my career and how I became I Java Champion.",{"slug":36995,"date":36996,"published":13,"tags":36997,"author":17,"cover":36999,"excerpt":-1},"java-champion","2024-01-21T17:00:00.000Z",[15,36998],"Meta","./java-champion-cover.png",{"type":19,"value":37001,"toc":37174},[37002,37011,37017,37020,37024,37027,37030,37034,37037,37040,37043,37046,37050,37053,37056,37059,37062,37065,37069,37072,37075,37078,37082,37085,37088,37091,37094,37097,37101,37104,37108,37111,37114,37117,37121,37124,37127,37131,37134,37137,37142,37151,37154,37158,37161,37164,37167,37171],[22,37003,37004,37005,37010],{},"On Monday, January 8th, I woke up expecting just another day. Little did I know that this would become a day etched in my memory forever. The day started with me helping my girls prepare for school. Once I settled into work and checked a few emails, I noticed a notification for a tweet mentioning me. At 8:46 AM, I received news from the ",[677,37006,37009],{"href":37007,"rel":37008},"https://twitter.com/Java_Champions/status/1744354879487758359",[681],"Java Champions Twitter account","; I had been named a Java Champion.",[22,37012,37013],{},[653,37014],{"alt":37015,"src":37016},"Java Champions Tweet","/images/blog/2024/01/21/tweet.png",[22,37018,37019],{},"In this article, I'll share my story as it led to the title I now hold. If you're only interested in learning about the Java Champions program, feel free to skip to the end.",[26,37021,37023],{"id":37022},"my-story","My Story",[22,37025,37026],{},"My story begins when I was a senior in high school in 1996, at the dawn of the internet era. My mother enrolled me in a typing class, much to my dismay. I remember telling her that I had no plans to become a writer or a secretary. Little did I know how significant that class would turn out to be. By the end of it, I had learned to type approximately 68 words per minute.",[22,37028,37029],{},"Shortly after graduation, my mother gave me an old computer. After making some upgrades, I got it up and running. This sparked my fascination with computers, how they were assembled, and the software that powered them.",[636,37031,37033],{"id":37032},"my-college-journey","My College Journey",[22,37035,37036],{},"As a landscaper, I was accustomed to hard work. My routine involved landscaping in the summer and plowing in the winter. Being out late in the cold was exhausting, and I grew to dislike it. Towards the end of one summer, I realized I needed a change, but I was unsure of my options. I had always enjoyed math, science, and mechanical engineering in high school, but without a scholarship or funds for college, it seemed out of reach.",[22,37038,37039],{},"Eventually, I discovered Remington College, a two-year technical college. I applied, got accepted, and started my journey. Although I don't remember the specifics of the financials, I recall the lengthy process of paying off my student loan.",[22,37041,37042],{},"During college, I learned about a programming language called Java. My classmates often complained about the complexity of starting with an Object-Oriented Language like Java instead of something simpler. But, naive and eager to learn, I embraced the challenge.",[22,37044,37045],{},"In March 2000, I graduated. While studying, I taught myself how to build web applications using HTML/CSS in FrontPage, eventually transitioning to Dreamweaver and learning ColdFusion. I found clients online and undertook side jobs building websites. My most significant project was for an online luggage retailer, for whom I built a complete e-commerce application.",[636,37047,37049],{"id":37048},"heading-out-west","Heading out West",[22,37051,37052],{},"I used my connections to join a California-based startup called Five9 as their seventh employee. My initial role was a level 2 support specialist, acting as a liaison between customers and engineers. As the company grew, I increasingly worked with engineers, helping to develop our primary product: call center software using VOIP.",[22,37054,37055],{},"I learned a lot in this role, particularly from repeatedly answering the same customer queries. This led me to develop training videos to address common problems. This was pre-YouTube era and, in hindsight, my first attempt to convert my knowledge into content to help others.",[22,37057,37058],{},"I also worked on a few of our websites that used Struts, which was my first exposure to a Java Framework and I thoroughly enjoyed it.",[22,37060,37061],{},"Living in Oakland, near Berkeley College, I used my free time to expand my knowledge in Java and ColdFusion. I hired a tutor from Craigslist, a Berkeley TA, to deepen my understanding of the Java programming language. Concurrently, I explored web building using ColdFusion, which became a significant part of my early career.",[22,37063,37064],{},"I cherished my time at Five9 and living in the Bay Area. However, after two years, I was homesick and didn't see a long-term future there, so I decided to return to the Cleveland area.",[636,37066,37068],{"id":37067},"web-development-career","Web Development Career",[22,37070,37071],{},"I started my career at a company called Letternine, which primarily focused on contract web development. They had a custom CMS built with ColdFusion for creating custom solutions for clients. As an integral part of a small team, I had the opportunity to work on some exciting projects. This is where I began engaging with the community, using an open-source blogging platform called BlogCFC. I frequently blogged about my work and developments within the ColdFusion community.",[22,37073,37074],{},"Later, I joined another company in the Cleveland area, STERIS. I undertook web development on various platforms, including our customer-facing website and an e-commerce platform written in Java + Oracle. My role involved a significant amount of integration with this platform. I stayed there for four years and greatly enjoyed my time.",[22,37076,37077],{},"Throughout my career in ColdFusion, I made some good friends in the community. One such friend was Jason Delmore, who used to be the product manager for ColdFusion. Jason moved to an insurance company called Markel as the Director of Technology and brought me into the organization. I worked on a few mission-critical applications and was part of the team that migrated some applications over to Java/Groovy/Grails. I had a great time working there, and I still have some close friends from that period. During my stint at Markel, I got married, and I was fortunate to have Jason at my bachelor party and Jason, Sam, and Lance at my wedding.",[636,37079,37081],{"id":37080},"coding-bootcamp","Coding Bootcamp",[22,37083,37084],{},"After about 15 years of coding, I sought a new challenge. Having coded for so long, I began pondering the next steps. While some transition into management, I discovered a different path. I learned about a new coding bootcamp, Tech Elevator, which had just started in the Cleveland area. I was already familiar with them, having participated in panel discussions and meetup groups they held.",[22,37086,37087],{},"Interestingly, I found out that the co-founder, David, and I attended the same high school. The world is indeed small.",[22,37089,37090],{},"I joined them as a Curriculum Developer, tasked with developing course content for both instructors and students. This role allowed me to utilize my content creation skills. I learned a lot from David and my colleagues, greatly improving my technical writing skills. Occasionally, I also taught in the classroom, sharing the knowledge I had included in the curriculum.",[22,37092,37093],{},"However, the most rewarding part of this experience was the lives we helped transform. Witnessing students from all backgrounds enter our program and depart with a new career they were genuinely excited about was truly amazing. The daily interaction with the students made coming to work exhilarating.",[22,37095,37096],{},"Unfortunately, the COVID-19 pandemic hit. Despite Tech Elevator's successful transition to a remote program, it wasn't the same for me. Hence, I decided to seize another opportunity.",[636,37098,37100],{"id":37099},"enterprise-java-architect","Enterprise Java Architect",[22,37102,37103],{},"I took a position as an Enterprise Java Architect at a company called Briebug. By the time I left, I had been promoted to Principal Architect. In this role, I worked for one of the world's largest logistics companies, with many responsibilities. I worked on several mission-critical systems that processed millions of transactions per hour. These systems were distributed and built on Java, Spring Boot, Spring Cloud, among other projects in this ecosystem. I also interviewed potential Java and Spring candidates and helped develop our interview process. Despite my enjoyment of the work and my colleagues, I decided to take a new opportunity when my dream job came calling.",[636,37105,37107],{"id":37106},"spring-developer-advocate","Spring Developer Advocate",[22,37109,37110],{},"I first learned about an opening in the Developer Advocacy team at VMware from a friend. They were specifically looking for an advocate for Spring, a role that seemed perfect for me. I had been an unofficial Spring advocate for some time, having worked with and taught Spring, so this was my dream job.",[22,37112,37113],{},"Securing this position was not straightforward and required a six-month process. However, as the saying goes, nothing worth having comes easily. Eventually, I was hired as a Spring Developer Advocate. This new role allowed me to educate our customers and the community through various mediums. I was tasked with producing YouTube videos, writing technical articles about Spring Ecosystem projects, speaking at conferences, and more.",[22,37115,37116],{},"Being actively involved in the Java community for the past two years has significantly contributed to my receipt of the Java Champion award. I genuinely enjoy what I do every day, and I never take that for granted.",[636,37118,37120],{"id":37119},"thoughts-on-my-career","Thoughts on my Career",[22,37122,37123],{},"At no point in my career have I been coasting. I've always wanted to do this, but it hasn't been easy. Every step of my journey has required extensive learning, late-night coding sessions, and failure. Success has never come easily, which is why I have a deep appreciation for it. This experience has also fostered a sense of respect for my peers and empathy towards them, as I understand the challenges they have faced.",[22,37125,37126],{},"I have a deep passion for learning and teaching others. I hope to continue this throughout my career.",[26,37128,37130],{"id":37129},"what-is-a-java-champion","What is a Java Champion?",[22,37132,37133],{},"So, that's my story. With this context, you might understand why I'm so thrilled about this title being bestowed upon me. It's a result of my hard work throughout my career.",[22,37135,37136],{},"You might be wondering, what is a Java Champion? This is someone who advocates for the Java programming language within the community. They can do this through various mediums such as writing books, speaking at conferences, contributing to open-source projects, and more. There is an insightful quote from the JavaOne Conference in 2009 that I believe aptly describes a Java Champion:",[29685,37138,37139],{},[22,37140,37141],{},"\"The Java Champions community was started by Sun at the 2005 JavaOne conference to recognize key influencers in the Java community. Java Champions are influential Rock Star presenters and Java technology educators, authors, and consultants; Java platform event organizers; and others within the Java technology ecosystem. For the third consecutive year, Java Champions have contributed to the JavaOne conference process as technical reviewers for paper submissions, have been recognized for their achievements, and have shared their thoughts about the state of the Java platform at their BOF sessions.”",[22,37143,37144,37145,37150],{},"Oracle currently oversees the ",[677,37146,37149],{"href":37147,"rel":37148},"https://dev.java/community/jcs/",[681],"Java Champions Program"," but does not vote on who is awarded this title. Becoming a Java Champion requires nomination by a current champion, followed by a voting process. I won't delve into the details of that process just yet. The Java Champions Program comprises some of the most talented individuals in our community, so being nominated and voted in by them is truly an honor.",[22,37152,37153],{},"Out of the 10-14 million Java developers worldwide, I am the 385th to be named a Java Champion. This recognition makes all my hard work over the years worth it. Many people have asked me what benefits come with being a Java Champion. Besides receiving a nice polo and jacket, which I will proudly share once I receive them, the most significant benefit is the recognition that comes with the title. I will wear this recognition like a badge of honor throughout my career.",[26,37155,37157],{"id":37156},"whats-next","What’s Next?",[22,37159,37160],{},"The reward for hard work is often more hard work. Through this nomination process, I've gained some insights. Firstly, I need to create a space to keep track of all my career achievements. It's important to remember the work that has shaped my career. Consequently, I plan to create a page to showcase my career highlights.",[22,37162,37163],{},"Secondly, I've realized that despite writing for my blog and newsletter, I aspire to contribute more to other publications. One of my long-term goals is to write a book, but I am not ready for that just yet. Instead, I aim to write for other outlets this year, with some projects already underway.",[22,37165,37166],{},"Lastly, I want to be more involved in the Java Community. I'm exploring ways to achieve this, and it will certainly be a focus for me this year and in the future.",[26,37168,37170],{"id":37169},"thank-you","Thank you",[22,37172,37173],{},"I want to express my gratitude to everyone who reached out to me with such kind words. I have read all of your messages and sincerely appreciate your sentiments. My hope is to carry this title with honor and to serve the Java community well.",{"title":57,"searchDepth":76,"depth":76,"links":37175},[37176,37185,37186,37187],{"id":37022,"depth":76,"text":37023,"children":37177},[37178,37179,37180,37181,37182,37183,37184],{"id":37032,"depth":82,"text":37033},{"id":37048,"depth":82,"text":37049},{"id":37067,"depth":82,"text":37068},{"id":37080,"depth":82,"text":37081},{"id":37099,"depth":82,"text":37100},{"id":37106,"depth":82,"text":37107},{"id":37119,"depth":82,"text":37120},{"id":37129,"depth":76,"text":37130},{"id":37156,"depth":76,"text":37157},{"id":37169,"depth":76,"text":37170},{"_id":37189,"path":37190,"title":37191,"description":37192,"meta":37193,"body":37199},"content/blog/2024/01/15/developer-advocate.md","/blog/2024/01/15/developer-advocate","What is Developer Relations and How do you define Developer Advocacy?","In this article, I'll provide an overview of Developer Relations (DevRel) and guide you on how to get started. If you're considering this as a potential career, I hope to inform and inspire you.",{"slug":37194,"date":37195,"published":13,"tags":37196,"author":17,"cover":37198,"excerpt":-1},"developer-advocate","2024-01-15T17:00:00.000Z",[37197],"DevRel","./developer-advocate.png",{"type":19,"value":37200,"toc":37841},[37201,37209,37212,37216,37219,37225,37228,37232,37235,37238,37243,37246,37249,37257,37266,37274,37282,37290,37299,37302,37308,37312,37321,37326,37329,37332,37376,37379,37384,37387,37392,37395,37400,37403,37407,37412,37415,37419,37422,37448,37452,37455,37459,37464,37467,37472,37475,37478,37486,37491,37494,37502,37508,37511,37516,37519,37522,37525,37530,37535,37543,37546,37549,37555,37558,37596,37603,37612,37617,37620,37625,37630,37633,37675,37680,37683,37686,37690,37693,37697,37703,37706,37750,37752,37757,37773,37777,37794,37798,37812,37817,37833,37835,37838],[22,37202,37203,37204,37208],{},"I recently gave a presentation on what Developer Advocacy is and I thought I would share my thoughts here. If you want to find the slides for that talk you can get them ",[677,37205,8441],{"href":37206,"rel":37207},"https://github.com/danvega/slides/blob/main/developer-relations-playbook.pdf",[681]," and if you’re interested in having me give this talk at your meetup or conference please contact me.",[22,37210,37211],{},"In this article, I'll provide an overview of Developer Relations (DevRel) and guide you on how to get started. If you're considering this as a potential career, I hope to inform and inspire you. Even if you're not planning to pursue this career in the near future, you can still advocate for a product or service you're passionate about in your current organization or on your own. You don't need permission to promote your favorite open-source software!",[26,37213,37215],{"id":37214},"what-is-developer-relations","What is Developer Relations",[22,37217,37218],{},"When discussing Developer Relations (DevRel), it's important to start with the job title. This profession has existed for a long time under various names. I recall early in my career as a ColdFusion developer, there was a gentleman named Ben Forta. He was a ColdFusion Evangelist for Adobe. He impressed me (and still does) by demonstrating the capabilities of ColdFusion, answering questions, and speaking at conferences. I remember admiring Ben and often wondering to myself, how does one pursue that position as a career path.",[22,37220,37221],{},[653,37222],{"alt":37223,"src":37224},"Evangelism","/images/blog/2024/01/15/evangelism.png",[22,37226,37227],{},"These days you will often hear to these individuals referred to as Developer Advocates. You will often hear the team of advocates referred to as Developer Relations and you may even have more than one of them inside an organization.",[636,37229,37231],{"id":37230},"how-do-you-define-developer-relations-devrel","How do you Define Developer Relations (DevRel)",[22,37233,37234],{},"Things start to get interesting here. If you ask 10 Developer Advocates to define their role, you're likely to receive 10 different answers with some overlap. This is because there's no standardized definition for a Developer Advocate. Moreover, if you search for job postings, you'll find varying responsibilities under the same title.",[22,37236,37237],{},"This diversity results from the fact that Developer Relations (DevRel) means different things to different companies and even to different departments within the same company. To understand what Developer Advocacy might imply for various companies, let's begin with the definition of advocacy:",[29685,37239,37240],{},[22,37241,37242],{},"“public support for or recommendation of a particular cause or policy.”",[22,37244,37245],{},"To illustrate, some of the most pressing advocacies for young people include climate change, mental health, education, and gender equality. Nevertheless, it's crucial to note that the most effective advocacy will be for a cause you truly care about. You can be an advocate in your communities for anything of these things but you should probably believe in it and or be passionate about it before you advocate for it. It’s hard to advocate for Climate Change if you don’t believe in it.",[22,37247,37248],{},"The is where the combination of Developer and Advocacy comes together. If you’re passionate about Developers and the path they have taken in their career to get where they are you are probably passionate about helping them. I remember struggling as a new developer learning new things and constantly failing and just how hard it was to get through that stage of my career. I remember questioning myself and weather or not I was smart enough to do this. This struggle has led me to a life where I truly enjoy helping others because I remember being in that position and when something finally clicked I remember that feeling of excitement that I finally understood it. I have a few quotes from Developer Advocates that I am excited to share with you around what they think it means to be a developer advocate.",[29685,37250,37251],{},[22,37252,37253,37254],{},"“In my opinion, someone in developer relations serves as an advocate for the tech community within their company.” - ",[646,37255,37256],{},"Emily Freeman - @editingemily",[22,37258,37259,37260,37265],{},"I greatly appreciate this perspective as, for me, advocacy begins with the community you're serving. These are the individuals you're striving to assist, and I believe Emily encapsulates this perfectly in her insightful article titled \"",[677,37261,37264],{"href":37262,"rel":37263},"https://emilyfreeman.io/blog/developer-relations-more-than-the-art-of-talking-good",[681],"Developer Relations: (More Than) the art of talking good","\". While addressing the issues your community struggles with is a part of it, having empathy for their experiences is equally, if not more, essential.",[29685,37267,37268],{},[22,37269,37270,37271],{},"“When I say that I’m a developer advocate, a lot of people wonder what that means, and they’re “Oh, are you just like a paid shill for GitHub? No, no. If anything, I kind of think it’s the inverse. I’m actually a paid shill for our users, because those are the people I care about, the community members.” - ",[646,37272,37273],{},"Christina Warren - Senior Developer Advocate at GitHub",[22,37275,37276,37277,2755],{},"Christina is an exceptional advocate with a deep understanding of her audience. Although we are paid by a particular company, our primary focus is serving the developers using our products. To hear more from Christina on this topic, you can listen to her guest appearance on the ",[677,37278,37281],{"href":37279,"rel":37280},"https://changelog.com/podcast/518",[681],"Changelog podcast",[29685,37283,37284],{},[22,37285,37286,37287],{},"“So the way that I run my developer relations team focuses on three different things. The first is around education, the second is around community, and the third is around the product.” - ",[646,37288,37289],{},"Lee Robinson - Vercel",[22,37291,37292,37293,37298],{},"I appreciate this quote by Lee Robinson because it encapsulates our role as Developer Advocates. Lee also appeared as a ",[677,37294,37297],{"href":37295,"rel":37296},"https://changelog.com/podcast/493",[681],"guest on the Changelog podcast",", where he shared his insights on Developer Advocacy.",[22,37300,37301],{},"I align with Lee's definition of a Developer Advocate. To me, it represents the intersection of community and company through education. I aid in educating the community through conference talks, podcasts, videos, articles, and more. Through this process, I identify the challenges our community faces and assist in answering their questions. I also bring this feedback back to my company.",[22,37303,37304],{},[653,37305],{"alt":37306,"src":37307},"Community/Education/Company","/images/blog/2024/01/15/community-education-company.png",[636,37309,37311],{"id":37310},"where-does-devrel-fit-in-an-organization","Where does DevRel fit in an organization?",[22,37313,37314,37315,37320],{},"The reason you get so many different answers when it comes to defining Developer Relations is because it means something different to different companies. I reached out to ",[677,37316,37319],{"href":37317,"rel":37318},"https://twitter.com/kelseyhightower",[681],"Kelsey Hightower"," on Twitter and asked him how he defines it and this was his response.",[22,37322,37323],{},[653,37324],{"alt":37319,"src":37325},"/images/blog/2024/01/15/kelsey.png",[22,37327,37328],{},"Kelsey is always ready to give his opinion, and I found his response to be incredibly candid and honest. Different companies have different objectives, so the company that employs you will typically define their goals.",[22,37330,37331],{},"So, where does Developer Relations (DevRel) fit within an organization - sales, marketing, or engineering? The answer varies depending on the organization's goals. It's also complex because Developer Relations combines many disciplines. Developer Relations is about:",[915,37333,37334,37340,37346,37352,37358,37364,37370],{},[37,37335,37336,37339],{},[646,37337,37338],{},"Engagement",": Establishing connections with developers and tech audiences",[37,37341,37342,37345],{},[646,37343,37344],{},"Community Building",": Fostering and nurturing developer communities",[37,37347,37348,37351],{},[646,37349,37350],{},"Advocacy",": Championing developers' needs for superior product experiences",[37,37353,37354,37357],{},[646,37355,37356],{},"Organizational Representation",": Promoting our products within the developer community",[37,37359,37360,37363],{},[646,37361,37362],{},"Technical Expertise",": Deep understanding of our product to educate and support developers",[37,37365,37366,37369],{},[646,37367,37368],{},"Resource Creation",": Developing demos, code samples, tutorials, and more",[37,37371,37372,37375],{},[646,37373,37374],{},"Beyond the Basics",": A role that encompasses a broad spectrum of skills and responsibilities",[22,37377,37378],{},"Different organizations may prioritize certain aspects over others. Let's look at a few examples of companies with Developer Relations (DevRel) teams and how their goals might vary.",[22,37380,37381],{},[646,37382,37383],{},"Startup",[22,37385,37386],{},"If you're part of a startup with new products, your primary goal might be raising product awareness. You could attend conferences and engage in introductory discussions with developers, letting them know you have solutions to their problems. Promoting your products through education should be your main objective. Once people start using your products, gather feedback regarding their questions or challenges. This valuable information can be relayed back to your product teams for improvements.",[22,37388,37389],{},[646,37390,37391],{},"Developer Tooling",[22,37393,37394],{},"If your company produces developer tooling, Developer Relations (DevRel) is essential for your company's success. It's crucial to engage with developers and gather their feedback. A positive user experience plays a significant role in product adoption, and feedback from beginners to advanced users can facilitate this. In this context, establishing a direct line with the engineering team to share feedback and make product improvements is beneficial. Additionally, education is a key component to ensure developers understand how to use the tools effectively.",[22,37396,37397],{},[646,37398,37399],{},"Developer APIs",[22,37401,37402],{},"There are often DevRel teams inside of companies where an API is part of a product but not the main product like it was in the previous example. These APIs are a set of tools and protocols used by developers to build software and applications. While the API serves as an important tool, it is not the main product offered by the company. Instead, it acts as an instrumental component that allows developers to interact with the software or platform, enabling them to create an array of applications and functionalities.",[636,37404,37406],{"id":37405},"measuring-impact","Measuring Impact",[22,37408,37409],{},[653,37410],{"alt":37406,"src":37411},"/images/blog/2024/01/15/measuring-impact.png",[22,37413,37414],{},"Regardless of where DevRel fits within an organization, one of the primary challenges is determining how to measure impact. Collaboration within the team is crucial here, as different individuals may have varying strengths. It's important to work with your managers to define team goals and ascertain your individual role. Tracking your contribution to different departments like sales and marketing throughout this processes is essential.",[636,37416,37418],{"id":37417},"what-a-developer-advocate-isnt","What a Developer Advocate Isn’t",[22,37420,37421],{},"Now that we have defined what developer relations is I think it’s just as important to define what it isn’t.",[915,37423,37424,37430,37436,37442],{},[37,37425,37426,37429],{},[646,37427,37428],{},"NOT a free vacation"," - While traveling may be a perk of the job, it's not akin to a free vacation. It doesn't guarantee complimentary meals or luxurious accommodation. Often, you have to work within budgets, which could mean dining in airport terminals before flights. While some trips may lead to exotic locations, many will be dictated by customer locations, including brief visits to places that may not be on your personal travel wishlist.",[37,37431,37432,37435],{},[646,37433,37434],{},"NOT Just Sales"," - DevRel is not about directly driving sales. Instead, it focuses on long-term community engagement and support, which can indirectly contribute to sales through trust and relationship building.",[37,37437,37438,37441],{},[646,37439,37440],{},"NOT Just Marketing"," - While DevRel involves communication and community building, it is not solely a marketing role. It's more about creating genuine relationships and understanding developers' needs than just promoting products.",[37,37443,37444,37447],{},[646,37445,37446],{},"NOT Just about writing code"," - While technical expertise is crucial, DevRel is as much about communication, empathy, and community building as it is about coding.",[26,37449,37451],{"id":37450},"developer-relations-playbook","Developer Relations Playbook",[22,37453,37454],{},"If you're intrigued by what you've heard so far, you may be wondering, \"How do I get into DevRel?\" In this section, we will cover things you can start doing now, including content creation along with various tips and tricks.",[636,37456,37458],{"id":37457},"career-path","Career Path",[22,37460,37461],{},[653,37462],{"alt":37458,"src":37463},"/images/blog/2024/01/15/career-path.png",[22,37465,37466],{},"These are some things you can start focusing on now, whether a career in DevRel is your aspiration, or if it's just something you want to do for fun.",[22,37468,37469],{},[646,37470,37471],{},"Be a Developer First",[22,37473,37474],{},"To be a Developer Advocate, I believe the first prerequisite is to be a developer. While there may be exceptions, for the most part, one needs to have developer experience. I’m not going to put a number of years of required experience, as it varies among individuals.",[22,37476,37477],{},"Undergoing the process of becoming a developer, including the challenging nights of studying and trial and error, fosters empathy. It helps understand the experiences of developers in your community. Building things is also important, a sentiment I strongly agree with, as expressed by Scott Hanselman at Microsoft.",[29685,37479,37480],{},[22,37481,37482,37483],{},"“The problem is that there are a lot of young people who have never built anything. How can you advocate for developers if you've never built anything? I tend to disappear for several months, ship something, and then talk about it.” - ",[646,37484,37485],{},"Scott Hanselman - Microsoft",[22,37487,37488],{},[646,37489,37490],{},"Share your passion",[22,37492,37493],{},"Start sharing your passion through various mediums such as writing, public speaking, videos, podcasts, and more. I didn't begin creating content because I aspired to be a developer advocate. I did it because I enjoyed learning something new and wanted to share it with the community. I also started when I encountered a problem and couldn't find a solution. Once I did, I wanted to ensure others could quickly find the same solution. Through this process, I became involved in the communities I was part of, which has been one of the best things I have done for my career.",[29685,37495,37496],{},[22,37497,37498,37499],{},"“Once you start down the path of enjoying something and sharing it, you're already well on the way to becoming a developer advocate.” ",[646,37500,37501],{},"Mark Heckler - Microsoft",[22,37503,37504,37505],{},"S",[646,37506,37507],{},"tart thinking of yourself as a brand",[22,37509,37510],{},"It’s never too early to start thinking of yourself as a personal brand. This can lead to many unique opportunities down the line including Developer Relations. You can start to build an online presence that allows you to establish and showcase your expertise. When it comes to Social Media I usually recommend picking one where your audience is.",[22,37512,37513],{},[646,37514,37515],{},"Advocate on your own",[22,37517,37518],{},"By sharing content, you can become a developer advocate for your favorite open-source project, or within your current company. Share what you're passionate about and generate excitement for the languages, frameworks, and projects that inspire you. The good news is you don't need permission to advocate for the tech that you find inspiring.",[636,37520,31404],{"id":37521},"content-creation",[22,37523,37524],{},"There are multiple methods to build a community through education. This section will highlight a few. Excelling at all of them isn't necessary. Instead, concentrate on mastering one or two before exploring others. Each of these topics could warrant its own article, but they will be briefly summarized here for conciseness.",[22,37526,37527],{},[646,37528,37529],{},"Public Speaking",[22,37531,37532],{},[653,37533],{"alt":37529,"src":37534},"/images/blog/2024/01/15/public-speaking.jpeg",[29685,37536,37537],{},[22,37538,37539,37540],{},"“Public speaking continues to be one of the number one fears that many people have. There's that common nightmare where you're standing up in front of your classmates at school giving a report and, all of a sudden, you're naked or you've forgotten what you wanted to say next. It takes a real act of courage to stand up and present yourself as an authority, especially to a group of your peers.” - ",[646,37541,37542],{},"Scott Davis - ThoughtWorks",[22,37544,37545],{},"It certainly requires bravery, but I want to clarify some misconceptions and hopefully inspire you to take that leap. Firstly, you don't need to be an extrovert. I know several introverts, including myself, who find comfort in discussing their passions in front of others. Another misconception is that you need to be an expert on the topic you're speaking about, which is far from the truth. You simply need to be one step ahead of your intended audience. This is why its crucial to be clear on what your presentation will cover and what the expected outcome is.",[22,37547,37548],{},"Additionally, some people believe that if someone has already written an article or presented on a topic, they should not do it. But if you look at the water aisle at your grocery store, you'll find all kinds of water with different packaging. I love the quote, \"There are no unique messages, only unique messengers\". You have a unique perspective and experience to share, so go ahead and share it.",[22,37550,37551],{},[653,37552],{"alt":37553,"src":37554},"Unique Messengers","/images/blog/2024/01/15/water.png",[22,37556,37557],{},"If you want to get better at speaking start small and move your way up",[915,37559,37560,37566,37572,37578,37584,37590],{},[37,37561,37562,37565],{},[646,37563,37564],{},"Lunch and Learns"," - I love Lunch and Learns because its usually with a group of your peers who you are already familiar with. There is no pressure here, just share your experience with a particular tool.",[37,37567,37568,37571],{},[646,37569,37570],{},"User Group Meetings"," -Local Meetup groups are always in search of new speakers. We're open to first-time speakers as well. Presentations can range from a brief 5-10 minute informal code share to an hour-long talk, and anything in between.",[37,37573,37574,37577],{},[646,37575,37576],{},"Podcasts"," - I appreciate podcasts as a means to share your story and express your passions. In this format, there's no need to write any code, which usually eases concerns about appearance.",[37,37579,37580,37583],{},[646,37581,37582],{},"Local Conferences"," - If you’re regularly speaking at local meetup groups a local conference might be the next step in your progression. In this case you might see a bunch of people you’re familiar with including coworkers which can make this situation a little less stressful.",[37,37585,37586,37589],{},[646,37587,37588],{},"Regional Conferences"," - You're on your way to becoming a pro and it's time to expand your audience and venture onto bigger and brighter stages. This doesn't imply you need to be the keynote speaker at a 20,000-developer conference. Many of the conferences I speak at are wonderful, even with less than 50 people in the room.",[37,37591,37592,37595],{},[646,37593,37594],{},"International Conferences"," - Now that you have presented at some regional conferences you can begin to branch out across the world.",[29685,37597,37598],{},[22,37599,37600,37601],{},"“I'm a big fan of developer advocates doing the same thing that stand-up comedians do: you work at a small bar, you do some open mics, and you move your way up to the big rooms. That's often how it's done.” - ",[646,37602,37485],{},[22,37604,37605,37606,37611],{},"There are also some really great groups out there where you can learn and get better at public speaking. ",[677,37607,37610],{"href":37608,"rel":37609},"https://www.toastmasters.org/",[681],"Toastmaster International"," is an organization that has clubs all over the world where you can meat and improve your craft.",[22,37613,37614],{},[646,37615,37616],{},"Build Something",[22,37618,37619],{},"As a developer advocate you should strive to lose your foundation, writing code. I understand that you’re not going to be writing code 40 hours a week but you to constantly be working on your craft and building things. This could be writing demos to show off a new feature, answering questions or as a part to your latest tutorial.",[22,37621,37622],{},[646,37623,37624],{},"Technical Writing",[22,37626,37627],{},[653,37628],{"alt":37624,"src":37629},"/images/blog/2024/01/15/technical-writing.jpeg",[22,37631,37632],{},"Writing is an important tool in the Developer Advocates Toolbox. Here are some different ways that you can work on your technical writing skills:",[915,37634,37635,37641,37647,37663,37669],{},[37,37636,37637,37640],{},[646,37638,37639],{},"Documentation"," - Documentation is an important part of every project but often forgotten about. If you go through a getting started experience or you’re reading the documentation for the latest new feature and you see a gap in the documentation you can contribute to it.",[37,37642,37643,37646],{},[646,37644,37645],{},"Social Media -"," Social media is a great place to share your experiences, tips, tricks and more. I love sharing ideas on social media because they get you involved in the community and simple thought or idea could lead to your next demo, article, video or event presentation.",[37,37648,37649,37652,37653,19931,37658,2755],{},[646,37650,37651],{},"Blog Posts"," - I have blogging for most of my career and it’s been a great way to keep writing. I started my own blog by utilizing open source software and it has grown throughout the years. If you don’t want to start your own blog you can begin writing today by taking advantage of platforms like ",[677,37654,37657],{"href":37655,"rel":37656},"https://dev.to/",[681],"DEV",[677,37659,37662],{"href":37660,"rel":37661},"https://hashnode.com/",[681],"HashNode",[37,37664,37665,37668],{},[646,37666,37667],{},"Articles"," - Many publications welcome the idea of guest posts. I love this because they often have large audiences and it’s a way to get your name out into the community.",[37,37670,37671,37674],{},[646,37672,37673],{},"Newsletter"," - I love newsletters because you can pick a cadence and try to stick to it. This will keep you writing by giving yourself some soft of schedule.",[22,37676,37677],{},[646,37678,37679],{},"Videos",[22,37681,37682],{},"I enjoy creating videos as it's my preferred learning method. My works range from short clips to extensive 15-hour courses. Video creation involves many elements, but that's a discussion for another time. If I were to give one piece of advice, it would be to invest in a quality microphone. For developer content, good audio quality is more critical than having a high-end camera.",[22,37684,37685],{},"I've heard it said that your first 100 videos may not be the best, and I think that's fair. What you should aim to do is identify one area for improvement in each video and focus on that in your next one. Repeat this process with different aspects of your videos, and you'll become proficient in no time.",[22,37687,37688],{},[646,37689,37576],{},[22,37691,37692],{},"Entering podcasting can be relatively straightforward if you have a good microphone. You can start by being a guest on another podcast. As a host myself, I can affirm that we're always in search of guests. If you're interested in appearing on a podcast, I'd encourage you to contact the hosts and propose a show concept. The worst they can say is no.",[636,37694,37696],{"id":37695},"tips-and-tricks","Tips and Tricks",[22,37698,37699],{},[653,37700],{"alt":37701,"src":37702},"Developer Advocate Tips and Tricks","/images/blog/2024/01/15/tips-tricks.png",[22,37704,37705],{},"These are few tips and tricks that I have picked up over the years.",[915,37707,37708,37714,37720,37726,37732,37738,37744],{},[37,37709,37710,37713],{},[646,37711,37712],{},"Repurpose Content"," - Learn to repurpose content for different mediums. A simple tweet might evolve into a demo, which could then become an article, a video, and eventually a conference talk. If you need a new example and idea for every piece of content you create, it may become challenging.",[37,37715,37716,37719],{},[646,37717,37718],{},"Answer Honestly"," - One of my greatest fears was standing on a conference stage and being asked a question I couldn't answer. However, it's acceptable to admit when you don't know something. We can't know everything, and no one will fault you for it. A good response could be, \"I'm not certain, but I will consult with the engineering team and share the answer on Twitter or in a tutorial.”",[37,37721,37722,37725],{},[646,37723,37724],{},"Be Curious"," - Part of being a Developer and a Developer Advocate is to always be curious and always be eager to learn new things. Not sure of how something works, put together a demo and try to use products in ways you haven’t thought of before.",[37,37727,37728,37731],{},[646,37729,37730],{},"Be Authentic"," - Developers are really good at picking up on BS. Be authentic and transparent about what you’re doing and I believe developers will appreciate that in the long run.",[37,37733,37734,37737],{},[646,37735,37736],{},"Avoid Burnout"," - In this profession, it's easy to experience burnout from constantly pursuing the next article, video, book, or conference talk. This highlights the importance of repurposing content and recognizing that producing a new article or video every day is not feasible. I used to feel guilty about not meeting self-imposed goals, but I now understand that my expectations were unrealistic.",[37,37739,37740,37743],{},[646,37741,37742],{},"Travel Hacks"," - Traveling for work and traveling for fun are 2 completely different animals. I am lucky to have had a coworker who was eager to show me the ropes. Learn some travel tips like avoiding checked bags to minimize time in the airport to taking advantage of programs like TSA, Clear and Global Entry.",[37,37745,37746,37749],{},[646,37747,37748],{},"Enjoy the Ride"," - This is my dream job. How many people can show up every day and really truly say they love what they do. I do and I don’t take it for granted.",[26,37751,4098],{"id":4097},[22,37753,37754],{},[646,37755,37756],{},"Books",[915,37758,37759,37766],{},[37,37760,37761],{},[677,37762,37765],{"href":37763,"rel":37764},"https://amzn.to/49cPjRV",[681],"Developer, Advocate!: Conversations on turning a passion for talking about tech into a career",[37,37767,37768],{},[677,37769,37772],{"href":37770,"rel":37771},"https://amzn.to/3TWZ2Y5",[681],"The Business Value of Developer Relations: How and Why Technical Communities Are Key To Your Success",[22,37774,37775],{},[646,37776,37667],{},[915,37778,37779,37787],{},[37,37780,37781],{},[646,37782,37783],{},[677,37784,37786],{"href":37262,"rel":37785},[681],"Developer Relations: (More Than) The Art of Talking Good",[37,37788,37789],{},[677,37790,37793],{"href":37791,"rel":37792},"https://jamesward.com/2021/09/26/the-seven-artifacts-of-developer-advocacy-projects/",[681],"The Seven Artifacts of Developer Advocacy Projects",[22,37795,37796],{},[646,37797,37576],{},[915,37799,37800,37806],{},[37,37801,37802],{},[677,37803,37805],{"href":37295,"rel":37804},[681],"What even is DevRel (Lee Robinson)",[37,37807,37808],{},[677,37809,37811],{"href":37279,"rel":37810},[681],"Coming home to GitHub (Christina Warren)",[22,37813,37814],{},[646,37815,37816],{},"Websites",[915,37818,37819,37826],{},[37,37820,37821],{},[677,37822,37825],{"href":37823,"rel":37824},"https://developerrelations.com/",[681],"https://developerrelations.com",[37,37827,37828],{},[677,37829,37832],{"href":37830,"rel":37831},"https://www.learndevrel.com/",[681],"https://www.learndevrel.com",[26,37834,1499],{"id":1498},[22,37836,37837],{},"It’s important to remember that everything in this article is based on my experience and it might be different for you. Developer Relations (DevRel) and Developer Advocacy involve advocating for the tech community within a company, focusing on education, community, and product. DevRel fits differently within organizations depending on their goals, encompassing engagement, community building, advocacy, organizational representation, technical expertise, and resource creation. Measuring impact is a challenge and requires collaboration within the team.",[22,37839,37840],{},"A Developer Advocate is not just about sales, marketing, or writing code, but about long-term community engagement, genuine relationships, and understanding developers' needs. The career path into DevRel involves being a developer first, sharing passion, thinking of oneself as a brand, and advocating on one's own. Content creation can involve public speaking, building something, technical writing, videos, and podcasts. Tips for success include repurposing content, answering honestly, being curious and authentic, avoiding burnout, and enjoying the ride.",{"title":57,"searchDepth":76,"depth":76,"links":37842},[37843,37849,37854,37855],{"id":37214,"depth":76,"text":37215,"children":37844},[37845,37846,37847,37848],{"id":37230,"depth":82,"text":37231},{"id":37310,"depth":82,"text":37311},{"id":37405,"depth":82,"text":37406},{"id":37417,"depth":82,"text":37418},{"id":37450,"depth":76,"text":37451,"children":37850},[37851,37852,37853],{"id":37457,"depth":82,"text":37458},{"id":37521,"depth":82,"text":31404},{"id":37695,"depth":82,"text":37696},{"id":4097,"depth":76,"text":4098},{"id":1498,"depth":76,"text":1499},{"_id":37857,"path":37858,"title":37859,"description":37860,"meta":37861,"body":37866},"content/blog/2024/01/01/happy-new-year-2024.md","/blog/2024/01/01/happy-new-year-2024","Happy New Year 2024","In this article I will take a look at some goals and things I want to focus on in the new year.",{"slug":37862,"date":37863,"published":13,"tags":37864,"author":17,"cover":37865,"excerpt":-1},"happy-new-year-2024","2024-01-01T17:00:00.000Z",[36998],"photo-1701170633885-7209b1bf10ee.jpeg",{"type":19,"value":37867,"toc":38049},[37868,37877,37879,37882,37925,37928,37932,37937,37940,37943,37946,37950,37955,37958,37961,37964,37968,37974,37977,37980,37991,37994,38007,38011,38016,38019,38022,38025,38028,38032,38038,38041,38044,38046],[22,37869,37870,37871,37876],{},"It’s hard to believe that another year has passed and we have made it to 2024. If you missed ",[677,37872,37875],{"href":37873,"rel":37874},"https://www.danvega.dev/blog/2023-year-in-review",[681],"my last post"," I did a year in review on what my 2023 looked like. In this post I want to focus on in the new year. I use this posts to reassess during the year to make sure I stay on track.",[26,37878,37107],{"id":37106},[22,37880,37881],{},"I’m entering my 3rd year as a Spring Developer Advocate now with Broadcom. As I mentioned in my previous post I’m excited to see what the year ahead looks like. With that I am going to be heads down doing what I love to do. That is connecting with you, talking about Java & Spring. I still can’t believe this is what I get to do every single day. I don’t take it for granted and I appreciate all of your support. These are the different areas that I want to focus on when it comes to learning and presenting on this year:",[915,37883,37884,37892,37906],{},[37,37885,37886,37887],{},"Java\n",[915,37888,37889],{},[37,37890,37891],{},"Java 21 & 22 New Features",[37,37893,37894,37895],{},"Kotlin\n",[915,37896,37897,37900,37903],{},[37,37898,37899],{},"Language Basics",[37,37901,37902],{},"Kotlin vs Java",[37,37904,37905],{},"Building Spring Applications with Kotlin",[37,37907,37908,37909],{},"Spring\n",[915,37910,37911,37914,37917,37919,37922],{},[37,37912,37913],{},"Spring Academy",[37,37915,37916],{},"Spring AI (AI in general)",[37,37918,8507],{},[37,37920,37921],{},"Spring Boot Migrator / OpenRewrite",[37,37923,37924],{},"Spring Security - Authorization Server",[22,37926,37927],{},"I would love to hear from you on what you’re interested in learning in the new year.",[26,37929,37931],{"id":37930},"writing","Writing",[22,37933,37934],{},[653,37935],{"alt":37931,"src":37936},"/images/blog/2024/01/01/photo-1501504905252-473c47e087f8.jpeg",[22,37938,37939],{},"I enjoy writing, and in 2023, I remained consistent with my newsletter and personal blog. However, I now want to expand my reach and audience. I have always had the goal of writing my own book, and I plan to start researching this area in the hopes of beginning the process later this year or early next year.",[22,37941,37942],{},"In the meantime, I would like to write for publications other than my personal blog. I already have a few opportunities lined up that I am really excited about, but if you know of any additional opportunities, please feel free to share them with me.",[22,37944,37945],{},"As for my goals, I aim to write 34 blog posts, with 5-10 of them being for publications other than my blog. I believe this is a reasonable number that will allow me to focus on my top priority, YouTube.",[26,37947,37949],{"id":37948},"youtube","YouTube",[22,37951,37952],{},[653,37953],{"alt":37949,"src":37954},"/images/blog/2024/01/01/photo-1521302200778-33500795e128.jpeg",[22,37956,37957],{},"I mentioned in my 2023 year in review that I am fully committed to YouTube this year. In the first 9 years of my YouTube channel, I gained 20,000 subscribers, and in 2023 alone, I gained 25,000 subscribers. If I can double that in 2024, I will reach a significant milestone of 100,000 subscribers and earn the coveted Silver Play Button.",[22,37959,37960],{},"This is my ultimate goal for the year, so much of what I do will revolve around it. I want to continue improving my video-making process and create content that helps people learn new things alongside me. This includes everything from the equipment I use and my studio setup to how I plan, edit, and publish my videos. There are numerous areas I can enhance, and I will focus on those in 2024.",[22,37962,37963],{},"Additionally, I have received many inquiries for sponsorships, and I hope to partner with some exceptional companies this year. If you are interested in sponsoring a video, please feel free to reach out to me.",[26,37965,37967],{"id":37966},"conferences-events","Conferences / Events",[22,37969,37970],{},[653,37971],{"alt":37972,"src":37973},"Conferences & Events","/images/blog/2024/01/01/photo-1587825140708-dfaf72ae4b04.jpeg",[22,37975,37976],{},"The only thing I love more than attending conferences is speaking at them. I enjoy being around developers and hearing about the things they are working on and the problems they are trying to solve.",[22,37978,37979],{},"I feel very fortunate to have traveled to so many awesome places last year. I hope to continue doing that this year, and I already have the following conferences scheduled:",[915,37981,37982,37985,37988],{},[37,37983,37984],{},"CodeMash - Sandusky, OH",[37,37986,37987],{},"ConFoo - Montreal, CA",[37,37989,37990],{},"Great International Developer Summit (GIDS) - Bengaluru, India",[22,37992,37993],{},"I also hope to visit new places this year and speak at conferences I haven't been to before. I have submitted proposals to several new conferences for the first half of the year, so I will keep my fingers crossed and hopefully see you there.",[22,37995,37996,37997,38002,38003,2755],{},"In addition, I am making an effort to attend more Java User Groups this year. I have some plans in progress, and I will share them with you as soon as they are confirmed. If you want to stay updated on where I will be, you can check out my ",[677,37998,38001],{"href":37999,"rel":38000},"https://www.danvega.dev/speaking",[681],"speaking page"," or subscribe to my free ",[677,38004,33895],{"href":38005,"rel":38006},"https://www.danvega.dev/newsletter/",[681],[26,38008,38010],{"id":38009},"spring-office-hours","Spring Office Hours",[22,38012,38013],{},[653,38014],{"alt":38010,"src":38015},"/images/blog/2024/01/01/spring-office-hours.png",[22,38017,38018],{},"I'm entering Season 3 of Spring Office Hours with more excitement than ever before. It's a lot of work, but it's incredibly gratifying to see all that hard work pay off.",[22,38020,38021],{},"While we have been aware of the numbers in terms of downloads, live stream views, and YouTube replays, they haven't been our main focus. However, that will change in Season 3. I plan to pay closer attention to the numbers early in the new year and determine our goals for the show. Once we establish those goals, we can work on implementing new strategies to support them.",[22,38023,38024],{},"In the new year, one of our priorities is to create a dedicated website for Spring Office Hours. This will give us more control over certain aspects and allow us to incorporate features that have been on our list.",[22,38026,38027],{},"Another goal for this year is to improve our ability to record podcasts while on the road. We conducted a few road shows last year, and although they were enjoyable, there is definitely room for improvement in this area. The first step is to understand the necessary equipment and determine how to obtain it. If you have experience in this area, we would greatly appreciate your input.",[26,38029,38031],{"id":38030},"my-website-danvegadev","My Website (danvega.dev)",[22,38033,38034],{},[653,38035],{"alt":38036,"src":38037},"My Website","/images/blog/2024/01/01/danvega_dev_home.png",[22,38039,38040],{},"I’m really happy that I was able to get my website moved over to Nuxt 3 with a new theme towards the end of 2023. What I really want to focus on next is working on the SEO for my blog posts. I have already started working on this for previous posts and will continue to do this throughout the year.",[22,38042,38043],{},"When I sit down to write a new post I want to figure out what people are searching for on a particular topic and cater my articles to those searches. My thought process here is if I am going to sit down and spend the time to write a blog post It would be nice to get some traffic to my website.",[26,38045,1499],{"id":1498},[22,38047,38048],{},"As I enter 2024, I'm grateful for the experiences and opportunities that came my way as a Spring Developer Advocate. This year, I'll explore new features in Java 21 and 22, dive into Kotlin, and deepen my understanding of the Spring ecosystem. I aim to expand my reach by writing for publications, grow my YouTube channel to 100,000 subscribers, and collaborate with exceptional companies through sponsorships. Attending conferences, improving Spring Office Hours, optimizing SEO for my blog posts, and sharing my experiences with the community are also on my agenda for this exciting year.",{"title":57,"searchDepth":76,"depth":76,"links":38050},[38051,38052,38053,38054,38055,38056,38057],{"id":37106,"depth":76,"text":37107},{"id":37930,"depth":76,"text":37931},{"id":37948,"depth":76,"text":37949},{"id":37966,"depth":76,"text":37967},{"id":38009,"depth":76,"text":38010},{"id":38030,"depth":76,"text":38031},{"id":1498,"depth":76,"text":1499},{"_id":38059,"path":38060,"title":38061,"description":38062,"meta":38063,"body":38068},"content/blog/2023/12/30/2023-year-in-review.md","/blog/2023/12/30/2023-year-in-review","2023 Year in Review","In this article I take a look back at 2023 and share some of the highlights from the year.",{"slug":38064,"date":38065,"published":13,"tags":38066,"author":17,"cover":38067,"excerpt":-1},"2023-year-in-review","2023-12-30T17:00:00.000Z",[36998],"behnam-norouzi-f_Bo19fq4Oc-unsplash.jpg",{"type":19,"value":38069,"toc":38363},[38070,38073,38076,38101,38105,38108,38111,38115,38121,38124,38127,38132,38154,38159,38164,38184,38189,38191,38196,38199,38202,38211,38215,38218,38220,38223,38226,38228,38231,38240,38243,38250,38259,38261,38270,38273,38276,38281,38292,38297,38323,38327,38333,38346,38348,38357,38360],[22,38071,38072],{},"2023 was an incredible year, and I'm excited to share all about it in this article. I would also love to hear about your highlights from 2023. What were some memorable moments for you?",[22,38074,38075],{},"TOC",[915,38077,38078,38087,38096,38098],{},[37,38079,38080,38081],{},"VMware / Broadcom\n",[915,38082,38083,38085],{},[37,38084,37972],{},[37,38086,38010],{},[37,38088,38089,38090],{},"Java & Spring\n",[915,38091,38092,38094],{},[37,38093,15],{},[37,38095,16],{},[37,38097,37949],{},[37,38099,38100],{},"Personal Website",[26,38102,38104],{"id":38103},"vmware","VMware",[22,38106,38107],{},"I am about to begin my third year at VMware, which has now become Broadcom as a result of an acquisition that was announced shortly after I joined the company. This acquisition process has been ongoing for the past 18 months. Despite the news, I made a conscious decision not to let it affect me. I had found my dream job, and I was determined to stick with it. Recently, the acquisition was finalized, and we are now officially a part of Broadcom.",[22,38109,38110],{},"The journey hasn't been easy. We lost many talented individuals, and not being able to work with them has been the most challenging aspect. However, I am grateful to still hold my position as a Spring Developer Advocate, and I am excited about the opportunities that lie ahead at Broadcom.",[636,38112,38114],{"id":38113},"conferences-and-events","Conferences and Events",[22,38116,38117],{},[653,38118],{"alt":38119,"src":38120},"Conference Badges","/images/blog/2023/12/30/conference_badges.jpeg",[22,38122,38123],{},"It was an amazing year of traveling to conferences and events. I think the obvious highlight for me was SpringOne in Las Vegas. This was the return of in-person SpringOne conference and it was amazing. The keynote was electric and I had 3 talks, a live Spring Office Hours and booth duty. It was a long exhausting week but was so great to be in the same spot as so many coworkers. I met so many people for the first time that I have looked up to.",[22,38125,38126],{},"I also made the trip across the pond to Barcelona Spain for VMware Explore. What an amazing trip that was to be in such an amazing place. I’m hoping I can make it back there soon. Here are some of the conferences and events I traveled to this year. I feel very grateful that I get to travel to these events and talk to developers about all the things I am passionate about.",[22,38128,38129],{},[646,38130,38131],{},"Conferences",[915,38133,38134,38137,38140,38143,38146,38149,38152],{},[37,38135,38136],{},"VMware Explore - Barcelona, Spain",[37,38138,38139],{},"Connect Tech - Atlanta, GA",[37,38141,38142],{},"SpringOne at VMware Explore - Las Vegas, NV",[37,38144,38145],{},"KCDC - Kansas City, MO",[37,38147,38148],{},"DevNexus - Atlanta, GA",[37,38150,38151],{},"SpringOne Essentials - San Francisco, CA",[37,38153,37984],{},[22,38155,38156],{},[653,38157],{"alt":38158,"src":38158},"https://www.danvega.dev/images/newsletter/2023/08/28/springone-keynote.jpeg",[22,38160,38161],{},[646,38162,38163],{},"Events",[915,38165,38166,38169,38172,38175,38178,38181],{},[37,38167,38168],{},"Cincinnati",[37,38170,38171],{},"Seattle",[37,38173,38174],{},"Austin",[37,38176,38177],{},"Detroit",[37,38179,38180],{},"Pittsburgh",[37,38182,38183],{},"Dallas",[22,38185,38186],{},[653,38187],{"alt":38188,"src":38188},"https://www.danvega.dev/images/newsletter/2023/08/28/graphql_talk.png",[636,38190,38010],{"id":38009},[22,38192,38193],{},[653,38194],{"alt":38010,"src":38195},"/images/blog/2023/12/30/streamyard-thumbnail_talking_heads.png",[22,38197,38198],{},"We just wrapped up Season 2 of Spring Office Hours and it was a really big year for us. If you don’t know my friend and coworker DaShaun and I started a live stream last year. Our goal for this show was to keep the community informed on what’s happening in the Spring Ecosystem and answer any questions you might have.",[22,38200,38201],{},"This year we got better at preparing for shows, scheduling guests and we even turned our show into a podcast. We kept going back and forth on when the right time might be to turn it into a podcast and finally I said I need to take the advice that I always give to others. It’s never going to be a perfect time to start something new, just do it and iterate on it to improve it. That’s what we did and I am really happy we did that.",[22,38203,38204,38205,38210],{},"We have some plans for the new year which include a new website, improving the podcast and improving our shows on the road. I will share more of that with you when I have something to share but until then we have our ",[677,38206,38209],{"href":38207,"rel":38208},"https://www.springofficehours.io",[681],"Season 3 Premier"," on Tuesday January 2nd at 3:30 EDT.",[26,38212,38214],{"id":38213},"java-spring","Java & Spring",[22,38216,38217],{},"It was an amazing year for both Java and Spring. DaShaun and I say it almost weekly on Spring Office Hours but it truly is an amazing time to be a Java and Spring Developer.",[636,38219,15],{"id":56},[22,38221,38222],{},"It was another big year for Java with 2 new releases. Java 20 was released in March and Java 21 was released in September. The highlight for me was Virtual Threads going final in Java 21 and for me this is one of the biggest releases in Java, ever. I don’t say that lately because I know there have been some pretty important releases in the history of this language. As we begin to deploy different types of workloads into production some of them have different requirements. With the release of Virtual Threads, Project CRaC and GraalVM Java has an answer for any type of application. If you want to learn more about Java 21 I shared my thoughts on Episode 52 of Spring Office Hours.",[36089,38224],{"id":38225},"TAliLDYe20M",[636,38227,16],{"id":11002},[22,38229,38230],{},"Spring Boot 3 was released at the end of 2022 so that was all the buzz heading into the new year. There were four major themes which were Java 17 Baseline, Jakarta EE, AOT, and Observability. I spent the majority talking about Spring Boot 3 and Beyond 🚀",[22,38232,38233,38234,38239],{},"Spring Boot 3.1 was released in May and for a point release it had a few really great features. I wrote a ",[677,38235,38238],{"href":38236,"rel":38237},"https://www.danvega.dev/blog/spring-boot-docker-compose",[681],"blog post"," about the one feature that really got my attention, the Docker Compose module. This feature will create a Docker Compose file for you based on the dependencies you select, start the container and inject the properties into a property source at runtime.",[36089,38241],{"id":38242},"lS1GwdIfk0c",[22,38244,38245,38246,2755],{},"Spring Boot 3.2 was released in November and it was packed full of features. In terms of Runtime Efficiency we shipped official support for Virtual Threads through a single property. This is such an important feature for anyone building imperative blocking applications and I wrote up a nice long blog post on it ",[677,38247,8441],{"href":38248,"rel":38249},"https://www.danvega.dev/blog/virtual-threads-spring-boot",[681],[22,38251,38252,38253,38258],{},"I’m also a big fan of the new client abstractions in Spring Boot 3.2 in the REST Client and JDBC Client. These go in that developer productivity bucket for me and I really can’t wait for developers to get their hands on both of these. I wrote a blog post on ",[677,38254,38257],{"href":38255,"rel":38256},"https://www.danvega.dev/blog/spring-boot-3-2",[681],"What’s new in Spring Boot 3.2"," if you want to learn more.",[26,38260,37949],{"id":37948},[22,38262,38263,38264,38269],{},"I wrote about this in my ",[677,38265,38268],{"href":38266,"rel":38267},"https://www.danvega.dev/blog/2022-reflections",[681],"2022 Year in Review"," but I had just crossed the 20,000 Subscriber mark towards the end of the year. I uploaded my first video in October of 2013 and it took me 9 years to get to 20,000. Now I didn’t actually start taking YouTube serious until 5 years ago but none the less it took me a long time to get there.",[22,38271,38272],{},"As I’m writing this I’m closing in on 45,000 Subscribers. So in 1 year I did what it took me 9 years to do. Just goes to show you that YouTube is a long game and once you get that ball rolling down the hill it starts going pretty fast. If I can somehow double again next year I will be closing in on that ever so illusive 100k mark which has been the goal since I started.",[22,38274,38275],{},"This past year I created 52 long form videos and 37 shorts. The 52 is a big number for me because my goal was to create at least 1 per week. I didn’t do that this year but I did end up averaging that which is impressive to me. Here are some stats and my top videos from the year.",[22,38277,38278],{},[646,38279,38280],{},"Stats",[915,38282,38283,38286,38289],{},[37,38284,38285],{},"Views - 1,620,206 - 142% over last year",[37,38287,38288],{},"Watch Time - 160,000 - 252% over last year",[37,38290,38291],{},"Subscribers - 21,473 - 135% over last year",[22,38293,38294],{},[646,38295,38296],{},"Top Videos (Views)",[915,38298,38299,38307,38315],{},[37,38300,38301,38306],{},[677,38302,38305],{"href":38303,"rel":38304},"https://youtu.be/UgX5lgv4uVM",[681],"Spring Boot Tutorial for Beginners"," - 230,000",[37,38308,38309,38314],{},[677,38310,38313],{"href":38311,"rel":38312},"https://youtu.be/us0VjFiHogo",[681],"OAuth2 Login Made Easy in Java"," - 65,000",[37,38316,38317,38322],{},[677,38318,38321],{"href":38319,"rel":38320},"https://youtu.be/KYNR5js2cXE",[681],"Spring Security JWT"," - 60,000",[26,38324,38326],{"id":38325},"personal-website-blog-newsletter","Personal Website (Blog / Newsletter )",[22,38328,38329],{},[653,38330],{"alt":38331,"src":38332},"Dan Vega Homepage","/images/blog/2023/12/30/danvega_dev_home.png",[22,38334,38335,38336,38341,38342,2755],{},"I felt a great sense of pride when I finally accomplished something I had been talking about for a while: migrating my website to Nuxt 3. Since I was already familiar with Vue and Nuxt, that wasn't the obstacle. Initially, I wanted a fresh look and feel for my website, but I didn't want to start designing it from scratch. That's when I discovered the Spotlight template from ",[677,38337,38340],{"href":38338,"rel":38339},"https://tailwindui.com/templates",[681],"Tailwind UI",", and I knew it was perfect. The only issue was that the template was written in React and designed for a different framework, so I had to adapt it. Fortunately, the process wasn't too challenging. This was the spark I needed, and once ignited, there was no stopping me. I transferred all of my previous content to the new blog, which inspired me to clean up various aspects, including old posts. Overall, I am pleased with how it turned out, and I am excited to create more content for my website in 2024. If you're interested in obtaining the source code for my website, you can find it ",[677,38343,8441],{"href":38344,"rel":38345},"https://github.com/danvega/danvega-dev-nuxt",[681],[636,38347,37931],{"id":37930},[22,38349,38350,38351,38356],{},"I wrote about where to focus my writing in my ",[677,38352,38355],{"href":38353,"rel":38354},"https://www.danvega.dev/blog/happy-new-year-2023",[681],"Happy New Year 2023"," blog post and I’m right back where I started at the beginning of the year. I wrote 42 newsletters this year which isn’t every single week but it’s almost weekly. I wrote 28 blog posts this year and some of them I am really proud of.",[22,38358,38359],{},"My problem with the newsletter is that it eats up a bunch of my time and I have nothing to tell me if its actually helping anyone. I tell people about what I was up to last week and most of it is content around Java / Spring which I could probably just mention on Spring Office Hours. Yes it might be pushing people to my blog posts and YouTube videos but my newsletter subscriber base isn’t growing. Instead of losing people like I did last year I think I gained 300ish to bring me to around 4300.",[22,38361,38362],{},"I’m not saying that everything has to fall down to vanity metrics but If I’m doing something that isn’t moving the needle I could easily be spending time working on something that will. I think I am going to",{"title":57,"searchDepth":76,"depth":76,"links":38364},[38365,38369,38373,38374],{"id":38103,"depth":76,"text":38104,"children":38366},[38367,38368],{"id":38113,"depth":82,"text":38114},{"id":38009,"depth":82,"text":38010},{"id":38213,"depth":76,"text":38214,"children":38370},[38371,38372],{"id":56,"depth":82,"text":15},{"id":11002,"depth":82,"text":16},{"id":37948,"depth":76,"text":37949},{"id":38325,"depth":76,"text":38326,"children":38375},[38376],{"id":37930,"depth":82,"text":37931},{"_id":38378,"path":38379,"title":38380,"description":38381,"meta":38382,"body":38387},"content/blog/2023/12/20/spring-boot-3-2.md","/blog/2023/12/20/spring-boot-3-2","What's New in Spring Boot 3.2","This article highlights some of the new features in the latest releases of Spring Framework 6.1 and Spring Boot 3.2.",{"slug":38383,"date":38384,"published":13,"tags":38385,"author":17,"cover":38386,"excerpt":-1},"spring-boot-3-2","2023-12-20T17:00:00.000Z",[2925],"whats-new.jpeg",{"type":19,"value":38388,"toc":40102},[38389,38397,38427,38431,38434,38437,38440,38443,38446,38449,38453,38456,38463,38469,38472,38475,38481,38485,38488,38494,38497,38505,38509,38517,38520,38524,38527,38531,38534,38537,38543,38546,38552,38555,38559,38562,38565,38569,38578,38581,38587,38591,38600,38603,38606,38609,38612,38619,39034,39043,39046,39050,39053,39056,39059,39062,39066,39069,39072,39075,39084,39087,39090,39093,39156,39159,39165,39172,39398,39406,39409,39413,39416,39419,39426,39629,39632,39641,39645,39648,39651,39654,39659,39663,39666,39683,39690,39928,39932,39961,40007,40013,40092,40094,40097,40099],[22,38390,38391,38392,38396],{},"This article highlights some of the new features in the latest releases of Spring Framework 6.1 and Spring Boot 3.2. The new release includes support for Java 21, so if you want to leverage all the new features in the JDK, you should consider upgrading. For a comprehensive overview of the changes, new features, and deprecations, I recommend referring to the ",[677,38393,34833],{"href":38394,"rel":38395},"https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-3.2-Release-Notes",[681],". In this release, I will focus on three main areas:",[915,38398,38399,38413,38424],{},[37,38400,38401,38402],{},"Runtime Efficiency\n",[915,38403,38404,38407,38410],{},[37,38405,38406],{},"Virtual Threads",[37,38408,38409],{},"Project CRaC (Coordinated Restore at Checkpoint)",[37,38411,38412],{},"Project Leydon",[37,38414,38415,38416],{},"New Client Abstractions\n",[915,38417,38418,38421],{},[37,38419,38420],{},"JDBC Client",[37,38422,38423],{},"REST Client",[37,38425,38426],{},"Observability Improvements",[26,38428,38430],{"id":38429},"runtime-efficiency","Runtime Efficiency",[22,38432,38433],{},"I have had the opportunity to travel to conferences and meet with customers, discussing the impressive features of Spring Boot 3 and beyond. One of the prominent themes in Spring Boot 3 is Runtime Efficiency. But what does that really mean, and which features are contributing to it?",[22,38435,38436],{},"Runtime efficiency is focused on addressing the challenges that arise when dealing with various workloads in production. Traditionally, a typical Java-based application would be a long-running process designed to handle high throughput. In this scenario, the JVM excels at optimizing for such workloads. It identifies \"hot spots\" or frequently executed areas of the code and compiles them for further optimizations.",[22,38438,38439],{},"Today, we are developing a wide range of applications with diverse requirements. Some of these applications demand lower memory usage and faster startup times. For others, which involve blocking requests like communication with a database or an HTTP service, scalability is essential.",[22,38441,38442],{},"Java and Spring now provide solutions for building various types of applications. In Spring Boot 3.0, we introduced Ahead-of-Time (AoT) compilation as a prerequisite for creating native images with GraalVM. In Spring Boot 3.2, we are introducing support for Virtual Threads and Project CRaC (Coordinated Restore at Checkpoint).",[636,38444,38406],{"id":38445},"virtual-threads",[22,38447,38448],{},"Java 21 might be one of the most significant releases we have seen in Java, ever. I don’t say that lightly because I know there have been some amazing releases over the years. This release however was the start of the Project Loom story with Virtual Threads being finalized and bringing us a solution to the scalability issues the thread-per-request model introduces.",[636,38450,38452],{"id":38451},"threads-in-java","Threads in Java",[22,38454,38455],{},"In Java, each statement inside a method executes in a thread. Java is Multi-threaded which means more than one thread can be executing a given set of instructions at once. Threads are fundamental to concurrent programming and allow a Java program to perform multiple tasks or operations concurrently, making efficient use of available resources, such as CPU cores.",[22,38457,38458,38459,38462],{},"In Java, a thread (",[59,38460,38461],{},"java.lang.Thread",") serves as a thin wrapper around the operating system thread, commonly known as Platform Threads. The operating system thread is a limited resource that relies on system resources. One major drawback of Platform Threads, in addition to their ubiquity, is their high resource consumption in terms of memory and processing time.",[22,38464,38465],{},[653,38466],{"alt":38467,"src":38468},"java_threads.png","/images/blog/2023/12/20/java_threads.png",[22,38470,38471],{},"Now imagine you need to perform a blocking operation such as talking to a database or communicating with a service over HTTP. These operations are consider blocking because the thread responsible for handling this operation needs to wait for the operation to complete.",[22,38473,38474],{},"Before the introduction of virtual threads, there were two options available for addressing scalability issues in concurrent programming in Java. One option was to add more hardware by scaling horizontally or vertically. Another option was to use asynchronous programming, which, like any architectural choice, has its own advantages and disadvantages.",[22,38476,38477],{},[653,38478],{"alt":38479,"src":38480},"scaling_options.png","/images/blog/2023/12/20//scaling_options.png",[636,38482,38484],{"id":38483},"why-virtual-threads-in-java","Why Virtual Threads in Java?",[22,38486,38487],{},"Virtual Threads were introduced in JDK 21 as a solution to the challenges of asynchronous programming. These lightweight threads do not block platform threads, making them highly efficient. In fact, you can spawn millions of Virtual Threads without needing to worry about thread pooling. This allows us to write imperative blocking code without significant concerns about scalability.",[22,38489,38490],{},[653,38491],{"alt":38492,"src":38493},"virtual_threads.png","/images/blog/2023/12/20//virtual_threads.png",[22,38495,38496],{},"Of course there are still Platform Threads which are a finite resource so how does this work under the hood? When a virtual thread is attached to a platform and needs to perform a blocking task it is unmounted from the platform thread and moved to the heap. When the blocking operation has completed it is taken from the heap and moved to a waiting list for the platform thread it was initially mounted on.",[22,38498,38499,38500,2755],{},"This is really just a high level introduction to Virtual Threads in JDK 21. If you want to understand the goals, non-goals, motivation and more about them I would encourage you to take some time to read through the ",[677,38501,38504],{"href":38502,"rel":38503},"https://openjdk.org/jeps/444",[681],"JEP (JDK Enhancement Proposal)",[636,38506,38508],{"id":38507},"virtual-threads-in-spring-boot-example","Virtual Threads in Spring Boot Example",[22,38510,38511,38512,38516],{},"If you're interested in learning more about Virtual Threads in Spring Boot, you can check out ",[677,38513,38515],{"href":38248,"rel":38514},[681],"this article",", which continues the conversation. In this example, you can create a new Spring Boot application, enable virtual threads, and run benchmark tests against it.",[36089,38518],{"id":38519},"THavIYnlwck",[26,38521,38523],{"id":38522},"what-is-project-crac-coordinated-restore-at-checkpoint","What is Project CRaC (Coordinated Restore at Checkpoint)",[22,38525,38526],{},"In the realm of runtime efficiency, Spring Boot 3.2 introduces an exciting JDK project called Coordinated Restore at Checkpoint (CRaC). With CRaC, projects on Linux can experience faster time to first transaction and achieve optimal code speed with reduced time and resource consumption. By capturing a snapshot of the fully warmed up Java process, CRaC enables the launch of multiple JVMs from this state. It's important to note that not all existing Java programs can seamlessly utilize CRaC, as explicit checkpointing and restoration of resources using the CRaC API are required. However, frameworks like Spring offer native support for CRaC checkpointing right out of the box.",[636,38528,38530],{"id":38529},"why-restore-from-a-checkpoint","Why Restore from a Checkpoint?",[22,38532,38533],{},"When a Java application runs, the JVM goes through a process of loading, initializing, and optimizing the code used by the application. However, each time the application is restarted, this process needs to be repeated, resulting in time and resource consumption. What if you could eliminate this initialization process and therefore remove the application's startup time and resource consumption?",[22,38535,38536],{},"One of the primary advantages of CRaC is its ability to create checkpoints within the application's execution flow. This allows the application to go through its initialization phase and, once warmed up and ready to go, a checkpoint can be taken. The checkpoint can then be used the next time the application needs to start up, eliminating the need to warm up the execution environment again.",[22,38538,38539],{},[653,38540],{"alt":38541,"src":38542},"Checkpoint","/images/blog/2023/12/20/checkpoint.png",[22,38544,38545],{},"I would encourage you to try this out in your applications and see what kind of performance gains you get. In many scenarios, you can expect up to two orders of magnitude faster performance. That is something to be really excited about!",[22,38547,38548],{},[653,38549],{"alt":38550,"src":38551},"Time to first Operation","/images/blog/2023/12/20/time_to_first_operation.png",[22,38553,38554],{},"When comparing this to something like AOT, there are several advantages. First, you don't need to add hints to the AOT compiler. However, the major advantage, in my opinion, is that natively compiled applications make a closed world assumption at runtime, whereas applications using Project CRaC can still leverage all the dynamic capabilities of the JVM.",[636,38556,38558],{"id":38557},"spring-boot-project-crac-example","Spring Boot + Project CRaC Example",[22,38560,38561],{},"I mentioned this earlier, but this feature is built on top of a feature in Linux. If you're running Linux locally, you can get this working fairly simply. If you aren't, you need to run the application in a container. The important thing to note is that this is not something you're going to deal with during development. This is a Runtime Efficiency feature and something we can work together with our DevOps teams to implement in our CI/CD process. If you want to see this in action in a Spring Boot 3.2 application, you can check out the video I made with my friend DaShaun below.",[36089,38563],{"id":38564},"sVXUx_Y4hRU",[636,38566,38568],{"id":38567},"project-leydon-and-class-data-sharing","Project Leydon and Class Data Sharing",[22,38570,38571,38572,38577],{},"This is fairly new project in the ",[677,38573,38576],{"href":38574,"rel":38575},"https://openjdk.org/projects/leyden/",[681],"OpenJDK"," and its initiative is to improve the startup and warmup of Java applications. The Spring Team has already done some initial work at looking at Project Leydon in the JDK and where it might be useful to Spring Developers.",[22,38579,38580],{},"Through that exploration they found some interesting discoveries of the JDK’s CDS (”Class Data Sharing”) feature and it has materialized into a new feature that shipping in Spring Framework 6.1 and Spring Boot 3.2. If you’re interested in learning more check out the blog post below.",[22,38582,38583],{},[677,38584,38585],{"href":38585,"rel":38586},"https://spring.io/blog/2023/12/04/cds-with-spring-framework-6-1",[681],[26,38588,38590],{"id":38589},"client-abstractions","Client Abstractions",[22,38592,38593,38594,34867,38597,38599],{},"Spring has simplified complex tasks by abstracting the low-level communication layer into an easy-to-use API. This includes interacting with a database through the JDBC Template or Spring Data, as well as communicating with an external service using the ",[59,38595,38596],{},"RestTemplate",[59,38598,34866],{}," for HTTP requests. In Spring Boot 3.2, two new client abstractions have been introduced to further simplify these processes.",[636,38601,38420],{"id":38602},"jdbc-client",[22,38604,38605],{},"Interacting with databases, reading and persisting data in Java has historically been complex. Factors like building JDBC URLs, managing database connections, and dealing with real-world application concerns such as connection pools need to be considered. Fortunately, Spring simplifies these tasks through its abstractions. One such abstraction is the JDBC template in Spring.",[22,38607,38608],{},"The JDBC template is a solid abstraction that simplifies database interaction. However, it can become verbose when building simple CRUD services based on resources. It requires a deep understanding of methods for communicating with a database, such as row mappers and column to field mapping, which can be tricky.",[22,38610,38611],{},"Despite these complexities, one main advantage is the complete control over SQL, which developers highly appreciate. This is where the newest addition, the JDBC client, comes in. It offers a fluent API that is easy to understand and read. One exciting feature of the JDBC client is its auto-configuration in Spring Boot 3.2. This means that we can simply request a bean in our application and get an instance of it without any hassle!",[22,38613,38614,38615,38618],{},"Here is an example of a Repository class that contains all of the CRUD methods for reading and persisting data to a database. Note that an instance of the ",[59,38616,38617],{},"JdbcClient"," is autowired in using constructor injection, and the code is made more readable thanks to the fluent API.",[52,38620,38622],{"className":54,"code":38621,"language":56,"meta":57,"style":57},"@Repository\npublic class PostRepository {\n\n private final JdbcClient jdbcClient;\n\n public PostRepository(JdbcClient jdbcClient) {\n this.jdbcClient = jdbcClient;\n }\n\n public List\u003CPost> findAll() {\n return jdbcClient.sql(\"SELECT * FROM Post\")\n .query(Post.class)\n .list();\n }\n\n public Optional\u003CPost> findById(String id) {\n return jdbcClient.sql(\"SELECT * FROM Post WHERE id = :id\")\n .param(\"id\", id)\n .query(Post.class)\n .optional();\n }\n\n public void create(Post post) {\n jdbcClient.sql(\"INSERT INTO Post(id,user_id,title,body) values(?,?,?,?)\")\n .params(List.of(post.id(), post.userId(), post.title(), post.body()))\n .update();\n }\n\n public void update(Post post, String id) {\n jdbcClient.sql(\"update Post set user_id = ?, title = ?, body = ? where id = ?\")\n .params(List.of(post.userId(), post.title(), post.body(), id))\n .update();\n }\n\n public void delete(String id) {\n jdbcClient.sql(\"delete from Post where id = :id\")\n .param(\"id\", id)\n .update();\n }\n\n}\n",[59,38623,38624,38630,38641,38645,38654,38658,38672,38684,38688,38692,38707,38724,38733,38742,38746,38750,38770,38785,38800,38808,38817,38821,38825,38841,38855,38885,38893,38897,38901,38920,38933,38958,38966,38970,38974,38989,39002,39014,39022,39026,39030],{"__ignoreMap":57},[62,38625,38626,38628],{"class":64,"line":65},[62,38627,942],{"class":72},[62,38629,23229],{"class":68},[62,38631,38632,38634,38636,38639],{"class":64,"line":76},[62,38633,116],{"class":68},[62,38635,119],{"class":68},[62,38637,38638],{"class":122}," PostRepository",[62,38640,126],{"class":72},[62,38642,38643],{"class":64,"line":82},[62,38644,79],{"emptyLinePlaceholder":13},[62,38646,38647,38649,38651],{"class":64,"line":89},[62,38648,137],{"class":68},[62,38650,458],{"class":68},[62,38652,38653],{"class":72}," JdbcClient jdbcClient;\n",[62,38655,38656],{"class":64,"line":95},[62,38657,79],{"emptyLinePlaceholder":13},[62,38659,38660,38662,38664,38667,38670],{"class":64,"line":101},[62,38661,194],{"class":68},[62,38663,38638],{"class":122},[62,38665,38666],{"class":72},"(JdbcClient ",[62,38668,38669],{"class":889},"jdbcClient",[62,38671,768],{"class":72},[62,38673,38674,38676,38679,38681],{"class":64,"line":107},[62,38675,2405],{"class":149},[62,38677,38678],{"class":72},".jdbcClient ",[62,38680,146],{"class":68},[62,38682,38683],{"class":72}," jdbcClient;\n",[62,38685,38686],{"class":64,"line":113},[62,38687,223],{"class":72},[62,38689,38690],{"class":64,"line":129},[62,38691,79],{"emptyLinePlaceholder":13},[62,38693,38694,38696,38698,38701,38703,38705],{"class":64,"line":134},[62,38695,194],{"class":68},[62,38697,3079],{"class":72},[62,38699,38700],{"class":68},"Post",[62,38702,3135],{"class":72},[62,38704,10287],{"class":122},[62,38706,206],{"class":72},[62,38708,38709,38711,38714,38717,38719,38722],{"class":64,"line":156},[62,38710,360],{"class":68},[62,38712,38713],{"class":72}," jdbcClient.",[62,38715,38716],{"class":122},"sql",[62,38718,2109],{"class":72},[62,38720,38721],{"class":1675},"\"SELECT * FROM Post\"",[62,38723,2212],{"class":72},[62,38725,38726,38728,38730],{"class":64,"line":161},[62,38727,2418],{"class":72},[62,38729,9155],{"class":122},[62,38731,38732],{"class":72},"(Post.class)\n",[62,38734,38735,38737,38740],{"class":64,"line":167},[62,38736,2418],{"class":72},[62,38738,38739],{"class":122},"list",[62,38741,822],{"class":72},[62,38743,38744],{"class":64,"line":173},[62,38745,223],{"class":72},[62,38747,38748],{"class":64,"line":179},[62,38749,79],{"emptyLinePlaceholder":13},[62,38751,38752,38754,38757,38759,38761,38764,38766,38768],{"class":64,"line":185},[62,38753,194],{"class":68},[62,38755,38756],{"class":72}," Optional\u003C",[62,38758,38700],{"class":68},[62,38760,3135],{"class":72},[62,38762,38763],{"class":122},"findById",[62,38765,1049],{"class":72},[62,38767,6283],{"class":889},[62,38769,768],{"class":72},[62,38771,38772,38774,38776,38778,38780,38783],{"class":64,"line":191},[62,38773,360],{"class":68},[62,38775,38713],{"class":72},[62,38777,38716],{"class":122},[62,38779,2109],{"class":72},[62,38781,38782],{"class":1675},"\"SELECT * FROM Post WHERE id = :id\"",[62,38784,2212],{"class":72},[62,38786,38787,38789,38792,38794,38797],{"class":64,"line":209},[62,38788,2418],{"class":72},[62,38790,38791],{"class":122},"param",[62,38793,2109],{"class":72},[62,38795,38796],{"class":1675},"\"id\"",[62,38798,38799],{"class":72},", id)\n",[62,38801,38802,38804,38806],{"class":64,"line":220},[62,38803,2418],{"class":72},[62,38805,9155],{"class":122},[62,38807,38732],{"class":72},[62,38809,38810,38812,38815],{"class":64,"line":226},[62,38811,2418],{"class":72},[62,38813,38814],{"class":122},"optional",[62,38816,822],{"class":72},[62,38818,38819],{"class":64,"line":231},[62,38820,223],{"class":72},[62,38822,38823],{"class":64,"line":236},[62,38824,79],{"emptyLinePlaceholder":13},[62,38826,38827,38829,38831,38833,38836,38839],{"class":64,"line":242},[62,38828,194],{"class":68},[62,38830,200],{"class":68},[62,38832,23301],{"class":122},[62,38834,38835],{"class":72},"(Post ",[62,38837,38838],{"class":889},"post",[62,38840,768],{"class":72},[62,38842,38843,38846,38848,38850,38853],{"class":64,"line":247},[62,38844,38845],{"class":72}," jdbcClient.",[62,38847,38716],{"class":122},[62,38849,2109],{"class":72},[62,38851,38852],{"class":1675},"\"INSERT INTO Post(id,user_id,title,body) values(?,?,?,?)\"",[62,38854,2212],{"class":72},[62,38856,38857,38859,38862,38864,38866,38868,38870,38873,38875,38877,38879,38881,38883],{"class":64,"line":252},[62,38858,2418],{"class":72},[62,38860,38861],{"class":122},"params",[62,38863,3295],{"class":72},[62,38865,3298],{"class":122},[62,38867,6305],{"class":72},[62,38869,6283],{"class":122},[62,38871,38872],{"class":72},"(), post.",[62,38874,27168],{"class":122},[62,38876,38872],{"class":72},[62,38878,3196],{"class":122},[62,38880,38872],{"class":72},[62,38882,11414],{"class":122},[62,38884,3890],{"class":72},[62,38886,38887,38889,38891],{"class":64,"line":257},[62,38888,2418],{"class":72},[62,38890,1338],{"class":122},[62,38892,822],{"class":72},[62,38894,38895],{"class":64,"line":271},[62,38896,223],{"class":72},[62,38898,38899],{"class":64,"line":281},[62,38900,79],{"emptyLinePlaceholder":13},[62,38902,38903,38905,38907,38910,38912,38914,38916,38918],{"class":64,"line":286},[62,38904,194],{"class":68},[62,38906,200],{"class":68},[62,38908,38909],{"class":122}," update",[62,38911,38835],{"class":72},[62,38913,38838],{"class":889},[62,38915,8624],{"class":72},[62,38917,6283],{"class":889},[62,38919,768],{"class":72},[62,38921,38922,38924,38926,38928,38931],{"class":64,"line":291},[62,38923,38845],{"class":72},[62,38925,38716],{"class":122},[62,38927,2109],{"class":72},[62,38929,38930],{"class":1675},"\"update Post set user_id = ?, title = ?, body = ? where id = ?\"",[62,38932,2212],{"class":72},[62,38934,38935,38937,38939,38941,38943,38945,38947,38949,38951,38953,38955],{"class":64,"line":296},[62,38936,2418],{"class":72},[62,38938,38861],{"class":122},[62,38940,3295],{"class":72},[62,38942,3298],{"class":122},[62,38944,6305],{"class":72},[62,38946,27168],{"class":122},[62,38948,38872],{"class":72},[62,38950,3196],{"class":122},[62,38952,38872],{"class":72},[62,38954,11414],{"class":122},[62,38956,38957],{"class":72},"(), id))\n",[62,38959,38960,38962,38964],{"class":64,"line":302},[62,38961,2418],{"class":72},[62,38963,1338],{"class":122},[62,38965,822],{"class":72},[62,38967,38968],{"class":64,"line":308},[62,38969,223],{"class":72},[62,38971,38972],{"class":64,"line":314},[62,38973,79],{"emptyLinePlaceholder":13},[62,38975,38976,38978,38980,38983,38985,38987],{"class":64,"line":320},[62,38977,194],{"class":68},[62,38979,200],{"class":68},[62,38981,38982],{"class":122}," delete",[62,38984,1049],{"class":72},[62,38986,6283],{"class":889},[62,38988,768],{"class":72},[62,38990,38991,38993,38995,38997,39000],{"class":64,"line":326},[62,38992,38845],{"class":72},[62,38994,38716],{"class":122},[62,38996,2109],{"class":72},[62,38998,38999],{"class":1675},"\"delete from Post where id = :id\"",[62,39001,2212],{"class":72},[62,39003,39004,39006,39008,39010,39012],{"class":64,"line":338},[62,39005,2418],{"class":72},[62,39007,38791],{"class":122},[62,39009,2109],{"class":72},[62,39011,38796],{"class":1675},[62,39013,38799],{"class":72},[62,39015,39016,39018,39020],{"class":64,"line":343},[62,39017,2418],{"class":72},[62,39019,1338],{"class":122},[62,39021,822],{"class":72},[62,39023,39024],{"class":64,"line":357},[62,39025,223],{"class":72},[62,39027,39028],{"class":64,"line":366},[62,39029,79],{"emptyLinePlaceholder":13},[62,39031,39032],{"class":64,"line":371},[62,39033,379],{"class":72},[22,39035,39036,39037,39042],{},"If you’re interested in diving deeper into the JDBC Client you can check out ",[677,39038,39041],{"href":39039,"rel":39040},"https://www.danvega.dev/blog/spring-jdbc-client",[681],"this blog post"," or watch the video below.",[36089,39044],{"id":39045},"JBu5GibEJ4k",[636,39047,39049],{"id":39048},"jdbc-client-vs-spring-data","JDBC Client vs Spring Data",[22,39051,39052],{},"After discussing the new JDBC Client in Spring Boot 3.2, I was asked about the difference between the JDBC Client and Spring Data JDBC. This presented a good opportunity to explore the various options available in Java and Spring for accessing and persisting data to a database.",[22,39054,39055],{},"To illustrate this, I created an example where we connect to a database in Java and explained the origins of the original JDBC Template abstraction in Spring. Then, I demonstrated how to access the same database in Spring using the JDBC Template. While discussing some of the issues with the JDBC Template, I introduced the new JDBC Client in Spring Boot 3.2 and highlighted how its simple and fluent API simplifies database access.",[22,39057,39058],{},"Finally, we delved into Spring Data and how the repository pattern can handle the mundane CRUD functionality, freeing you to focus on your business requirements.",[36089,39060],{"id":39061},"qLDrfebeXS0",[636,39063,39065],{"id":39064},"multiple-datasources-in-spring","Multiple DataSources in Spring",[22,39067,39068],{},"Another question that arose with the new JDBC Client in Spring Boot 3.2 was whether it is possible to use multiple JDBC Clients when working with multiple DataSources in Spring. I found this to be a great question and a perfect opportunity to discuss how a DataSource and JDBC Client are configured for you in Spring.",[36089,39070],{"id":39071},"ZKYFGuukhT4",[636,39073,38423],{"id":39074},"rest-client",[22,39076,39077,39078,39083],{},"Over the years, Spring has offered various solutions for dealing with the complexities of working with web services, particularly RESTful services. In Spring Framework 3.0, the ",[677,39079,39082],{"href":39080,"rel":39081},"https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.html",[681],"Rest Template"," was introduced as a client for making synchronous HTTP requests. This API simplified the interaction with web services by abstracting the underlying HTTP connection, making it easier for developers to perform HTTP operations and parse responses. However, as requirements grew, so did the API, eventually making it more difficult to use.",[22,39085,39086],{},"In Spring Framework 5, Spring WebFlux was introduced as a way to create fully asynchronous and non-blocking applications. Alongside this release, the new Web Client was introduced to address the limitations of the RestTemplate in a non-blocking and reactive programming environment. Drawing from the lessons learned with the RestTemplate, the Spring team improved upon it with a simple-to-learn, fluent API.",[22,39088,39089],{},"Developers really enjoyed the easy to use fluent API of the Web Client. So much that they were bringing in the reactive stack to their blocking MVC applications just to take advantage of the Web Client. Fast forward to Spring Boot 3.2 and the new Rest Client offers a similar experience in imperative style applications.",[22,39091,39092],{},"To get started with rest client you can create an instance using the Rest Client Builder and setting the base url for your service.",[52,39094,39096],{"className":54,"code":39095,"language":56,"meta":57,"style":57},"private final RestClient restClient;\n\npublic PostService(RestClient.Builder builder) {\n this.restClient = builder\n .baseUrl(\"https://jsonplaceholder.typicode.com/\")\n .build();\n}\n",[59,39097,39098,39106,39110,39120,39131,39144,39152],{"__ignoreMap":57},[62,39099,39100,39102,39104],{"class":64,"line":65},[62,39101,1359],{"class":68},[62,39103,458],{"class":68},[62,39105,11287],{"class":72},[62,39107,39108],{"class":64,"line":76},[62,39109,79],{"emptyLinePlaceholder":13},[62,39111,39112,39114,39117],{"class":64,"line":82},[62,39113,116],{"class":68},[62,39115,39116],{"class":122}," PostService",[62,39118,39119],{"class":72},"(RestClient.Builder builder) {\n",[62,39121,39122,39125,39127,39129],{"class":64,"line":89},[62,39123,39124],{"class":149}," this",[62,39126,11311],{"class":72},[62,39128,146],{"class":68},[62,39130,2413],{"class":72},[62,39132,39133,39135,39137,39139,39142],{"class":64,"line":95},[62,39134,2610],{"class":72},[62,39136,11188],{"class":122},[62,39138,2109],{"class":72},[62,39140,39141],{"class":1675},"\"https://jsonplaceholder.typicode.com/\"",[62,39143,2212],{"class":72},[62,39145,39146,39148,39150],{"class":64,"line":101},[62,39147,2610],{"class":72},[62,39149,2189],{"class":122},[62,39151,822],{"class":72},[62,39153,39154],{"class":64,"line":107},[62,39155,379],{"class":72},[22,39157,39158],{},"As a developer, one of the features that I really appreciate about the new Rest Client is how intuitive its API is. When I type \"rest client dot\", I am presented with sensible methods like get, post, put, and delete. This eliminates any confusion I had about which overloaded method to choose from, simplifying everything for me.",[22,39160,39161],{},[653,39162],{"alt":39163,"src":39164},"REST Client Fluent API","/images/blog/2023/12/20/rest_client_dot.png",[22,39166,39167,39168,39171],{},"After calling the ",[59,39169,39170],{},"get()"," method, we can use the fluent API to complete our call. As you can see from the methods below, this is very readable, even for someone who has never used it before.",[52,39173,39175],{"className":54,"code":39174,"language":56,"meta":57,"style":57},"@Service\npublic class PostService {\n\n private final RestClient restClient;\n\n public PostService(RestClient.Builder builder) {\n this.restClient = builder\n .baseUrl(\"https://jsonplaceholder.typicode.com/\")\n .build();\n }\n\n public List\u003CPost> findAll() {\n return restClient.get()\n .uri(\"/posts\")\n .retrieve()\n .body(new ParameterizedTypeReference\u003C>(){});\n }\n\n public ResponseEntity\u003CPost> findById(Long id) {\n return restClient.get()\n .uri(\"/posts/{id}\", id)\n .retrieve()\n .toEntity(Post.class);\n }\n\n}\n",[59,39176,39177,39183,39193,39197,39205,39209,39221,39231,39243,39251,39255,39259,39273,39283,39296,39304,39317,39321,39325,39345,39355,39368,39376,39386,39390,39394],{"__ignoreMap":57},[62,39178,39179,39181],{"class":64,"line":65},[62,39180,942],{"class":72},[62,39182,945],{"class":68},[62,39184,39185,39187,39189,39191],{"class":64,"line":76},[62,39186,116],{"class":68},[62,39188,119],{"class":68},[62,39190,39116],{"class":122},[62,39192,126],{"class":72},[62,39194,39195],{"class":64,"line":82},[62,39196,79],{"emptyLinePlaceholder":13},[62,39198,39199,39201,39203],{"class":64,"line":89},[62,39200,137],{"class":68},[62,39202,458],{"class":68},[62,39204,11287],{"class":72},[62,39206,39207],{"class":64,"line":95},[62,39208,79],{"emptyLinePlaceholder":13},[62,39210,39211,39213,39215,39217,39219],{"class":64,"line":101},[62,39212,194],{"class":68},[62,39214,39116],{"class":122},[62,39216,11165],{"class":72},[62,39218,2160],{"class":889},[62,39220,768],{"class":72},[62,39222,39223,39225,39227,39229],{"class":64,"line":107},[62,39224,2405],{"class":149},[62,39226,11311],{"class":72},[62,39228,146],{"class":68},[62,39230,2413],{"class":72},[62,39232,39233,39235,39237,39239,39241],{"class":64,"line":113},[62,39234,2418],{"class":72},[62,39236,11188],{"class":122},[62,39238,2109],{"class":72},[62,39240,39141],{"class":1675},[62,39242,2212],{"class":72},[62,39244,39245,39247,39249],{"class":64,"line":129},[62,39246,2418],{"class":72},[62,39248,2189],{"class":122},[62,39250,822],{"class":72},[62,39252,39253],{"class":64,"line":134},[62,39254,223],{"class":72},[62,39256,39257],{"class":64,"line":156},[62,39258,79],{"emptyLinePlaceholder":13},[62,39260,39261,39263,39265,39267,39269,39271],{"class":64,"line":161},[62,39262,194],{"class":68},[62,39264,3079],{"class":72},[62,39266,38700],{"class":68},[62,39268,3135],{"class":72},[62,39270,10287],{"class":122},[62,39272,206],{"class":72},[62,39274,39275,39277,39279,39281],{"class":64,"line":167},[62,39276,360],{"class":68},[62,39278,11360],{"class":72},[62,39280,11363],{"class":122},[62,39282,2223],{"class":72},[62,39284,39285,39287,39289,39291,39294],{"class":64,"line":173},[62,39286,2418],{"class":72},[62,39288,11372],{"class":122},[62,39290,2109],{"class":72},[62,39292,39293],{"class":1675},"\"/posts\"",[62,39295,2212],{"class":72},[62,39297,39298,39300,39302],{"class":64,"line":179},[62,39299,2418],{"class":72},[62,39301,11405],{"class":122},[62,39303,2223],{"class":72},[62,39305,39306,39308,39310,39312,39314],{"class":64,"line":185},[62,39307,2418],{"class":72},[62,39309,11414],{"class":122},[62,39311,2109],{"class":72},[62,39313,2426],{"class":68},[62,39315,39316],{"class":72}," ParameterizedTypeReference\u003C>(){});\n",[62,39318,39319],{"class":64,"line":191},[62,39320,223],{"class":72},[62,39322,39323],{"class":64,"line":209},[62,39324,79],{"emptyLinePlaceholder":13},[62,39326,39327,39329,39332,39334,39336,39338,39341,39343],{"class":64,"line":220},[62,39328,194],{"class":68},[62,39330,39331],{"class":72}," ResponseEntity\u003C",[62,39333,38700],{"class":68},[62,39335,3135],{"class":72},[62,39337,38763],{"class":122},[62,39339,39340],{"class":72},"(Long ",[62,39342,6283],{"class":889},[62,39344,768],{"class":72},[62,39346,39347,39349,39351,39353],{"class":64,"line":226},[62,39348,360],{"class":68},[62,39350,11360],{"class":72},[62,39352,11363],{"class":122},[62,39354,2223],{"class":72},[62,39356,39357,39359,39361,39363,39366],{"class":64,"line":231},[62,39358,2418],{"class":72},[62,39360,11372],{"class":122},[62,39362,2109],{"class":72},[62,39364,39365],{"class":1675},"\"/posts/{id}\"",[62,39367,38799],{"class":72},[62,39369,39370,39372,39374],{"class":64,"line":236},[62,39371,2418],{"class":72},[62,39373,11405],{"class":122},[62,39375,2223],{"class":72},[62,39377,39378,39380,39383],{"class":64,"line":242},[62,39379,2418],{"class":72},[62,39381,39382],{"class":122},"toEntity",[62,39384,39385],{"class":72},"(Post.class);\n",[62,39387,39388],{"class":64,"line":247},[62,39389,223],{"class":72},[62,39391,39392],{"class":64,"line":252},[62,39393,79],{"emptyLinePlaceholder":13},[62,39395,39396],{"class":64,"line":257},[62,39397,379],{"class":72},[22,39399,39400,39401,39042],{},"If you’re interested in learning more about the Rest Client you check out ",[677,39402,39405],{"href":39403,"rel":39404},"https://www.danvega.dev/blog/rest-client-first-look",[681],"this post",[36089,39407],{"id":39408},"UDNrJAvKc0k",[636,39410,39412],{"id":39411},"http-interface-clients","Http Interface Clients",[22,39414,39415],{},"Spring Framework 6 and Spring Boot 3 have introduced HTTP Interface Clients. With the Spring Framework, you can define an HTTP service as a Java interface using HTTP exchange methods. By generating a proxy that implements this interface, you can handle the HTTP exchanges. This simplifies remote access to HTTP services and provides the flexibility to choose an API style, such as synchronous or reactive.",[22,39417,39418],{},"This was great, but it was dependent on the Web Client, so you needed to bring in that dependency into your Spring MVC applications for it to work. In Spring Boot 3.2 this is now based on the new Rest Client if you’re in a MVC application.",[22,39420,39421,39422,39425],{},"Instead of having to write the boilerplate low level code to communicate with another service you can just define the method contracts using the ",[59,39423,39424],{},"@HttpExchange"," annotation or one of the specialized versions of it.",[52,39427,39429],{"className":54,"code":39428,"language":56,"meta":57,"style":57},"public interface ArticleClient {\n\n @GetExchange(\"/articles\")\n ResponseEntity\u003CList\u003CArticle>> findAll();\n\n @GetExchange(\"/articles/{id}\")\n Optional\u003CArticle> findOne(@PathVariable Integer id);\n\n @PostExchange(\"/articles\")\n void create(@RequestBody Article article);\n\n @PutExchange(\"/articles/{id}\")\n void update(@RequestBody Article article, @PathVariable Integer id);\n\n @DeleteExchange(\"/articles/{id}\")\n void delete(@PathVariable Integer id);\n}\n",[59,39430,39431,39442,39446,39460,39475,39479,39492,39515,39519,39532,39550,39554,39567,39592,39596,39609,39625],{"__ignoreMap":57},[62,39432,39433,39435,39437,39440],{"class":64,"line":65},[62,39434,116],{"class":68},[62,39436,8531],{"class":68},[62,39438,39439],{"class":122}," ArticleClient",[62,39441,126],{"class":72},[62,39443,39444],{"class":64,"line":76},[62,39445,79],{"emptyLinePlaceholder":13},[62,39447,39448,39450,39453,39455,39458],{"class":64,"line":82},[62,39449,2143],{"class":72},[62,39451,39452],{"class":68},"GetExchange",[62,39454,2109],{"class":72},[62,39456,39457],{"class":1675},"\"/articles\"",[62,39459,2212],{"class":72},[62,39461,39462,39465,39468,39471,39473],{"class":64,"line":89},[62,39463,39464],{"class":72}," ResponseEntity\u003CList\u003C",[62,39466,39467],{"class":68},"Article",[62,39469,39470],{"class":72},">> ",[62,39472,10287],{"class":122},[62,39474,822],{"class":72},[62,39476,39477],{"class":64,"line":95},[62,39478,79],{"emptyLinePlaceholder":13},[62,39480,39481,39483,39485,39487,39490],{"class":64,"line":101},[62,39482,2143],{"class":72},[62,39484,39452],{"class":68},[62,39486,2109],{"class":72},[62,39488,39489],{"class":1675},"\"/articles/{id}\"",[62,39491,2212],{"class":72},[62,39493,39494,39497,39499,39501,39504,39506,39508,39511,39513],{"class":64,"line":107},[62,39495,39496],{"class":72}," Optional\u003C",[62,39498,39467],{"class":68},[62,39500,3135],{"class":72},[62,39502,39503],{"class":122},"findOne",[62,39505,2475],{"class":72},[62,39507,23740],{"class":68},[62,39509,39510],{"class":72}," Integer ",[62,39512,6283],{"class":889},[62,39514,1133],{"class":72},[62,39516,39517],{"class":64,"line":113},[62,39518,79],{"emptyLinePlaceholder":13},[62,39520,39521,39523,39526,39528,39530],{"class":64,"line":129},[62,39522,2143],{"class":72},[62,39524,39525],{"class":68},"PostExchange",[62,39527,2109],{"class":72},[62,39529,39457],{"class":1675},[62,39531,2212],{"class":72},[62,39533,39534,39536,39538,39540,39542,39545,39548],{"class":64,"line":134},[62,39535,11710],{"class":68},[62,39537,23301],{"class":122},[62,39539,2475],{"class":72},[62,39541,2478],{"class":68},[62,39543,39544],{"class":72}," Article ",[62,39546,39547],{"class":889},"article",[62,39549,1133],{"class":72},[62,39551,39552],{"class":64,"line":156},[62,39553,79],{"emptyLinePlaceholder":13},[62,39555,39556,39558,39561,39563,39565],{"class":64,"line":161},[62,39557,2143],{"class":72},[62,39559,39560],{"class":68},"PutExchange",[62,39562,2109],{"class":72},[62,39564,39489],{"class":1675},[62,39566,2212],{"class":72},[62,39568,39569,39571,39573,39575,39577,39579,39581,39584,39586,39588,39590],{"class":64,"line":167},[62,39570,11710],{"class":68},[62,39572,38909],{"class":122},[62,39574,2475],{"class":72},[62,39576,2478],{"class":68},[62,39578,39544],{"class":72},[62,39580,39547],{"class":889},[62,39582,39583],{"class":72},", @",[62,39585,23740],{"class":68},[62,39587,39510],{"class":72},[62,39589,6283],{"class":889},[62,39591,1133],{"class":72},[62,39593,39594],{"class":64,"line":173},[62,39595,79],{"emptyLinePlaceholder":13},[62,39597,39598,39600,39603,39605,39607],{"class":64,"line":179},[62,39599,2143],{"class":72},[62,39601,39602],{"class":68},"DeleteExchange",[62,39604,2109],{"class":72},[62,39606,39489],{"class":1675},[62,39608,2212],{"class":72},[62,39610,39611,39613,39615,39617,39619,39621,39623],{"class":64,"line":185},[62,39612,11710],{"class":68},[62,39614,38982],{"class":122},[62,39616,2475],{"class":72},[62,39618,23740],{"class":68},[62,39620,39510],{"class":72},[62,39622,6283],{"class":889},[62,39624,1133],{"class":72},[62,39626,39627],{"class":64,"line":191},[62,39628,379],{"class":72},[22,39630,39631],{},"I liken this to the Spring Data project where we can use the repository abstraction to get the CRUD functionality out of the box freeing us up to focus on the business requirements of the application.",[22,39633,39634,39635,39640],{},"I had a good conversation on ",[677,39636,39639],{"href":39637,"rel":39638},"https://tanzu.vmware.com/developer/tv/spring-office-hours/0059/",[681],"Spring Office Hours with Olga and Rossen"," from the Spring team about the history of clients in Spring and Http Interfaces.",[26,39642,39644],{"id":39643},"observability","Observability",[22,39646,39647],{},"Observability was a major theme in Spring Boot 3.0 and will continue to be important for the framework and projects in the ecosystem. But what exactly is it, and why should you care about it?",[22,39649,39650],{},"Observability is the ability to observe the internal state of a running system from the outside. It consists of three pillars: logging, metrics, and traces.",[22,39652,39653],{},"It is not enough to simply build an application and release it into production hoping for the best. You need to have visibility into your application's inner workings so that you can proactively address issues instead of reacting to customer complaints.",[22,39655,39656],{},[653,39657],{"alt":39658,"src":39658},"https://images.unsplash.com/photo-1483919283443-8db97e2bcd81?ixlib=rb-4.0.3&q=85&fm=jpg&crop=entropy&cs=srgb",[636,39660,39662],{"id":39661},"observation-api","Observation API",[22,39664,39665],{},"One of the most significant changes in Spring Boot 3.0 was the introduction of a unified Observation API. This API, which is used by the framework internally, is now also available to developers. The purpose of this API is to allow us to instrument our applications. For each instrumentation, we can perform the following actions:",[915,39667,39668,39671,39674,39677,39680],{},[37,39669,39670],{},"Emit Log Messages",[37,39672,39673],{},"Start & Stop Timers",[37,39675,39676],{},"Increment Counters",[37,39678,39679],{},"Start & Stop Spans",[37,39681,39682],{},"Signal Errors",[22,39684,39685,39686,39689],{},"If you want to do metrics and tracing, you would typically need to instrument your application twice. This can result in a lot of boilerplate code, which may discourage you from implementing observation in the first place.To create an observation, you can use the ",[59,39687,39688],{},"createNotStarted"," method of the Observation class. This method simplifies the process of observing code and captures all the necessary information. However, be cautious as this approach can lead to code pollution if you instrument code excessively.",[52,39691,39693],{"className":54,"code":39692,"language":56,"meta":57,"style":57},"@SpringBootApplication\npublic class Application {\n\n private static final Logger log = LoggerFactory.getLogger(Application.class);\n\n public static void main(String[] args) {\n SpringApplication.run(Application.class, args);\n }\n \n @Bean\n CommandLineRunner commandLineRunner(ObservationRegistry registry) {\n return args -> {\n // Create an Observation and observe your code!\n Observation.createNotStarted(\"user.name\", registry)\n .contextualName(\"getting-user-name\")\n // let's assume that you can have 3 user types\n .lowCardinalityKeyValue(\"userType\", \"userType1\")\n // let's assume that this is an arbitrary number \n .highCardinalityKeyValue(\"userId\", \"1234\") \n // this is a shortcut for starting an observation, opening a scope,\n .observe(() -> log.info(\"Hello\")); \n };\n }\n}\n",[59,39694,39695,39701,39711,39715,39734,39738,39758,39766,39770,39774,39780,39794,39804,39809,39824,39838,39843,39862,39867,39887,39892,39916,39920,39924],{"__ignoreMap":57},[62,39696,39697,39699],{"class":64,"line":65},[62,39698,942],{"class":72},[62,39700,2079],{"class":68},[62,39702,39703,39705,39707,39709],{"class":64,"line":76},[62,39704,116],{"class":68},[62,39706,119],{"class":68},[62,39708,2088],{"class":122},[62,39710,126],{"class":72},[62,39712,39713],{"class":64,"line":82},[62,39714,79],{"emptyLinePlaceholder":13},[62,39716,39717,39719,39721,39723,39725,39727,39729,39731],{"class":64,"line":89},[62,39718,137],{"class":68},[62,39720,2101],{"class":68},[62,39722,458],{"class":68},[62,39724,3061],{"class":72},[62,39726,146],{"class":68},[62,39728,3066],{"class":72},[62,39730,3069],{"class":122},[62,39732,39733],{"class":72},"(Application.class);\n",[62,39735,39736],{"class":64,"line":95},[62,39737,79],{"emptyLinePlaceholder":13},[62,39739,39740,39742,39744,39746,39748,39750,39752,39754,39756],{"class":64,"line":101},[62,39741,194],{"class":68},[62,39743,2101],{"class":68},[62,39745,200],{"class":68},[62,39747,2106],{"class":122},[62,39749,2109],{"class":72},[62,39751,973],{"class":68},[62,39753,2114],{"class":72},[62,39755,2117],{"class":889},[62,39757,768],{"class":72},[62,39759,39760,39762,39764],{"class":64,"line":107},[62,39761,2124],{"class":72},[62,39763,2127],{"class":122},[62,39765,2130],{"class":72},[62,39767,39768],{"class":64,"line":113},[62,39769,223],{"class":72},[62,39771,39772],{"class":64,"line":129},[62,39773,961],{"class":72},[62,39775,39776,39778],{"class":64,"line":134},[62,39777,2143],{"class":72},[62,39779,2146],{"class":68},[62,39781,39782,39784,39786,39789,39792],{"class":64,"line":156},[62,39783,2151],{"class":72},[62,39785,2154],{"class":122},[62,39787,39788],{"class":72},"(ObservationRegistry ",[62,39790,39791],{"class":889},"registry",[62,39793,768],{"class":72},[62,39795,39796,39798,39800,39802],{"class":64,"line":161},[62,39797,360],{"class":68},[62,39799,2169],{"class":72},[62,39801,800],{"class":68},[62,39803,126],{"class":72},[62,39805,39806],{"class":64,"line":167},[62,39807,39808],{"class":85}," // Create an Observation and observe your code!\n",[62,39810,39811,39814,39816,39818,39821],{"class":64,"line":173},[62,39812,39813],{"class":72}," Observation.",[62,39815,39688],{"class":122},[62,39817,2109],{"class":72},[62,39819,39820],{"class":1675},"\"user.name\"",[62,39822,39823],{"class":72},", registry)\n",[62,39825,39826,39828,39831,39833,39836],{"class":64,"line":179},[62,39827,2217],{"class":72},[62,39829,39830],{"class":122},"contextualName",[62,39832,2109],{"class":72},[62,39834,39835],{"class":1675},"\"getting-user-name\"",[62,39837,2212],{"class":72},[62,39839,39840],{"class":64,"line":185},[62,39841,39842],{"class":85}," // let's assume that you can have 3 user types\n",[62,39844,39845,39847,39850,39852,39855,39857,39860],{"class":64,"line":191},[62,39846,2217],{"class":72},[62,39848,39849],{"class":122},"lowCardinalityKeyValue",[62,39851,2109],{"class":72},[62,39853,39854],{"class":1675},"\"userType\"",[62,39856,976],{"class":72},[62,39858,39859],{"class":1675},"\"userType1\"",[62,39861,2212],{"class":72},[62,39863,39864],{"class":64,"line":209},[62,39865,39866],{"class":85}," // let's assume that this is an arbitrary number \n",[62,39868,39869,39871,39874,39876,39879,39881,39884],{"class":64,"line":220},[62,39870,2217],{"class":72},[62,39872,39873],{"class":122},"highCardinalityKeyValue",[62,39875,2109],{"class":72},[62,39877,39878],{"class":1675},"\"userId\"",[62,39880,976],{"class":72},[62,39882,39883],{"class":1675},"\"1234\"",[62,39885,39886],{"class":72},") \n",[62,39888,39889],{"class":64,"line":226},[62,39890,39891],{"class":85}," // this is a shortcut for starting an observation, opening a scope,\n",[62,39893,39894,39896,39899,39901,39903,39906,39908,39910,39913],{"class":64,"line":231},[62,39895,2217],{"class":72},[62,39897,39898],{"class":122},"observe",[62,39900,797],{"class":72},[62,39902,800],{"class":68},[62,39904,39905],{"class":72}," log.",[62,39907,12688],{"class":122},[62,39909,2109],{"class":72},[62,39911,39912],{"class":1675},"\"Hello\"",[62,39914,39915],{"class":72},")); \n",[62,39917,39918],{"class":64,"line":236},[62,39919,2252],{"class":72},[62,39921,39922],{"class":64,"line":242},[62,39923,223],{"class":72},[62,39925,39926],{"class":64,"line":247},[62,39927,379],{"class":72},[636,39929,39931],{"id":39930},"observability-improvements-in-spring-boot-32","Observability Improvements in Spring Boot 3.2",[22,39933,39934,39935,39938,39939,39942,39943,39942,39946,39942,39949,39952,39953,39956,39957,39960],{},"Prior to Spring Boot 3.2 you could use the ",[59,39936,39937],{},"@Observed"," annotation but you to do some manual configuration to make this happen. In Spring Boot 3.2 you can now use Micrometers ",[59,39940,39941],{},"[@Timed](https://micrometer.io/docs/concepts#_the_timed_annotation)",", ",[59,39944,39945],{},"@Counted",[59,39947,39948],{},"@NewSpan",[59,39950,39951],{},"@ContinueSpan"," and ",[59,39954,39955],{},"[@Observed](https://micrometer.io/docs/observation#_using_annotations_with_observed)"," annotations. The aspects for them are now auto-configured if you have AspectJ on the classpath. You can do this by adding the ",[59,39958,39959],{},"spring-boot-starter-aop"," dependency to your project.",[52,39962,39964],{"className":1769,"code":39963,"language":1771,"meta":57,"style":57},"\u003Cdependency>\n \u003CgroupId>org.springframework.boot\u003C/groupId>\n \u003CartifactId>spring-boot-starter-aop\u003C/artifactId>\n\u003C/dependency>\n",[59,39965,39966,39974,39986,39999],{"__ignoreMap":57},[62,39967,39968,39970,39972],{"class":64,"line":65},[62,39969,760],{"class":72},[62,39971,1781],{"class":1780},[62,39973,1784],{"class":72},[62,39975,39976,39978,39980,39982,39984],{"class":64,"line":76},[62,39977,1789],{"class":72},[62,39979,1792],{"class":1780},[62,39981,1795],{"class":72},[62,39983,1792],{"class":1780},[62,39985,1784],{"class":72},[62,39987,39988,39990,39992,39995,39997],{"class":64,"line":82},[62,39989,1789],{"class":72},[62,39991,1806],{"class":1780},[62,39993,39994],{"class":72},">spring-boot-starter-aop\u003C/",[62,39996,1806],{"class":1780},[62,39998,1784],{"class":72},[62,40000,40001,40003,40005],{"class":64,"line":89},[62,40002,1818],{"class":72},[62,40004,1781],{"class":1780},[62,40006,1784],{"class":72},[22,40008,40009,40010,40012],{},"Instrumenting the same code becomes much cleaner with the ",[59,40011,39937],{}," annotation!",[52,40014,40016],{"className":54,"code":40015,"language":56,"meta":57,"style":57},"@Bean\n@Observed(name = \"user.name\", contextualName = \"getter-user-name\")\nCommandLineRunner commandLineRunner(ObservationRegistry registry) {\n return args -> {\n log.info(\"Hello, World!\");\n };\n}\n",[59,40017,40018,40024,40051,40061,40071,40083,40088],{"__ignoreMap":57},[62,40019,40020,40022],{"class":64,"line":65},[62,40021,942],{"class":72},[62,40023,2146],{"class":68},[62,40025,40026,40028,40031,40033,40035,40037,40040,40042,40044,40046,40049],{"class":64,"line":76},[62,40027,942],{"class":72},[62,40029,40030],{"class":68},"Observed",[62,40032,2109],{"class":72},[62,40034,3107],{"class":149},[62,40036,2556],{"class":68},[62,40038,40039],{"class":1675}," \"user.name\"",[62,40041,976],{"class":72},[62,40043,39830],{"class":149},[62,40045,2556],{"class":68},[62,40047,40048],{"class":1675}," \"getter-user-name\"",[62,40050,2212],{"class":72},[62,40052,40053,40056,40058],{"class":64,"line":82},[62,40054,40055],{"class":72},"CommandLineRunner ",[62,40057,2154],{"class":122},[62,40059,40060],{"class":72},"(ObservationRegistry registry) {\n",[62,40062,40063,40065,40067,40069],{"class":64,"line":89},[62,40064,2599],{"class":68},[62,40066,2169],{"class":72},[62,40068,800],{"class":68},[62,40070,126],{"class":72},[62,40072,40073,40075,40077,40079,40081],{"class":64,"line":95},[62,40074,12685],{"class":72},[62,40076,12688],{"class":122},[62,40078,2109],{"class":72},[62,40080,27469],{"class":1675},[62,40082,1133],{"class":72},[62,40084,40085],{"class":64,"line":101},[62,40086,40087],{"class":72}," };\n",[62,40089,40090],{"class":64,"line":107},[62,40091,379],{"class":72},[26,40093,1499],{"id":1498},[22,40095,40096],{},"This article covers various topics related to Spring Boot 3.2. It introduces virtual threads as a solution for scalability issues in concurrent programming, discusses the Coordinated Restore at Checkpoint (CRaC) project for faster startup time and reduced resource consumption, explores the new client abstractions for JDBC and REST in Spring Boot, and highlights observability improvements. It also provides links to articles, videos, and blog posts for further reading and examples. I hope you enjoyed this and as always friends",[22,40098,36004],{},[1527,40100,40101],{},"html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .spoOn, html code.shiki .spoOn{--shiki-default:#E36209;--shiki-dark:#FFAB70;--shiki-sepia:#E36209}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .s4-Ip, html code.shiki .s4-Ip{--shiki-default:#6A737D;--shiki-dark:#6A737D;--shiki-sepia:#6A737D}html pre.shiki code .suaqH, html code.shiki .suaqH{--shiki-default:#22863A;--shiki-dark:#85E89D;--shiki-sepia:#22863A}",{"title":57,"searchDepth":76,"depth":76,"links":40103},[40104,40110,40115,40122,40126],{"id":38429,"depth":76,"text":38430,"children":40105},[40106,40107,40108,40109],{"id":38445,"depth":82,"text":38406},{"id":38451,"depth":82,"text":38452},{"id":38483,"depth":82,"text":38484},{"id":38507,"depth":82,"text":38508},{"id":38522,"depth":76,"text":38523,"children":40111},[40112,40113,40114],{"id":38529,"depth":82,"text":38530},{"id":38557,"depth":82,"text":38558},{"id":38567,"depth":82,"text":38568},{"id":38589,"depth":76,"text":38590,"children":40116},[40117,40118,40119,40120,40121],{"id":38602,"depth":82,"text":38420},{"id":39048,"depth":82,"text":39049},{"id":39064,"depth":82,"text":39065},{"id":39074,"depth":82,"text":38423},{"id":39411,"depth":82,"text":39412},{"id":39643,"depth":76,"text":39644,"children":40123},[40124,40125],{"id":39661,"depth":82,"text":39662},{"id":39930,"depth":82,"text":39931},{"id":1498,"depth":76,"text":1499},{"_id":40128,"path":40129,"title":40130,"description":40131,"meta":40132,"body":40137},"content/blog/2023/12/14/virtual-threads-spring-boot.md","/blog/2023/12/14/virtual-threads-spring-boot","Virtual Threads in Spring Boot","In this article, we will discuss Virtual Threads in Java, their significance for Spring Boot developers, and how they can enhance the performance of certain types of Spring Boot applications.",{"slug":40133,"date":40134,"published":13,"tags":40135,"author":17,"cover":40136,"excerpt":-1},"virtual-threads-spring-boot","2023-12-14T17:00:00.000Z",[15,2925],"spring-boot-virtual-threads.png",{"type":19,"value":40138,"toc":40840},[40139,40142,40146,40149,40153,40159,40161,40163,40169,40171,40173,40178,40180,40185,40189,40192,40198,40202,40210,40234,40239,40250,40253,40270,40313,40316,40324,40440,40443,40583,40594,40621,40627,40637,40640,40665,40669,40672,40686,40697,40700,40706,40712,40719,40725,40731,40734,40740,40743,40749,40752,40783,40786,40810,40813,40819,40823,40826,40828,40831,40837],[22,40140,40141],{},"If you're reading this article, chances are you have heard about Project Loom and Virtual Threads in Java. If you haven't heard about them yet, don't worry. I will provide a quick introduction to what they are and why they are so important. Additionally, I will explain why I believe this feature is significant for Spring Boot developers and provide guidance on how you can start using it today. By the end of this article, you will have a better understanding of virtual threads and how they can enhance the performance of certain types of Spring Boot applications.",[26,40143,40145],{"id":40144},"virtual-threads-in-java","Virtual Threads in Java",[22,40147,40148],{},"In Java, each statement inside of a method executes in a thread. Java is Multi-threaded which means more than one thread can be executing a given set of instructions at once. Threads are fundamental to concurrent programming and allow a Java program to perform multiple tasks or operations concurrently, making efficient use of available resources, such as CPU cores.",[22,40150,38458,40151,38462],{},[59,40152,38461],{},[22,40154,40155],{},[653,40156],{"alt":40157,"src":40158},"Java Threads","/images/blog/2023/12/14/java_threads.png",[22,40160,38471],{},[22,40162,38474],{},[22,40164,40165],{},[653,40166],{"alt":40167,"src":40168},"Scalability Options","/images/blog/2023/12/14/scaling_options.png",[636,40170,38484],{"id":38483},[22,40172,38487],{},[22,40174,40175],{},[653,40176],{"alt":38406,"src":40177},"/images/blog/2023/12/14/virtual_threads.png",[22,40179,38496],{},[22,40181,38499,40182,2755],{},[677,40183,38504],{"href":38502,"rel":40184},[681],[26,40186,40188],{"id":40187},"virtual-threads-in-spring","Virtual Threads in Spring",[22,40190,40191],{},"Now that you have an understanding of concurrency in Java and the problem that Virtual Threads are trying to solve, let's discuss it in the context of Spring Boot. If you are building Spring MVC applications that involve blocking operations, such as database communication, writing to an Input Stream, or interacting with an HTTP service, then your application is an ideal candidate to benefit from Virtual Threads.",[22,40193,40194],{},[653,40195],{"alt":40196,"src":40197},"Blocking Requests","/images/blog/2023/12/14/blocking_app.png",[636,40199,40201],{"id":40200},"spring-boot-virtual-threads-example","Spring Boot Virtual Threads Example",[22,40203,40204,40205,40209],{},"In this section we will create a new Spring Boot Application that will perform a blocking operation by communicating with a REST API over HTTP. To get started head over to ",[677,40206,2903],{"href":40207,"rel":40208},"http://start.spring.io",[681]," and create a project with the following properties:",[915,40211,40212,40216,40220,40225,40229],{},[37,40213,40214,2914],{},[646,40215,2913],{},[37,40217,40218,2920],{},[646,40219,2919],{},[37,40221,40222,40224],{},[646,40223,2925],{},": 3.2.0+",[37,40226,40227,34938],{},[646,40228,15],{},[37,40230,40231,40233],{},[646,40232,2931],{},": Web",[22,40235,40236],{},[653,40237],{"alt":24606,"src":40238},"/images/blog/2023/12/14/spring-init.png",[22,40240,40241,40242,40245,40246,40249],{},"Next, we will create a controller that handles a ",[59,40243,40244],{},"GET"," request and makes a call to an external service called HTTPBin. This service is useful because it provides various public endpoints, including one called ",[59,40247,40248],{},"/block"," that allows us to pause for a specified number of seconds.",[22,40251,40252],{},"To create the controller, follow these steps:",[34,40254,40255,40261],{},[37,40256,40257,40258,2755],{},"Create a new controller named ",[59,40259,40260],{},"HttpBinController",[37,40262,40263,40264,19931,40267,2755],{},"Annotate the controller with ",[59,40265,40266],{},"@RestController",[59,40268,40269],{},"@RequestMapping(\"/httpbin\")",[52,40271,40273],{"className":54,"code":40272,"language":56,"meta":57,"style":57},"@RestController\n@RequestMapping(\"/httpbin\")\npublic class HttpBinController {\n\n}\n",[59,40274,40275,40281,40294,40305,40309],{"__ignoreMap":57},[62,40276,40277,40279],{"class":64,"line":65},[62,40278,942],{"class":72},[62,40280,2342],{"class":68},[62,40282,40283,40285,40287,40289,40292],{"class":64,"line":76},[62,40284,942],{"class":72},[62,40286,10592],{"class":68},[62,40288,2109],{"class":72},[62,40290,40291],{"class":1675},"\"/httpbin\"",[62,40293,2212],{"class":72},[62,40295,40296,40298,40300,40303],{"class":64,"line":82},[62,40297,116],{"class":68},[62,40299,119],{"class":68},[62,40301,40302],{"class":122}," HttpBinController",[62,40304,126],{"class":72},[62,40306,40307],{"class":64,"line":89},[62,40308,79],{"emptyLinePlaceholder":13},[62,40310,40311],{"class":64,"line":95},[62,40312,379],{"class":72},[22,40314,40315],{},"To communicate with HttpBin, we can utilize the new RestClient in Spring Boot 3.2. You can use the builder to set the base URL and obtain a new instance for using the Rest Client.",[22,40317,40318,40319,40323],{},"For more information, refer to the ",[677,40320,40322],{"href":39403,"rel":40321},[681],"Rest Client First Look"," blog post.",[52,40325,40327],{"className":54,"code":40326,"language":56,"meta":57,"style":57},"@RestController\n@RequestMapping(\"/httpbin\")\npublic class HttpBinController {\n\n private static final Logger log = LoggerFactory.getLogger(HttpBinController.class);\n private final RestClient restClient;\n\n public HttpBinController(RestClient.Builder restClientBuilder) {\n restClient = restClientBuilder.baseUrl(\"https://httpbin.org/\").build();\n }\n\n}\n",[59,40328,40329,40335,40347,40357,40361,40380,40388,40392,40405,40428,40432,40436],{"__ignoreMap":57},[62,40330,40331,40333],{"class":64,"line":65},[62,40332,942],{"class":72},[62,40334,2342],{"class":68},[62,40336,40337,40339,40341,40343,40345],{"class":64,"line":76},[62,40338,942],{"class":72},[62,40340,10592],{"class":68},[62,40342,2109],{"class":72},[62,40344,40291],{"class":1675},[62,40346,2212],{"class":72},[62,40348,40349,40351,40353,40355],{"class":64,"line":82},[62,40350,116],{"class":68},[62,40352,119],{"class":68},[62,40354,40302],{"class":122},[62,40356,126],{"class":72},[62,40358,40359],{"class":64,"line":89},[62,40360,79],{"emptyLinePlaceholder":13},[62,40362,40363,40365,40367,40369,40371,40373,40375,40377],{"class":64,"line":95},[62,40364,137],{"class":68},[62,40366,2101],{"class":68},[62,40368,458],{"class":68},[62,40370,3061],{"class":72},[62,40372,146],{"class":68},[62,40374,3066],{"class":72},[62,40376,3069],{"class":122},[62,40378,40379],{"class":72},"(HttpBinController.class);\n",[62,40381,40382,40384,40386],{"class":64,"line":101},[62,40383,137],{"class":68},[62,40385,458],{"class":68},[62,40387,11287],{"class":72},[62,40389,40390],{"class":64,"line":107},[62,40391,79],{"emptyLinePlaceholder":13},[62,40393,40394,40396,40398,40400,40403],{"class":64,"line":113},[62,40395,194],{"class":68},[62,40397,40302],{"class":122},[62,40399,11165],{"class":72},[62,40401,40402],{"class":889},"restClientBuilder",[62,40404,768],{"class":72},[62,40406,40407,40410,40412,40415,40417,40419,40422,40424,40426],{"class":64,"line":129},[62,40408,40409],{"class":72}," restClient ",[62,40411,146],{"class":68},[62,40413,40414],{"class":72}," restClientBuilder.",[62,40416,11188],{"class":122},[62,40418,2109],{"class":72},[62,40420,40421],{"class":1675},"\"https://httpbin.org/\"",[62,40423,15503],{"class":72},[62,40425,2189],{"class":122},[62,40427,822],{"class":72},[62,40429,40430],{"class":64,"line":134},[62,40431,223],{"class":72},[62,40433,40434],{"class":64,"line":156},[62,40435,79],{"emptyLinePlaceholder":13},[62,40437,40438],{"class":64,"line":161},[62,40439,379],{"class":72},[22,40441,40442],{},"Next, create a new method called \"delay\" that takes the number of seconds to block for as a parameter. Use the Rest Client to make a call to the HttpBin service, which does not return a body. Utilize a logger to log the status code and the current thread, allowing us to determine whether we are using normal platform threads or virtual threads.",[52,40444,40446],{"className":54,"code":40445,"language":56,"meta":57,"style":57},"@GetMapping(\"/block/{seconds}\")\npublic String delay(@PathVariable int seconds) {\n ResponseEntity\u003CVoid> result = restClient.get()\n .uri(\"/delay/\" + seconds)\n .retrieve()\n .toBodilessEntity();\n\n log.info(\"{} on {}\", result.getStatusCode(), Thread.currentThread());\n\n return Thread.currentThread().toString();\n}\n",[59,40447,40448,40461,40479,40498,40514,40522,40531,40535,40560,40564,40579],{"__ignoreMap":57},[62,40449,40450,40452,40454,40456,40459],{"class":64,"line":65},[62,40451,942],{"class":72},[62,40453,2548],{"class":68},[62,40455,2109],{"class":72},[62,40457,40458],{"class":1675},"\"/block/{seconds}\"",[62,40460,2212],{"class":72},[62,40462,40463,40465,40467,40470,40472,40474,40476],{"class":64,"line":76},[62,40464,116],{"class":68},[62,40466,2469],{"class":72},[62,40468,40469],{"class":122},"delay",[62,40471,2475],{"class":72},[62,40473,23740],{"class":68},[62,40475,140],{"class":68},[62,40477,40478],{"class":72}," seconds) {\n",[62,40480,40481,40484,40487,40490,40492,40494,40496],{"class":64,"line":82},[62,40482,40483],{"class":72}," ResponseEntity\u003C",[62,40485,40486],{"class":68},"Void",[62,40488,40489],{"class":72},"> result ",[62,40491,146],{"class":68},[62,40493,11360],{"class":72},[62,40495,11363],{"class":122},[62,40497,2223],{"class":72},[62,40499,40500,40502,40504,40506,40509,40511],{"class":64,"line":89},[62,40501,2610],{"class":72},[62,40503,11372],{"class":122},[62,40505,2109],{"class":72},[62,40507,40508],{"class":1675},"\"/delay/\"",[62,40510,4507],{"class":68},[62,40512,40513],{"class":72}," seconds)\n",[62,40515,40516,40518,40520],{"class":64,"line":95},[62,40517,2610],{"class":72},[62,40519,11405],{"class":122},[62,40521,2223],{"class":72},[62,40523,40524,40526,40529],{"class":64,"line":101},[62,40525,2610],{"class":72},[62,40527,40528],{"class":122},"toBodilessEntity",[62,40530,822],{"class":72},[62,40532,40533],{"class":64,"line":107},[62,40534,79],{"emptyLinePlaceholder":13},[62,40536,40537,40540,40542,40544,40547,40550,40552,40555,40558],{"class":64,"line":113},[62,40538,40539],{"class":72}," log.",[62,40541,12688],{"class":122},[62,40543,2109],{"class":72},[62,40545,40546],{"class":1675},"\"{} on {}\"",[62,40548,40549],{"class":72},", result.",[62,40551,11781],{"class":122},[62,40553,40554],{"class":72},"(), Thread.",[62,40556,40557],{"class":122},"currentThread",[62,40559,1091],{"class":72},[62,40561,40562],{"class":64,"line":129},[62,40563,79],{"emptyLinePlaceholder":13},[62,40565,40566,40568,40571,40573,40575,40577],{"class":64,"line":134},[62,40567,2599],{"class":68},[62,40569,40570],{"class":72}," Thread.",[62,40572,40557],{"class":122},[62,40574,3229],{"class":72},[62,40576,23175],{"class":122},[62,40578,822],{"class":72},[62,40580,40581],{"class":64,"line":156},[62,40582,379],{"class":72},[22,40584,40585,40586,40588,40589,40593],{},"If you run the application and perform a ",[59,40587,40244],{}," request to ",[677,40590,40591],{"href":40591,"rel":40592},"http://localhost:8080/httpbin/block/3",[681],", you should see the following result in the console. Upon closer inspection, you will notice that the application is running on the main thread. This behavior is consistent with previous versions of Spring Boot + Java.",[52,40595,40597],{"className":1663,"code":40596,"language":1665,"meta":57,"style":57},"2023-12-14T09:39:27.943-05:00 INFO 12176 --- [nio-8080-exec-1] d.d.h.HttpBinController : 200 OK on Thread[#38,http-nio-8080-exec-1,5,main]\n",[59,40598,40599],{"__ignoreMap":57},[62,40600,40601,40604,40607,40610,40613,40616,40618],{"class":64,"line":65},[62,40602,40603],{"class":122},"2023-12-14T09:39:27.943-05:00",[62,40605,40606],{"class":1675}," INFO",[62,40608,40609],{"class":149}," 12176",[62,40611,40612],{"class":149}," ---",[62,40614,40615],{"class":72}," [nio-8080-exec-1] d.d.h.HttpBinController ",[62,40617,1266],{"class":149},[62,40619,40620],{"class":72}," 200 OK on Thread[#38,http-nio-8080-exec-1,5,main]\n",[22,40622,40623,40624,40626],{},"Enabling Virtual Threads in your Spring Boot application couldn't be easier. Open the ",[59,40625,1265],{}," file and set the following property.",[52,40628,40629],{"className":1269,"code":1270,"language":1271,"meta":57,"style":57},[59,40630,40631],{"__ignoreMap":57},[62,40632,40633,40635],{"class":64,"line":65},[62,40634,1278],{"class":68},[62,40636,1281],{"class":72},[22,40638,40639],{},"If you rerun the application and check the console, you should see that we are now running on Virtual Threads.",[52,40641,40643],{"className":1663,"code":40642,"language":1665,"meta":57,"style":57},"2023-12-14T09:43:10.088-05:00 INFO 12418 --- [omcat-handler-0] d.d.h.HttpBinController : 200 OK on VirtualThread[#48,tomcat-handler-0]/runnable@ForkJoinPool-1-worker-1\n",[59,40644,40645],{"__ignoreMap":57},[62,40646,40647,40650,40652,40655,40657,40660,40662],{"class":64,"line":65},[62,40648,40649],{"class":122},"2023-12-14T09:43:10.088-05:00",[62,40651,40606],{"class":1675},[62,40653,40654],{"class":149}," 12418",[62,40656,40612],{"class":149},[62,40658,40659],{"class":72}," [omcat-handler-0] d.d.h.HttpBinController ",[62,40661,1266],{"class":149},[62,40663,40664],{"class":72}," 200 OK on VirtualThread[#48,tomcat-handler-0]/runnable@ForkJoinPool-1-worker-1\n",[636,40666,40668],{"id":40667},"virtual-threads-in-action-with-apache-benchmark","Virtual Threads in Action with Apache Benchmark",[22,40670,40671],{},"Now that you know how to enable Virtual Threads in a Spring Boot Application (it's quite trivial), let's explore how we can run some initial benchmarks to assess the potential performance improvements in our applications.",[22,40673,40674,40675,40680,40681,40685],{},"If you wish to conduct these initial benchmarks, you can utilize a tool like ",[677,40676,40679],{"href":40677,"rel":40678},"https://httpd.apache.org/docs/2.4/programs/ab.html",[681],"Apache Benchmark",", which comes pre-installed with macOS and is the reason I recommend it. If you are using a different operating system, there are various alternative tools available. To begin, return to your ",[677,40682,1265],{"href":40683,"rel":40684},"http://application.properties/",[681]," file and disable Virtual Threads.",[52,40687,40689],{"className":1269,"code":40688,"language":1271,"meta":57,"style":57},"spring.threads.virtual.enabled=false\n",[59,40690,40691],{"__ignoreMap":57},[62,40692,40693,40695],{"class":64,"line":65},[62,40694,1278],{"class":68},[62,40696,14152],{"class":72},[22,40698,40699],{},"Run the application and open up a terminal to use Apache Benchmark. This first test will send 10 requests that block for 3 seconds. As you would expect this test takes around 30 seconds to complete.",[22,40701,40702],{},[653,40703],{"alt":40704,"src":40705},"BenchMark Test 1","/images/blog/2023/12/14/10_requests.png",[22,40707,40708],{},[653,40709],{"alt":40710,"src":40711},"BenchMark Test 1 Results","/images/blog/2023/12/14/10_requests_results.png",[22,40713,40714,40715,40718],{},"Now, let's run the same test but handle multiple requests at a time. To enable concurrency in Apache Benchmark, you can use the ",[59,40716,40717],{},"c"," argument followed by the number of concurrent connections. In this example, I will run the test with 2 concurrent connections. As a result, the test should take half the time and complete in approximately 15 seconds.",[22,40720,40721],{},[653,40722],{"alt":40723,"src":40724},"BenchMark Test 2","/images/blog/2023/12/14/10_2_requests.png",[22,40726,40727],{},[653,40728],{"alt":40729,"src":40730},"BenchMark Test 2 Results","/images/blog/2023/12/14/10_2_requests_results.png",[22,40732,40733],{},"We recently ran an example that utilized two threads simultaneously. This raises the question: \"How many threads can I run concurrently?\" The answer to this question depends on the machine you are using. In Java, a thread acts as a thin wrapper around a platform or operating system thread. Typically, you have one OS thread per core. Since my machine has 10 cores, I can theoretically run up to 10 threads concurrently.",[22,40735,40736],{},[653,40737],{"alt":40738,"src":40739},"Maximum Cores","/images/blog/2023/12/14/cores.png",[22,40741,40742],{},"That means if we run the following test it should take somewhere around 3 seconds.",[22,40744,40745],{},[653,40746],{"alt":40747,"src":40748},"BenchMark Test 3","/images/blog/2023/12/14/10_10_requests.png",[22,40750,40751],{},"In this next example, let's explicitly set the maximum number of threads we can use at once to 10.",[52,40753,40755],{"className":1663,"code":40754,"language":1665,"meta":57,"style":57},"# tomcat threads defaults to 200, 0 is unlimited\nserver.tomcat.threads.max=10\n\nspring.threads.virtual.enabled=false\n",[59,40756,40757,40762,40770,40774],{"__ignoreMap":57},[62,40758,40759],{"class":64,"line":65},[62,40760,40761],{"class":85},"# tomcat threads defaults to 200, 0 is unlimited\n",[62,40763,40764,40767],{"class":64,"line":76},[62,40765,40766],{"class":122},"server.tomcat.threads.max",[62,40768,40769],{"class":1675},"=10\n",[62,40771,40772],{"class":64,"line":82},[62,40773,79],{"emptyLinePlaceholder":13},[62,40775,40776,40778,40780],{"class":64,"line":89},[62,40777,1278],{"class":122},[62,40779,146],{"class":1675},[62,40781,40782],{"class":149},"false\n",[22,40784,40785],{},"Know that the maximum number of concurrent requests that can be handled at one time. How long would you expect the following benchmark to take?",[52,40787,40789],{"className":1663,"code":40788,"language":1665,"meta":57,"style":57},"ab -n 60 -c 20 http://localhost:8080/httpbin/block/3\n",[59,40790,40791],{"__ignoreMap":57},[62,40792,40793,40796,40799,40801,40804,40807],{"class":64,"line":65},[62,40794,40795],{"class":122},"ab",[62,40797,40798],{"class":149}," -n",[62,40800,6976],{"class":149},[62,40802,40803],{"class":149}," -c",[62,40805,40806],{"class":149}," 20",[62,40808,40809],{"class":1675}," http://localhost:8080/httpbin/block/3\n",[22,40811,40812],{},"Your first guess might be 9 seconds, but that would be incorrect. This is because even though we are sending 20 concurrent requests, our application can only handle 10 at a time. The first 10 requests come in and the system processes them. Since these are blocking requests, the thread must complete before it can handle other requests. If the application handles 10 requests every 3 seconds, this test will take somewhere around 18 seconds (6x3=18).",[22,40814,40815],{},[653,40816],{"alt":40817,"src":40818},"BenchMark Test 4","/images/blog/2023/12/14/60_20_requests.png",[636,40820,40822],{"id":40821},"where-virtual-threads-dont-make-sense","Where Virtual Threads Don’t Make Sense",[22,40824,40825],{},"We know that Virtual Threads make sense when our applications are performing some type of blocking operation. Where it doesn’t make sense is in a purely CPU intensive application. Another scenario where they don’t make sense is if you’re already using asynchronous programming. In either scenario you can enable them but you won’t see any performance benefits.",[26,40827,1499],{"id":1498},[22,40829,40830],{},"This blog post discusses the concept of Virtual Threads in Java, their significance for Spring Boot developers, and how they can enhance the performance of certain types of Spring Boot applications. It explains the challenges of asynchronous programming, introduces Virtual Threads as a solution, and provides an example of using Virtual Threads in a Spring Boot application. The post also includes benchmarks to demonstrate the performance improvement achieved with Virtual Threads and highlights the scenarios where Virtual Threads are beneficial.",[22,40832,40833,40834,40836],{},"Thank You,",[36006,40835],{},"\nHappy coding!",[1527,40838,40839],{},"html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .spoOn, html code.shiki .spoOn{--shiki-default:#E36209;--shiki-dark:#FFAB70;--shiki-sepia:#E36209}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}html pre.shiki code .s4-Ip, html code.shiki .s4-Ip{--shiki-default:#6A737D;--shiki-dark:#6A737D;--shiki-sepia:#6A737D}",{"title":57,"searchDepth":76,"depth":76,"links":40841},[40842,40845,40850],{"id":40144,"depth":76,"text":40145,"children":40843},[40844],{"id":38483,"depth":82,"text":38484},{"id":40187,"depth":76,"text":40188,"children":40846},[40847,40848,40849],{"id":40200,"depth":82,"text":40201},{"id":40667,"depth":82,"text":40668},{"id":40821,"depth":82,"text":40822},{"id":1498,"depth":76,"text":1499},{"_id":40852,"path":40853,"title":40854,"description":40855,"meta":40856,"body":40861},"content/blog/2023/09/17/spring-boot-actuator.md","/blog/2023/09/17/spring-boot-actuator","The Spring Boot Actuator is the one dependency you should include in every project","In this article, we will focus on the most important Spring Boot starter that you should include in all of your projects. The Spring Boot Actuator.",{"slug":40857,"date":40858,"published":13,"tags":40859,"author":17,"cover":40860,"excerpt":-1},"spring-boot-actuator","2023-09-17T17:00:00.000Z",[2925],"spring-boot-actuator.png",{"type":19,"value":40862,"toc":41204},[40863,40866,40870,40873,40881,40884,40887,40893,40897,40900,40909,40912,40915,40919,40922,40939,40942,40945,40949,40952,40972,40978,40981,40985,40992,40995,40998,41002,41011,41014,41017,41020,41023,41030,41051,41057,41061,41064,41084,41182,41185,41189,41192,41195,41198,41201],[22,40864,40865],{},"In this article, we will focus on the most important Spring Boot starter that you should include in all of your projects. When creating a new Spring Boot Application, you have multiple choices for the dependencies you should include based on what you're building. While this is just my opinion, I will share what I believe is the number one Spring Boot Starter that you should include in each of your applications. Before reading the entire article, do you have any guesses? If you guessed the Spring Boot Actuator, you are correct. Let's take a look at what the Spring Boot Actuator is and why you should include it in every project.",[26,40867,40869],{"id":40868},"what-is-a-spring-boot-starter","What is a Spring Boot Starter",[22,40871,40872],{},"For those who are not familiar with the concept, Spring Boot Starters are a set of convenient dependency descriptors that you can include in your application. You get a one-stop-shop for all the Spring and related technology you may need. Basically, starters are a set of convenient dependency bundles that you can include in your application.",[22,40874,40875,40876,40880],{},"You head over to ",[677,40877,40879],{"href":2901,"rel":40878},[681],"start Spring IO",", fill in some metadata about your project, and then shift over to the right side of the screen. Here you have a plethora of dependencies to choose from.",[22,40882,40883],{},"The pivotal question here is: From this vast range of dependencies, which one should you include in every project you create?",[22,40885,40886],{},"There's no universally correct answer to this question, as it largely hinges on your specific project requirements. However, in this post, I am sharing my opinion or rather the solution that I religiously follow - Include the Spring Boot Actuator in every single project.",[22,40888,40889],{},[653,40890],{"alt":40891,"src":40892},"Start","/images/blog/2023/09/17/photo-1608496601160-f86d19a44f9f.jpeg",[26,40894,40896],{"id":40895},"why-the-spring-boot-actuator","Why the Spring Boot Actuator?",[22,40898,40899],{},"Our ultimate goal while developing a new application is to usher it to the promised land - production. Regardless of whether you're building a straightforward demo, an MVP (Minimum Viable Product), or even the next-gen SaaS offering, the application should be production-ready.",[22,40901,40902,40903,40908],{},"This is where the ",[677,40904,40907],{"href":40905,"rel":40906},"https://docs.spring.io/spring-boot/docs/current/reference/html/actuator.html",[681],"Spring Boot Actuator"," comes into play.",[22,40910,40911],{},"It offers several additional features to monitor and manage your application when you push it into production, providing invaluable insights into app functionality. This is much beyond merely pushing code into production and waiting for traffic.",[22,40913,40914],{},"The Spring Boot Actuator fosters a solid understanding of what's happening within your app.",[26,40916,40918],{"id":40917},"setting-the-stage-building-a-new-application","Setting the Stage: Building a New Application",[22,40920,40921],{},"In today's discussion, the primary objectives are:",[915,40923,40924,40927,40930,40933,40936],{},[37,40925,40926],{},"Building a new application from scratch,",[37,40928,40929],{},"Incorporating Spring Boot Actuator,",[37,40931,40932],{},"Discussing what you get out of the box,",[37,40934,40935],{},"Exploring potentials to configure it,",[37,40937,40938],{},"Reviewing several endpoints that arrive with Spring Boot Actuator.",[22,40940,40941],{},"In addition to this, we will also delve into creating your custom endpoints for tailoring added functionality within the actuator.",[22,40943,40944],{},"So, are we all set for an exciting journey into the realm of the Spring Boot Actuator? Well, let's roll up our sleeves and get coding!",[26,40946,40948],{"id":40947},"creating-a-new-project-with-spring-boot-actuator","Creating a New Project with Spring Boot Actuator",[22,40950,40951],{},"To kickstart our process, navigate to start Spring IO and create a new project. Here's a step-by-step guide to assist you with this:",[915,40953,40954,40957,40960,40963,40966,40969],{},[37,40955,40956],{},"Choose maven java as your project type.",[37,40958,40959],{},"Select the latest stable release of Spring Boot, the current one being 3.1.3.",[37,40961,40962],{},"Fill in the metadata, say, developer name - Dan Vega, and project name - actuator demo.",[37,40964,40965],{},"Click on dependencies and choose Spring Boot Actuator.",[37,40967,40968],{},"Depending on the type of application, you can add other dependencies. In this case, I will keep the app simple and include the web dependency for building a web application.",[37,40970,40971],{},"Finally, generate the project, download the zip file, and open it in your preferred IDE (Integrated Development Environment).",[22,40973,40974],{},[653,40975],{"alt":40976,"src":40977},"Spring Boot Initializr","/images/blog/2023/09/17/spring-init.png",[22,40979,40980],{},"Now that we have a new project with the Spring Boot Actuator dependency configured, it's time to explore what the actuator brings to the table and how we can leverage it to its optimum potential.",[26,40982,40984],{"id":40983},"running-the-application-taking-a-peep-underneath-the-hood","Running the Application: Taking a Peep Underneath the Hood",[22,40986,40987,40988,2755],{},"If you do nothing and just run the application as is you will see in the console that there is mapping exposed at “/actuator”. Head over to your browser and open ",[677,40989,40990],{"href":40990,"rel":40991},"http://localhost:8080/actuator",[681],[22,40993,40994],{},"Here, you will notice some exposed endpoints. Start your exploration with the Health endpoint; it will display a status of \"up.\" This status update is an indicator for other applications about the functioning status of your application.",[22,40996,40997],{},"However, out-of-the-box, Spring Boot Actuator doesn't offer much. To understand why, let's hop over to the documentation.",[26,40999,41001],{"id":41000},"diving-deeper-into-the-spring-boot-actuator","Diving Deeper into the Spring Boot Actuator",[22,41003,41004,41005,41010],{},"Examining the ",[677,41006,41009],{"href":41007,"rel":41008},"https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#production-ready",[681],"Spring Boot documentation",", you can find a section titled \"Production-ready features.\" Here's where you will encounter details about the Actuator, Endpoints, metrics observability, etc.",[22,41012,41013],{},"The Actuator provides a variety of features to help you monitor and manage your application. You can manage and monitor these features either over HTTP endpoints or JMX. Currently, we're working with HTTP.",[22,41015,41016],{},"You can apply features like auditing, health, and metrics gathering to your applications automatically. How to do this? Simply include the Spring Boot Starter Actuator!",[22,41018,41019],{},"The Actuator Endpoints enable you to monitor and interact with your application. While there are several built-in endpoints, you also hold the liberty to add your own. Some of these might be contingent on whether you have another dependency included.",[22,41021,41022],{},"Most of these endpoints are disabled by default, and then you can go through and configure them as per your requirements. In essence, they offer a substantial amount of insight about the beans in your application context.",[22,41024,41025,41026,41029],{},"If you open up ",[59,41027,41028],{},"[application.properties](http://application.properties)"," you can set the following properties to enable all of the endpoints and get more information in the health endpoint.",[52,41031,41033],{"className":1269,"code":41032,"language":1271,"meta":57,"style":57},"management.endpoints.web.exposure.include=*\nmanagement.endpoint.health.show-details=always\n",[59,41034,41035,41043],{"__ignoreMap":57},[62,41036,41037,41040],{"class":64,"line":65},[62,41038,41039],{"class":68},"management.endpoints.web.exposure.include",[62,41041,41042],{"class":72},"=*\n",[62,41044,41045,41048],{"class":64,"line":76},[62,41046,41047],{"class":68},"management.endpoint.health.show-details",[62,41049,41050],{"class":72},"=always\n",[22,41052,41053],{},[653,41054],{"alt":41055,"src":41056},"actuator_endpoints.png","/images/blog/2023/09/17/actuator_endpoints.png",[26,41058,41060],{"id":41059},"extending-the-usability-of-spring-boot-actuator-creating-your-own-custom-endpoint","Extending the Usability of Spring Boot Actuator: Creating Your Own Custom Endpoint",[22,41062,41063],{},"While the pre-built endpoints of Spring Boot Actuator are great, you might experience the need to develop your own endpoint. The following illustration shows how to create a custom endpoint:",[915,41065,41066,41069,41072,41075,41078,41081],{},[37,41067,41068],{},"To create a new random endpoint, add the @Endpoint annotation and give it an ID of \"random.\"",[37,41070,41071],{},"Implement different operations based on your needs.",[37,41073,41074],{},"In this case, we want to provide a random number via the custom endpoint. So, @ReadOperation would be the most fitting choice here.",[37,41076,41077],{},"Enter the return type (integer in this case).",[37,41079,41080],{},"Write down the method to create a random number, such as {return new Random().nextInt(100);}",[37,41082,41083],{},"Lastly, it's essential to annotate the custom endpoint class with Spring's @Service annotation to ensure that Spring recognizes it, and it's correctly injected into the application context.",[52,41085,41087],{"className":54,"code":41086,"language":56,"meta":57,"style":57},"@Service\n@Endpoint(id = \"random\")\npublic class RandomEndpoint {\n\n @ReadOperation\n public String random() {\n return String.valueOf(new Random().nextInt());\n }\n\n}\n",[59,41088,41089,41095,41113,41124,41128,41135,41146,41170,41174,41178],{"__ignoreMap":57},[62,41090,41091,41093],{"class":64,"line":65},[62,41092,942],{"class":72},[62,41094,945],{"class":68},[62,41096,41097,41099,41102,41104,41106,41108,41111],{"class":64,"line":76},[62,41098,942],{"class":72},[62,41100,41101],{"class":68},"Endpoint",[62,41103,2109],{"class":72},[62,41105,6283],{"class":149},[62,41107,2556],{"class":68},[62,41109,41110],{"class":1675}," \"random\"",[62,41112,2212],{"class":72},[62,41114,41115,41117,41119,41122],{"class":64,"line":82},[62,41116,116],{"class":68},[62,41118,119],{"class":68},[62,41120,41121],{"class":122}," RandomEndpoint",[62,41123,126],{"class":72},[62,41125,41126],{"class":64,"line":89},[62,41127,79],{"emptyLinePlaceholder":13},[62,41129,41130,41132],{"class":64,"line":95},[62,41131,2143],{"class":72},[62,41133,41134],{"class":68},"ReadOperation\n",[62,41136,41137,41139,41141,41144],{"class":64,"line":101},[62,41138,194],{"class":68},[62,41140,2469],{"class":72},[62,41142,41143],{"class":122},"random",[62,41145,206],{"class":72},[62,41147,41148,41150,41153,41156,41158,41160,41163,41165,41168],{"class":64,"line":107},[62,41149,360],{"class":68},[62,41151,41152],{"class":72}," String.",[62,41154,41155],{"class":122},"valueOf",[62,41157,2109],{"class":72},[62,41159,2426],{"class":68},[62,41161,41162],{"class":122}," Random",[62,41164,3229],{"class":72},[62,41166,41167],{"class":122},"nextInt",[62,41169,1091],{"class":72},[62,41171,41172],{"class":64,"line":113},[62,41173,223],{"class":72},[62,41175,41176],{"class":64,"line":129},[62,41177,79],{"emptyLinePlaceholder":13},[62,41179,41180],{"class":64,"line":134},[62,41181,379],{"class":72},[22,41183,41184],{},"Congratulations! You have successfully extended the usability of the Spring Boot Actuator by creating your own custom endpoint.",[26,41186,41188],{"id":41187},"summing-up-the-spring-boot-actuator-journey","Summing Up the Spring Boot Actuator Journey",[22,41190,41191],{},"The Spring Boot Actuator proves to be an exceptionally versatile tool for Spring Developers, aiding in efficient application monitoring and management when pushed into production with minimal effort. It provides an additional layer of insight into application functionality, thereby leaving no room for guesswork.",[22,41193,41194],{},"Whether it's the out-of-the-box usability or the freedom to extend its capability further by developing your custom endpoints, the Spring Boot Actuator checks all the boxes.",[22,41196,41197],{},"So, if you are planning to embark on your next Spring Boot project, do give the Spring Boot Actuator a whirl. It is one Spring Boot Starter that promises to revamp your coding journey, making it more insightful and streamlined.",[22,41199,41200],{},"Happy coding, folks!",[1527,41202,41203],{},"html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}",{"title":57,"searchDepth":76,"depth":76,"links":41205},[41206,41207,41208,41209,41210,41211,41212,41213],{"id":40868,"depth":76,"text":40869},{"id":40895,"depth":76,"text":40896},{"id":40917,"depth":76,"text":40918},{"id":40947,"depth":76,"text":40948},{"id":40983,"depth":76,"text":40984},{"id":41000,"depth":76,"text":41001},{"id":41059,"depth":76,"text":41060},{"id":41187,"depth":76,"text":41188},{"_id":41215,"path":41216,"title":41217,"description":41218,"meta":41219,"body":41224},"content/blog/2023/09/11/spring-jdbc-client.md","/blog/2023/09/11/spring-jdbc-client","A First Look at the new JDBC Client in Spring Boot 3.2","In this tutorial you will learn about the new JDBC Client in Spring Framework 6.1 and Spring Boot 3.2.",{"slug":41220,"date":41221,"published":13,"tags":41222,"author":17,"cover":41223,"excerpt":-1},"spring-jdbc-client","2023-09-11T17:00:00.000Z",[2925],"./spring-jdbc-client.png",{"type":19,"value":41225,"toc":43606},[41226,41229,41232,41236,41239,41242,41245,41248,41252,41255,41258,41262,41265,41271,41275,41282,41285,41289,41308,41338,41347,41681,41697,41808,41812,41822,41963,41969,41996,41999,42003,42012,42018,42733,42736,42740,42753,42759,43308,43311,43315,43330,43432,43590,43593,43595,43598,43603],[22,41227,41228],{},"In this tutorial, we'll be diving into the fresh waters of the new JDBC client in Spring Framework 6.1 and Spring Boot 3.2.",[22,41230,41231],{},"We'll be not only reviewing how to use it but also discussing some of the associated advantages. So, without further ado, let's delve into it.",[26,41233,41235],{"id":41234},"remembering-the-journey-jdbc-template","Remembering the Journey - JDBC Template",[22,41237,41238],{},"Before we get started, let's talk about how we got here. Interacting with the database, reading and persisting data in Java has historically been quite complex.",[22,41240,41241],{},"You need to consider a lot of factors, like building JDBC URLs, managing database connections, dealing with real-world application concerns such as connection pools, etc. Fortunately, Spring came to our rescue and made these tasks more manageable through its abstractions. One of such is the JDBC template in Spring.",[22,41243,41244],{},"The JDBC template is a solid abstraction that simplified our interaction with databases. However, it has a few cons as it can get pretty verbose if you're building simple CRUD services based on resources. It demands a deeper understanding of all the methods you need to communicate with a database - factors like row mappers, column to field mapping and so on - which can be pretty tricky.",[22,41246,41247],{},"Despite these complexities, one main advantage is the complete control over SQL, which is highly appreciated amongst developers.",[26,41249,41251],{"id":41250},"introducing-the-new-jdbc-client","Introducing the New JDBC Client",[22,41253,41254],{},"This is where the newest kid on the block - the JDBC client, comes in. Among the things it brings to the table is a fluent API, which is a breeze to understand and read. You'll see this when we glance over the code.",[22,41256,41257],{},"One exciting feature with the JDBC client is that it's auto-configured for us in Spring Boot 3.2. This means we can simply ask for a bean in our application, and we get an instance of it, hassle-free!",[26,41259,41261],{"id":41260},"diving-deep-into-code","Diving Deep Into Code",[22,41263,41264],{},"Let's get to the fun part - the coding! Being a massive fan of the new JDBC client, I trust that you will love it too!",[22,41266,41267],{},[653,41268],{"alt":41269,"src":41270},"Post Controller","/images/blog/2023/09/11/post-controller.png",[636,41272,41274],{"id":41273},"step-1-creating-a-new-application","Step 1: Creating a New Application",[22,41276,41277,41278,41281],{},"For this demo, we'll create a brand-new application and set up database access to a local H2 database. For the starter, we are going to use the ",[677,41279,24606],{"href":2901,"rel":41280},[681]," which allows you to quickly prototype your application.",[22,41283,41284],{},"After generating the skeleton of your Maven project, import it in your favorite IDE or text editor.",[636,41286,41288],{"id":41287},"step-2-building-the-application","Step 2: Building the Application",[22,41290,22257,41291,41293,41294,976,41296,976,41298,976,41301,976,41304,41307],{},[59,41292,38700],{}," class, which represents a blog post in our application. It's a basic record with properties like ",[59,41295,8845],{},[59,41297,3196],{},[59,41299,41300],{},"slug",[59,41302,41303],{},"localDateTime",[59,41305,41306],{},"tags",", etc.",[52,41309,41311],{"className":54,"code":41310,"language":56,"meta":57,"style":57},"public record Post(String id, String title, String slug, LocalDate date, int timeToRead, String tags) {\n\n}\n",[59,41312,41313,41330,41334],{"__ignoreMap":57},[62,41314,41315,41317,41319,41322,41325,41327],{"class":64,"line":65},[62,41316,116],{"class":68},[62,41318,2996],{"class":68},[62,41320,41321],{"class":122}," Post",[62,41323,41324],{"class":72},"(String id, String title, String slug, LocalDate date, ",[62,41326,747],{"class":68},[62,41328,41329],{"class":72}," timeToRead, String tags) {\n",[62,41331,41332],{"class":64,"line":76},[62,41333,79],{"emptyLinePlaceholder":13},[62,41335,41336],{"class":64,"line":82},[62,41337,379],{"class":72},[22,41339,23213,41340,41343,41344,2755],{},[59,41341,41342],{},"PostController"," that will act as a REST controller. It will respond to a request mapping of \"api/posts\". For this controller, we'll wire in a ",[59,41345,41346],{},"PostService",[52,41348,41350],{"className":54,"code":41349,"language":56,"meta":57,"style":57},"@RestController\n@RequestMapping(\"/api/posts\")\npublic class PostController {\n\n private final PostService postService;\n\n public PostController(PostService postService) {\n this.postService = postService;\n }\n\n @GetMapping(\"\")\n List\u003CPost> findAll() {\n return postService.findAll();\n }\n\n @GetMapping(\"/{id}\")\n Optional\u003CPost> findById(@PathVariable String id) {\n return postService.findById(id);\n }\n\n @PostMapping\n @ResponseStatus(HttpStatus.CREATED)\n void create(@RequestBody Post post) {\n postService.create(post);\n }\n\n @PutMapping(\"/{id}\")\n void update(@RequestBody Post post, @PathVariable String id) {\n postService.update(post, id);\n }\n\n @DeleteMapping(\"/{id}\")\n void delete(@PathVariable String id) {\n postService.delete(id);\n }\n\n}\n",[59,41351,41352,41358,41371,41382,41386,41395,41399,41413,41425,41429,41433,41445,41457,41468,41472,41476,41489,41509,41519,41523,41527,41534,41544,41561,41570,41574,41578,41591,41615,41624,41628,41632,41644,41660,41669,41673,41677],{"__ignoreMap":57},[62,41353,41354,41356],{"class":64,"line":65},[62,41355,942],{"class":72},[62,41357,2342],{"class":68},[62,41359,41360,41362,41364,41366,41369],{"class":64,"line":76},[62,41361,942],{"class":72},[62,41363,10592],{"class":68},[62,41365,2109],{"class":72},[62,41367,41368],{"class":1675},"\"/api/posts\"",[62,41370,2212],{"class":72},[62,41372,41373,41375,41377,41380],{"class":64,"line":82},[62,41374,116],{"class":68},[62,41376,119],{"class":68},[62,41378,41379],{"class":122}," PostController",[62,41381,126],{"class":72},[62,41383,41384],{"class":64,"line":89},[62,41385,79],{"emptyLinePlaceholder":13},[62,41387,41388,41390,41392],{"class":64,"line":95},[62,41389,137],{"class":68},[62,41391,458],{"class":68},[62,41393,41394],{"class":72}," PostService postService;\n",[62,41396,41397],{"class":64,"line":101},[62,41398,79],{"emptyLinePlaceholder":13},[62,41400,41401,41403,41405,41408,41411],{"class":64,"line":107},[62,41402,194],{"class":68},[62,41404,41379],{"class":122},[62,41406,41407],{"class":72},"(PostService ",[62,41409,41410],{"class":889},"postService",[62,41412,768],{"class":72},[62,41414,41415,41417,41420,41422],{"class":64,"line":113},[62,41416,2405],{"class":149},[62,41418,41419],{"class":72},".postService ",[62,41421,146],{"class":68},[62,41423,41424],{"class":72}," postService;\n",[62,41426,41427],{"class":64,"line":129},[62,41428,223],{"class":72},[62,41430,41431],{"class":64,"line":134},[62,41432,79],{"emptyLinePlaceholder":13},[62,41434,41435,41437,41439,41441,41443],{"class":64,"line":156},[62,41436,2143],{"class":72},[62,41438,2548],{"class":68},[62,41440,2109],{"class":72},[62,41442,25895],{"class":1675},[62,41444,2212],{"class":72},[62,41446,41447,41449,41451,41453,41455],{"class":64,"line":161},[62,41448,4396],{"class":72},[62,41450,38700],{"class":68},[62,41452,3135],{"class":72},[62,41454,10287],{"class":122},[62,41456,206],{"class":72},[62,41458,41459,41461,41464,41466],{"class":64,"line":167},[62,41460,360],{"class":68},[62,41462,41463],{"class":72}," postService.",[62,41465,10287],{"class":122},[62,41467,822],{"class":72},[62,41469,41470],{"class":64,"line":173},[62,41471,223],{"class":72},[62,41473,41474],{"class":64,"line":179},[62,41475,79],{"emptyLinePlaceholder":13},[62,41477,41478,41480,41482,41484,41487],{"class":64,"line":185},[62,41479,2143],{"class":72},[62,41481,2548],{"class":68},[62,41483,2109],{"class":72},[62,41485,41486],{"class":1675},"\"/{id}\"",[62,41488,2212],{"class":72},[62,41490,41491,41493,41495,41497,41499,41501,41503,41505,41507],{"class":64,"line":191},[62,41492,39496],{"class":72},[62,41494,38700],{"class":68},[62,41496,3135],{"class":72},[62,41498,38763],{"class":122},[62,41500,2475],{"class":72},[62,41502,23740],{"class":68},[62,41504,2469],{"class":72},[62,41506,6283],{"class":889},[62,41508,768],{"class":72},[62,41510,41511,41513,41515,41517],{"class":64,"line":209},[62,41512,360],{"class":68},[62,41514,41463],{"class":72},[62,41516,38763],{"class":122},[62,41518,23764],{"class":72},[62,41520,41521],{"class":64,"line":220},[62,41522,223],{"class":72},[62,41524,41525],{"class":64,"line":226},[62,41526,79],{"emptyLinePlaceholder":13},[62,41528,41529,41531],{"class":64,"line":231},[62,41530,2143],{"class":72},[62,41532,41533],{"class":68},"PostMapping\n",[62,41535,41536,41538,41541],{"class":64,"line":236},[62,41537,2143],{"class":72},[62,41539,41540],{"class":68},"ResponseStatus",[62,41542,41543],{"class":72},"(HttpStatus.CREATED)\n",[62,41545,41546,41548,41550,41552,41554,41557,41559],{"class":64,"line":242},[62,41547,11710],{"class":68},[62,41549,23301],{"class":122},[62,41551,2475],{"class":72},[62,41553,2478],{"class":68},[62,41555,41556],{"class":72}," Post ",[62,41558,38838],{"class":889},[62,41560,768],{"class":72},[62,41562,41563,41566,41568],{"class":64,"line":247},[62,41564,41565],{"class":72}," postService.",[62,41567,14348],{"class":122},[62,41569,5777],{"class":72},[62,41571,41572],{"class":64,"line":252},[62,41573,223],{"class":72},[62,41575,41576],{"class":64,"line":257},[62,41577,79],{"emptyLinePlaceholder":13},[62,41579,41580,41582,41585,41587,41589],{"class":64,"line":271},[62,41581,2143],{"class":72},[62,41583,41584],{"class":68},"PutMapping",[62,41586,2109],{"class":72},[62,41588,41486],{"class":1675},[62,41590,2212],{"class":72},[62,41592,41593,41595,41597,41599,41601,41603,41605,41607,41609,41611,41613],{"class":64,"line":281},[62,41594,11710],{"class":68},[62,41596,38909],{"class":122},[62,41598,2475],{"class":72},[62,41600,2478],{"class":68},[62,41602,41556],{"class":72},[62,41604,38838],{"class":889},[62,41606,39583],{"class":72},[62,41608,23740],{"class":68},[62,41610,2469],{"class":72},[62,41612,6283],{"class":889},[62,41614,768],{"class":72},[62,41616,41617,41619,41621],{"class":64,"line":286},[62,41618,41565],{"class":72},[62,41620,1338],{"class":122},[62,41622,41623],{"class":72},"(post, id);\n",[62,41625,41626],{"class":64,"line":291},[62,41627,223],{"class":72},[62,41629,41630],{"class":64,"line":296},[62,41631,79],{"emptyLinePlaceholder":13},[62,41633,41634,41636,41638,41640,41642],{"class":64,"line":302},[62,41635,2143],{"class":72},[62,41637,23719],{"class":68},[62,41639,2109],{"class":72},[62,41641,41486],{"class":1675},[62,41643,2212],{"class":72},[62,41645,41646,41648,41650,41652,41654,41656,41658],{"class":64,"line":308},[62,41647,11710],{"class":68},[62,41649,38982],{"class":122},[62,41651,2475],{"class":72},[62,41653,23740],{"class":68},[62,41655,2469],{"class":72},[62,41657,6283],{"class":889},[62,41659,768],{"class":72},[62,41661,41662,41664,41667],{"class":64,"line":314},[62,41663,41565],{"class":72},[62,41665,41666],{"class":122},"delete",[62,41668,23764],{"class":72},[62,41670,41671],{"class":64,"line":320},[62,41672,223],{"class":72},[62,41674,41675],{"class":64,"line":326},[62,41676,79],{"emptyLinePlaceholder":13},[62,41678,41679],{"class":64,"line":338},[62,41680,379],{"class":72},[22,41682,41683,41684,41686,41687,976,41689,976,41691,976,41693,4201,41695,2755],{},"Crucially, it's important to provide implementations for the ",[59,41685,41346],{}," concerning the two types of JDBC that we're discussing. For this, it involves creating an interface and then defining several methods like ",[59,41688,10287],{},[59,41690,38763],{},[59,41692,14348],{},[59,41694,1338],{},[59,41696,41666],{},[52,41698,41700],{"className":54,"code":41699,"language":56,"meta":57,"style":57},"public interface PostService {\n\n List\u003CPost> findAll();\n\n Optional\u003CPost> findById(String id);\n\n void create(Post post);\n\n void update(Post post, String id);\n\n void delete(String id);\n\n}\n",[59,41701,41702,41712,41716,41728,41732,41748,41752,41764,41768,41784,41788,41800,41804],{"__ignoreMap":57},[62,41703,41704,41706,41708,41710],{"class":64,"line":65},[62,41705,116],{"class":68},[62,41707,8531],{"class":68},[62,41709,39116],{"class":122},[62,41711,126],{"class":72},[62,41713,41714],{"class":64,"line":76},[62,41715,79],{"emptyLinePlaceholder":13},[62,41717,41718,41720,41722,41724,41726],{"class":64,"line":82},[62,41719,4396],{"class":72},[62,41721,38700],{"class":68},[62,41723,3135],{"class":72},[62,41725,10287],{"class":122},[62,41727,822],{"class":72},[62,41729,41730],{"class":64,"line":89},[62,41731,79],{"emptyLinePlaceholder":13},[62,41733,41734,41736,41738,41740,41742,41744,41746],{"class":64,"line":95},[62,41735,39496],{"class":72},[62,41737,38700],{"class":68},[62,41739,3135],{"class":72},[62,41741,38763],{"class":122},[62,41743,1049],{"class":72},[62,41745,6283],{"class":889},[62,41747,1133],{"class":72},[62,41749,41750],{"class":64,"line":101},[62,41751,79],{"emptyLinePlaceholder":13},[62,41753,41754,41756,41758,41760,41762],{"class":64,"line":107},[62,41755,11710],{"class":68},[62,41757,23301],{"class":122},[62,41759,38835],{"class":72},[62,41761,38838],{"class":889},[62,41763,1133],{"class":72},[62,41765,41766],{"class":64,"line":113},[62,41767,79],{"emptyLinePlaceholder":13},[62,41769,41770,41772,41774,41776,41778,41780,41782],{"class":64,"line":129},[62,41771,11710],{"class":68},[62,41773,38909],{"class":122},[62,41775,38835],{"class":72},[62,41777,38838],{"class":889},[62,41779,8624],{"class":72},[62,41781,6283],{"class":889},[62,41783,1133],{"class":72},[62,41785,41786],{"class":64,"line":134},[62,41787,79],{"emptyLinePlaceholder":13},[62,41789,41790,41792,41794,41796,41798],{"class":64,"line":156},[62,41791,11710],{"class":68},[62,41793,38982],{"class":122},[62,41795,1049],{"class":72},[62,41797,6283],{"class":889},[62,41799,1133],{"class":72},[62,41801,41802],{"class":64,"line":161},[62,41803,79],{"emptyLinePlaceholder":13},[62,41805,41806],{"class":64,"line":167},[62,41807,379],{"class":72},[636,41809,41811],{"id":41810},"step-3-connecting-to-the-database","Step 3: Connecting to the Database",[22,41813,41814,41815,41818,41819,41821],{},"Before we progress, we need to ensure that we can connect to a database. For that, we'll create a ",[59,41816,41817],{},"schema.sql"," file that will define our database table structure, which should correspond to the ",[59,41820,38700],{}," record we've created.",[52,41823,41826],{"className":41824,"code":41825,"language":38716,"meta":57,"style":57},"language-sql shiki shiki-themes github-light github-dark github-light","DROP TABLE IF EXISTS Post;\n\nCREATE TABLE Post (\n id varchar(255) NOT NULL,\n title varchar(255) NOT NULL,\n slug varchar(255) NOT NULL,\n date date NOT NULL,\n time_to_read int NOT NULL,\n tags varchar(255),\n PRIMARY KEY (id)\n);\n",[59,41827,41828,41845,41849,41860,41880,41897,41914,41927,41938,41951,41959],{"__ignoreMap":57},[62,41829,41830,41833,41836,41839,41842],{"class":64,"line":65},[62,41831,41832],{"class":68},"DROP",[62,41834,41835],{"class":68}," TABLE",[62,41837,41838],{"class":68}," IF",[62,41840,41841],{"class":68}," EXISTS",[62,41843,41844],{"class":72}," Post;\n",[62,41846,41847],{"class":64,"line":76},[62,41848,79],{"emptyLinePlaceholder":13},[62,41850,41851,41854,41856,41858],{"class":64,"line":82},[62,41852,41853],{"class":68},"CREATE",[62,41855,41835],{"class":68},[62,41857,41321],{"class":122},[62,41859,36931],{"class":72},[62,41861,41862,41865,41868,41870,41873,41875,41878],{"class":64,"line":89},[62,41863,41864],{"class":72}," id ",[62,41866,41867],{"class":68},"varchar",[62,41869,2109],{"class":72},[62,41871,41872],{"class":149},"255",[62,41874,5024],{"class":72},[62,41876,41877],{"class":68},"NOT NULL",[62,41879,3338],{"class":72},[62,41881,41882,41885,41887,41889,41891,41893,41895],{"class":64,"line":95},[62,41883,41884],{"class":72}," title ",[62,41886,41867],{"class":68},[62,41888,2109],{"class":72},[62,41890,41872],{"class":149},[62,41892,5024],{"class":72},[62,41894,41877],{"class":68},[62,41896,3338],{"class":72},[62,41898,41899,41902,41904,41906,41908,41910,41912],{"class":64,"line":101},[62,41900,41901],{"class":72}," slug ",[62,41903,41867],{"class":68},[62,41905,2109],{"class":72},[62,41907,41872],{"class":149},[62,41909,5024],{"class":72},[62,41911,41877],{"class":68},[62,41913,3338],{"class":72},[62,41915,41916,41919,41922,41925],{"class":64,"line":107},[62,41917,41918],{"class":68}," date",[62,41920,41921],{"class":68}," date",[62,41923,41924],{"class":68}," NOT NULL",[62,41926,3338],{"class":72},[62,41928,41929,41932,41934,41936],{"class":64,"line":113},[62,41930,41931],{"class":72}," time_to_read ",[62,41933,747],{"class":68},[62,41935,41924],{"class":68},[62,41937,3338],{"class":72},[62,41939,41940,41943,41945,41947,41949],{"class":64,"line":129},[62,41941,41942],{"class":72}," tags ",[62,41944,41867],{"class":68},[62,41946,2109],{"class":72},[62,41948,41872],{"class":149},[62,41950,3324],{"class":72},[62,41952,41953,41956],{"class":64,"line":134},[62,41954,41955],{"class":68}," PRIMARY KEY",[62,41957,41958],{"class":72}," (id)\n",[62,41960,41961],{"class":64,"line":156},[62,41962,1133],{"class":72},[22,41964,41965,41966,41968],{},"You will also need to update ",[59,41967,1265],{}," to include the database connection details.",[52,41970,41972],{"className":1269,"code":41971,"language":1271,"meta":57,"style":57},"spring.datasource.generate-unique-name=false\nspring.datasource.name=blog\nspring.h2.console.enabled=true\n",[59,41973,41974,41981,41989],{"__ignoreMap":57},[62,41975,41976,41979],{"class":64,"line":65},[62,41977,41978],{"class":68},"spring.datasource.generate-unique-name",[62,41980,14152],{"class":72},[62,41982,41983,41986],{"class":64,"line":76},[62,41984,41985],{"class":68},"spring.datasource.name",[62,41987,41988],{"class":72},"=blog\n",[62,41990,41991,41994],{"class":64,"line":82},[62,41992,41993],{"class":68},"spring.h2.console.enabled",[62,41995,1281],{"class":72},[22,41997,41998],{},"Remember, since we are not utilizing something like JPA, we must create our table. With all these in place, our application should be ready to launch.",[636,42000,42002],{"id":42001},"step-4-implementing-the-jdbc-template","Step 4: Implementing the JDBC template",[22,42004,15404,42005,42008,42009,42011],{},[59,42006,42007],{},"TemplatePostService"," class. The task here is to implement all the methods that we previously defined in the ",[59,42010,41346],{}," interface.",[22,42013,42014,42015,42017],{},"For each method, we'll employ the JDBC template to write SQL queries. On a general note, each query method in the JDBC template will necessitate a row mapper to map columns in the database table to the ",[59,42016,38700],{}," record, which can be quite tasking.",[52,42019,42021],{"className":54,"code":42020,"language":56,"meta":57,"style":57},"@Service\npublic class TemplatePostService implements PostService {\n\n private static final Logger log = LoggerFactory.getLogger(TemplatePostService.class);\n private final JdbcTemplate jdbcTemplate;\n\n public TemplatePostService(JdbcTemplate jdbcTemplate) {\n this.jdbcTemplate = jdbcTemplate;\n }\n\n RowMapper\u003CPost> rowMapper = (rs, rowNum) -> new Post(\n rs.getString(\"id\"),\n rs.getString(\"title\"),\n rs.getString(\"slug\"),\n rs.getDate(\"date\").toLocalDate(),\n rs.getInt(\"time_to_read\"),\n rs.getString(\"tags\")\n );\n\n @Override\n public List\u003CPost> findAll() {\n var sql = \"SELECT id,title,slug,date,time_to_read,tags FROM post\";\n return jdbcTemplate.query(sql, rowMapper);\n }\n\n @Override\n public Optional\u003CPost> findById(String id) {\n var sql = \"SELECT id,title,slug,date,time_to_read,tags FROM post WHERE id = ?\";\n Post post = null;\n try {\n post = jdbcTemplate.queryForObject(sql,rowMapper,id);\n } catch (DataAccessException ex) {\n log.info(\"Post not found: \" + id);\n }\n\n return Optional.ofNullable(post);\n }\n\n @Override\n public void create(Post post) {\n String sql = \"INSERT INTO post(id,title,slug,date,time_to_read,tags) values(?,?,?,?,?,?)\";\n int insert = jdbcTemplate.update(sql,post.id(),post.title(),post.slug(),post.date(),post.timeToRead(),post.tags());\n if(insert == 1) {\n log.info(\"New Post Created: \" + post.title());\n }\n }\n\n @Override\n public void update(Post post, String id) {\n String sql = \"update post set title = ?, slug = ?, date = ?, time_to_read = ?, tags = ? where id = ?\";\n int update = jdbcTemplate.update(sql,post.title(),post.slug(),post.date(),post.timeToRead(),post.tags(),id);\n if(update == 1) {\n log.info(\"Post Updated: \" + post.title());\n }\n }\n\n @Override\n public void delete(String id) {\n String sql = \"delete from post where id = ?\";\n int delete = jdbcTemplate.update(sql,id);\n if(delete == 1) {\n log.info(\"Post Deleted: \" + id);\n }\n }\n}\n",[59,42022,42023,42029,42044,42048,42067,42076,42080,42094,42106,42110,42114,42137,42151,42164,42177,42196,42210,42223,42227,42231,42237,42251,42265,42277,42281,42285,42291,42309,42322,42333,42339,42354,42368,42384,42388,42392,42404,42408,42412,42418,42432,42444,42487,42500,42519,42523,42527,42531,42537,42555,42566,42602,42615,42634,42638,42642,42646,42652,42666,42677,42693,42706,42721,42725,42729],{"__ignoreMap":57},[62,42024,42025,42027],{"class":64,"line":65},[62,42026,942],{"class":72},[62,42028,945],{"class":68},[62,42030,42031,42033,42035,42038,42040,42042],{"class":64,"line":76},[62,42032,116],{"class":68},[62,42034,119],{"class":68},[62,42036,42037],{"class":122}," TemplatePostService",[62,42039,13520],{"class":68},[62,42041,39116],{"class":122},[62,42043,126],{"class":72},[62,42045,42046],{"class":64,"line":82},[62,42047,79],{"emptyLinePlaceholder":13},[62,42049,42050,42052,42054,42056,42058,42060,42062,42064],{"class":64,"line":89},[62,42051,137],{"class":68},[62,42053,2101],{"class":68},[62,42055,458],{"class":68},[62,42057,3061],{"class":72},[62,42059,146],{"class":68},[62,42061,3066],{"class":72},[62,42063,3069],{"class":122},[62,42065,42066],{"class":72},"(TemplatePostService.class);\n",[62,42068,42069,42071,42073],{"class":64,"line":95},[62,42070,137],{"class":68},[62,42072,458],{"class":68},[62,42074,42075],{"class":72}," JdbcTemplate jdbcTemplate;\n",[62,42077,42078],{"class":64,"line":101},[62,42079,79],{"emptyLinePlaceholder":13},[62,42081,42082,42084,42086,42089,42092],{"class":64,"line":107},[62,42083,194],{"class":68},[62,42085,42037],{"class":122},[62,42087,42088],{"class":72},"(JdbcTemplate ",[62,42090,42091],{"class":889},"jdbcTemplate",[62,42093,768],{"class":72},[62,42095,42096,42098,42101,42103],{"class":64,"line":113},[62,42097,2405],{"class":149},[62,42099,42100],{"class":72},".jdbcTemplate ",[62,42102,146],{"class":68},[62,42104,42105],{"class":72}," jdbcTemplate;\n",[62,42107,42108],{"class":64,"line":129},[62,42109,223],{"class":72},[62,42111,42112],{"class":64,"line":134},[62,42113,79],{"emptyLinePlaceholder":13},[62,42115,42116,42119,42121,42124,42126,42129,42131,42133,42135],{"class":64,"line":156},[62,42117,42118],{"class":72}," RowMapper\u003C",[62,42120,38700],{"class":68},[62,42122,42123],{"class":72},"> rowMapper ",[62,42125,146],{"class":68},[62,42127,42128],{"class":72}," (rs, rowNum) ",[62,42130,800],{"class":68},[62,42132,466],{"class":68},[62,42134,41321],{"class":122},[62,42136,3301],{"class":72},[62,42138,42139,42142,42145,42147,42149],{"class":64,"line":161},[62,42140,42141],{"class":72}," rs.",[62,42143,42144],{"class":122},"getString",[62,42146,2109],{"class":72},[62,42148,38796],{"class":1675},[62,42150,3324],{"class":72},[62,42152,42153,42155,42157,42159,42162],{"class":64,"line":167},[62,42154,42141],{"class":72},[62,42156,42144],{"class":122},[62,42158,2109],{"class":72},[62,42160,42161],{"class":1675},"\"title\"",[62,42163,3324],{"class":72},[62,42165,42166,42168,42170,42172,42175],{"class":64,"line":173},[62,42167,42141],{"class":72},[62,42169,42144],{"class":122},[62,42171,2109],{"class":72},[62,42173,42174],{"class":1675},"\"slug\"",[62,42176,3324],{"class":72},[62,42178,42179,42181,42184,42186,42189,42191,42194],{"class":64,"line":179},[62,42180,42141],{"class":72},[62,42182,42183],{"class":122},"getDate",[62,42185,2109],{"class":72},[62,42187,42188],{"class":1675},"\"date\"",[62,42190,15503],{"class":72},[62,42192,42193],{"class":122},"toLocalDate",[62,42195,4651],{"class":72},[62,42197,42198,42200,42203,42205,42208],{"class":64,"line":185},[62,42199,42141],{"class":72},[62,42201,42202],{"class":122},"getInt",[62,42204,2109],{"class":72},[62,42206,42207],{"class":1675},"\"time_to_read\"",[62,42209,3324],{"class":72},[62,42211,42212,42214,42216,42218,42221],{"class":64,"line":191},[62,42213,42141],{"class":72},[62,42215,42144],{"class":122},[62,42217,2109],{"class":72},[62,42219,42220],{"class":1675},"\"tags\"",[62,42222,2212],{"class":72},[62,42224,42225],{"class":64,"line":209},[62,42226,5969],{"class":72},[62,42228,42229],{"class":64,"line":220},[62,42230,79],{"emptyLinePlaceholder":13},[62,42232,42233,42235],{"class":64,"line":226},[62,42234,2143],{"class":72},[62,42236,13555],{"class":68},[62,42238,42239,42241,42243,42245,42247,42249],{"class":64,"line":231},[62,42240,194],{"class":68},[62,42242,3079],{"class":72},[62,42244,38700],{"class":68},[62,42246,3135],{"class":72},[62,42248,10287],{"class":122},[62,42250,206],{"class":72},[62,42252,42253,42255,42258,42260,42263],{"class":64,"line":236},[62,42254,13605],{"class":68},[62,42256,42257],{"class":72}," sql ",[62,42259,146],{"class":68},[62,42261,42262],{"class":1675}," \"SELECT id,title,slug,date,time_to_read,tags FROM post\"",[62,42264,153],{"class":72},[62,42266,42267,42269,42272,42274],{"class":64,"line":242},[62,42268,360],{"class":68},[62,42270,42271],{"class":72}," jdbcTemplate.",[62,42273,9155],{"class":122},[62,42275,42276],{"class":72},"(sql, rowMapper);\n",[62,42278,42279],{"class":64,"line":247},[62,42280,223],{"class":72},[62,42282,42283],{"class":64,"line":252},[62,42284,79],{"emptyLinePlaceholder":13},[62,42286,42287,42289],{"class":64,"line":257},[62,42288,2143],{"class":72},[62,42290,13555],{"class":68},[62,42292,42293,42295,42297,42299,42301,42303,42305,42307],{"class":64,"line":271},[62,42294,194],{"class":68},[62,42296,38756],{"class":72},[62,42298,38700],{"class":68},[62,42300,3135],{"class":72},[62,42302,38763],{"class":122},[62,42304,1049],{"class":72},[62,42306,6283],{"class":889},[62,42308,768],{"class":72},[62,42310,42311,42313,42315,42317,42320],{"class":64,"line":281},[62,42312,13605],{"class":68},[62,42314,42257],{"class":72},[62,42316,146],{"class":68},[62,42318,42319],{"class":1675}," \"SELECT id,title,slug,date,time_to_read,tags FROM post WHERE id = ?\"",[62,42321,153],{"class":72},[62,42323,42324,42327,42329,42331],{"class":64,"line":286},[62,42325,42326],{"class":72}," Post post ",[62,42328,146],{"class":68},[62,42330,13324],{"class":149},[62,42332,153],{"class":72},[62,42334,42335,42337],{"class":64,"line":291},[62,42336,807],{"class":68},[62,42338,126],{"class":72},[62,42340,42341,42344,42346,42348,42351],{"class":64,"line":296},[62,42342,42343],{"class":72}," post ",[62,42345,146],{"class":68},[62,42347,42271],{"class":72},[62,42349,42350],{"class":122},"queryForObject",[62,42352,42353],{"class":72},"(sql,rowMapper,id);\n",[62,42355,42356,42358,42360,42363,42366],{"class":64,"line":302},[62,42357,880],{"class":72},[62,42359,883],{"class":68},[62,42361,42362],{"class":72}," (DataAccessException ",[62,42364,42365],{"class":889},"ex",[62,42367,768],{"class":72},[62,42369,42370,42372,42374,42376,42379,42381],{"class":64,"line":308},[62,42371,13341],{"class":72},[62,42373,12688],{"class":122},[62,42375,2109],{"class":72},[62,42377,42378],{"class":1675},"\"Post not found: \"",[62,42380,4507],{"class":68},[62,42382,42383],{"class":72}," id);\n",[62,42385,42386],{"class":64,"line":314},[62,42387,533],{"class":72},[62,42389,42390],{"class":64,"line":320},[62,42391,79],{"emptyLinePlaceholder":13},[62,42393,42394,42396,42399,42402],{"class":64,"line":326},[62,42395,360],{"class":68},[62,42397,42398],{"class":72}," Optional.",[62,42400,42401],{"class":122},"ofNullable",[62,42403,5777],{"class":72},[62,42405,42406],{"class":64,"line":338},[62,42407,223],{"class":72},[62,42409,42410],{"class":64,"line":343},[62,42411,79],{"emptyLinePlaceholder":13},[62,42413,42414,42416],{"class":64,"line":357},[62,42415,2143],{"class":72},[62,42417,13555],{"class":68},[62,42419,42420,42422,42424,42426,42428,42430],{"class":64,"line":366},[62,42421,194],{"class":68},[62,42423,200],{"class":68},[62,42425,23301],{"class":122},[62,42427,38835],{"class":72},[62,42429,38838],{"class":889},[62,42431,768],{"class":72},[62,42433,42434,42437,42439,42442],{"class":64,"line":371},[62,42435,42436],{"class":72}," String sql ",[62,42438,146],{"class":68},[62,42440,42441],{"class":1675}," \"INSERT INTO post(id,title,slug,date,time_to_read,tags) values(?,?,?,?,?,?)\"",[62,42443,153],{"class":72},[62,42445,42446,42448,42451,42453,42455,42457,42460,42462,42465,42467,42469,42471,42473,42476,42478,42481,42483,42485],{"class":64,"line":376},[62,42447,5681],{"class":68},[62,42449,42450],{"class":72}," insert ",[62,42452,146],{"class":68},[62,42454,42271],{"class":72},[62,42456,1338],{"class":122},[62,42458,42459],{"class":72},"(sql,post.",[62,42461,6283],{"class":122},[62,42463,42464],{"class":72},"(),post.",[62,42466,3196],{"class":122},[62,42468,42464],{"class":72},[62,42470,41300],{"class":122},[62,42472,42464],{"class":72},[62,42474,42475],{"class":122},"date",[62,42477,42464],{"class":72},[62,42479,42480],{"class":122},"timeToRead",[62,42482,42464],{"class":72},[62,42484,41306],{"class":122},[62,42486,1091],{"class":72},[62,42488,42489,42491,42494,42496,42498],{"class":64,"line":16333},[62,42490,12741],{"class":68},[62,42492,42493],{"class":72},"(insert ",[62,42495,28328],{"class":68},[62,42497,22038],{"class":149},[62,42499,768],{"class":72},[62,42501,42502,42504,42506,42508,42511,42513,42515,42517],{"class":64,"line":16349},[62,42503,13341],{"class":72},[62,42505,12688],{"class":122},[62,42507,2109],{"class":72},[62,42509,42510],{"class":1675},"\"New Post Created: \"",[62,42512,4507],{"class":68},[62,42514,5105],{"class":72},[62,42516,3196],{"class":122},[62,42518,1091],{"class":72},[62,42520,42521],{"class":64,"line":16365},[62,42522,533],{"class":72},[62,42524,42525],{"class":64,"line":16381},[62,42526,223],{"class":72},[62,42528,42529],{"class":64,"line":16402},[62,42530,79],{"emptyLinePlaceholder":13},[62,42532,42533,42535],{"class":64,"line":16411},[62,42534,2143],{"class":72},[62,42536,13555],{"class":68},[62,42538,42539,42541,42543,42545,42547,42549,42551,42553],{"class":64,"line":16427},[62,42540,194],{"class":68},[62,42542,200],{"class":68},[62,42544,38909],{"class":122},[62,42546,38835],{"class":72},[62,42548,38838],{"class":889},[62,42550,8624],{"class":72},[62,42552,6283],{"class":889},[62,42554,768],{"class":72},[62,42556,42557,42559,42561,42564],{"class":64,"line":16448},[62,42558,42436],{"class":72},[62,42560,146],{"class":68},[62,42562,42563],{"class":1675}," \"update post set title = ?, slug = ?, date = ?, time_to_read = ?, tags = ? where id = ?\"",[62,42565,153],{"class":72},[62,42567,42568,42570,42573,42575,42577,42579,42581,42583,42585,42587,42589,42591,42593,42595,42597,42599],{"class":64,"line":16457},[62,42569,5681],{"class":68},[62,42571,42572],{"class":72}," update ",[62,42574,146],{"class":68},[62,42576,42271],{"class":72},[62,42578,1338],{"class":122},[62,42580,42459],{"class":72},[62,42582,3196],{"class":122},[62,42584,42464],{"class":72},[62,42586,41300],{"class":122},[62,42588,42464],{"class":72},[62,42590,42475],{"class":122},[62,42592,42464],{"class":72},[62,42594,42480],{"class":122},[62,42596,42464],{"class":72},[62,42598,41306],{"class":122},[62,42600,42601],{"class":72},"(),id);\n",[62,42603,42604,42606,42609,42611,42613],{"class":64,"line":16466},[62,42605,12741],{"class":68},[62,42607,42608],{"class":72},"(update ",[62,42610,28328],{"class":68},[62,42612,22038],{"class":149},[62,42614,768],{"class":72},[62,42616,42617,42619,42621,42623,42626,42628,42630,42632],{"class":64,"line":16471},[62,42618,13341],{"class":72},[62,42620,12688],{"class":122},[62,42622,2109],{"class":72},[62,42624,42625],{"class":1675},"\"Post Updated: \"",[62,42627,4507],{"class":68},[62,42629,5105],{"class":72},[62,42631,3196],{"class":122},[62,42633,1091],{"class":72},[62,42635,42636],{"class":64,"line":16487},[62,42637,533],{"class":72},[62,42639,42640],{"class":64,"line":16504},[62,42641,223],{"class":72},[62,42643,42644],{"class":64,"line":16517},[62,42645,79],{"emptyLinePlaceholder":13},[62,42647,42648,42650],{"class":64,"line":16523},[62,42649,2143],{"class":72},[62,42651,13555],{"class":68},[62,42653,42654,42656,42658,42660,42662,42664],{"class":64,"line":16532},[62,42655,194],{"class":68},[62,42657,200],{"class":68},[62,42659,38982],{"class":122},[62,42661,1049],{"class":72},[62,42663,6283],{"class":889},[62,42665,768],{"class":72},[62,42667,42668,42670,42672,42675],{"class":64,"line":16546},[62,42669,42436],{"class":72},[62,42671,146],{"class":68},[62,42673,42674],{"class":1675}," \"delete from post where id = ?\"",[62,42676,153],{"class":72},[62,42678,42679,42681,42684,42686,42688,42690],{"class":64,"line":16557},[62,42680,5681],{"class":68},[62,42682,42683],{"class":72}," delete ",[62,42685,146],{"class":68},[62,42687,42271],{"class":72},[62,42689,1338],{"class":122},[62,42691,42692],{"class":72},"(sql,id);\n",[62,42694,42695,42697,42700,42702,42704],{"class":64,"line":16563},[62,42696,12741],{"class":68},[62,42698,42699],{"class":72},"(delete ",[62,42701,28328],{"class":68},[62,42703,22038],{"class":149},[62,42705,768],{"class":72},[62,42707,42708,42710,42712,42714,42717,42719],{"class":64,"line":16572},[62,42709,13341],{"class":72},[62,42711,12688],{"class":122},[62,42713,2109],{"class":72},[62,42715,42716],{"class":1675},"\"Post Deleted: \"",[62,42718,4507],{"class":68},[62,42720,42383],{"class":72},[62,42722,42723],{"class":64,"line":16581},[62,42724,533],{"class":72},[62,42726,42727],{"class":64,"line":16590},[62,42728,223],{"class":72},[62,42730,42731],{"class":64,"line":16599},[62,42732,379],{"class":72},[22,42734,42735],{},"This is just to show off an example of how to use the JDBC Template so you can compare it to the JDBC Client.",[636,42737,42739],{"id":42738},"step-5-using-the-jdbc-client","Step 5: Using the JDBC Client",[22,42741,42742,42743,42745,42746,42749,42750,42752],{},"Now, we're going to simplify things. We've seen the ",[59,42744,42007],{},"; now it's time to bring in the sleek ",[59,42747,42748],{},"ClientPostService",". This class implements the ",[59,42751,41346],{}," interface, with similar methods to those we have already discussed.",[22,42754,42755,42756,42758],{},"We'll start by obtaining an instance of the JDBC client. Just like the JDBC template for ",[59,42757,42007],{},", the auto-configured JDBC client of Spring Boot 3.2 is handed over to us.",[52,42760,42762],{"className":54,"code":42761,"language":56,"meta":57,"style":57},"@Service\npublic class ClientPostService implements PostService {\n\n private final JdbcClient jdbcClient;\n\n public ClientPostService(JdbcClient jdbcClient) {\n this.jdbcClient = jdbcClient;\n }\n\n @Override\n public List\u003CPost> findAll() {\n return jdbcClient.sql(\"SELECT id,title,slug,date,time_to_read,tags FROM post\")\n .query(Post.class)\n .list();\n }\n\n @Override\n public Optional\u003CPost> findById(String id) {\n return jdbcClient.sql(\"SELECT id,title,slug,date,time_to_read,tags FROM post WHERE id = :id\")\n .param(\"id\", id)\n .query(Post.class)\n .optional();\n }\n\n @Override\n public void create(Post post) {\n int update = jdbcClient.sql(\"INSERT INTO post(id,title,slug,date,time_to_read,tags) values(?,?,?,?,?,?)\")\n .params(List.of(post.id(), post.title(), post.slug(), post.date(), post.timeToRead(), post.tags()))\n .update();\n\n Assert.state(update == 1, \"Failed to create post \" + post.title());\n }\n\n @Override\n public void update(Post post, String id) {\n var updated = jdbcClient.sql(\"update post set title = ?, slug = ?, date = ?, time_to_read = ?, tags = ? where id = ?\")\n .params(List.of(post.title(), post.slug(), post.date(), post.timeToRead(), post.tags(), id))\n .update();\n\n Assert.state(updated == 1, \"Failed to update post \" + post.title());\n }\n\n @Override\n public void delete(String id) {\n var updated = jdbcClient.sql(\"delete from post where id = :id\")\n .param(\"id\", id)\n .update();\n\n Assert.state(updated == 1, \"Failed to delete post \" + id);\n }\n\n}\n",[59,42763,42764,42770,42785,42789,42798,42802,42814,42824,42828,42832,42839,42853,42868,42877,42885,42889,42893,42899,42917,42932,42944,42952,42960,42964,42968,42974,42988,43007,43043,43051,43055,43081,43085,43089,43095,43113,43134,43166,43174,43178,43204,43208,43212,43218,43232,43251,43263,43271,43275,43296,43300,43304],{"__ignoreMap":57},[62,42765,42766,42768],{"class":64,"line":65},[62,42767,942],{"class":72},[62,42769,945],{"class":68},[62,42771,42772,42774,42776,42779,42781,42783],{"class":64,"line":76},[62,42773,116],{"class":68},[62,42775,119],{"class":68},[62,42777,42778],{"class":122}," ClientPostService",[62,42780,13520],{"class":68},[62,42782,39116],{"class":122},[62,42784,126],{"class":72},[62,42786,42787],{"class":64,"line":82},[62,42788,79],{"emptyLinePlaceholder":13},[62,42790,42791,42794,42796],{"class":64,"line":89},[62,42792,42793],{"class":68}," private",[62,42795,458],{"class":68},[62,42797,38653],{"class":72},[62,42799,42800],{"class":64,"line":95},[62,42801,79],{"emptyLinePlaceholder":13},[62,42803,42804,42806,42808,42810,42812],{"class":64,"line":101},[62,42805,26643],{"class":68},[62,42807,42778],{"class":122},[62,42809,38666],{"class":72},[62,42811,38669],{"class":889},[62,42813,768],{"class":72},[62,42815,42816,42818,42820,42822],{"class":64,"line":107},[62,42817,39124],{"class":149},[62,42819,38678],{"class":72},[62,42821,146],{"class":68},[62,42823,38683],{"class":72},[62,42825,42826],{"class":64,"line":113},[62,42827,3731],{"class":72},[62,42829,42830],{"class":64,"line":129},[62,42831,79],{"emptyLinePlaceholder":13},[62,42833,42834,42837],{"class":64,"line":134},[62,42835,42836],{"class":72}," @",[62,42838,13555],{"class":68},[62,42840,42841,42843,42845,42847,42849,42851],{"class":64,"line":156},[62,42842,26643],{"class":68},[62,42844,3079],{"class":72},[62,42846,38700],{"class":68},[62,42848,3135],{"class":72},[62,42850,10287],{"class":122},[62,42852,206],{"class":72},[62,42854,42855,42857,42859,42861,42863,42866],{"class":64,"line":161},[62,42856,2599],{"class":68},[62,42858,38713],{"class":72},[62,42860,38716],{"class":122},[62,42862,2109],{"class":72},[62,42864,42865],{"class":1675},"\"SELECT id,title,slug,date,time_to_read,tags FROM post\"",[62,42867,2212],{"class":72},[62,42869,42870,42873,42875],{"class":64,"line":167},[62,42871,42872],{"class":72}," .",[62,42874,9155],{"class":122},[62,42876,38732],{"class":72},[62,42878,42879,42881,42883],{"class":64,"line":173},[62,42880,42872],{"class":72},[62,42882,38739],{"class":122},[62,42884,822],{"class":72},[62,42886,42887],{"class":64,"line":179},[62,42888,3731],{"class":72},[62,42890,42891],{"class":64,"line":185},[62,42892,79],{"emptyLinePlaceholder":13},[62,42894,42895,42897],{"class":64,"line":191},[62,42896,42836],{"class":72},[62,42898,13555],{"class":68},[62,42900,42901,42903,42905,42907,42909,42911,42913,42915],{"class":64,"line":209},[62,42902,26643],{"class":68},[62,42904,38756],{"class":72},[62,42906,38700],{"class":68},[62,42908,3135],{"class":72},[62,42910,38763],{"class":122},[62,42912,1049],{"class":72},[62,42914,6283],{"class":889},[62,42916,768],{"class":72},[62,42918,42919,42921,42923,42925,42927,42930],{"class":64,"line":220},[62,42920,2599],{"class":68},[62,42922,38713],{"class":72},[62,42924,38716],{"class":122},[62,42926,2109],{"class":72},[62,42928,42929],{"class":1675},"\"SELECT id,title,slug,date,time_to_read,tags FROM post WHERE id = :id\"",[62,42931,2212],{"class":72},[62,42933,42934,42936,42938,42940,42942],{"class":64,"line":226},[62,42935,42872],{"class":72},[62,42937,38791],{"class":122},[62,42939,2109],{"class":72},[62,42941,38796],{"class":1675},[62,42943,38799],{"class":72},[62,42945,42946,42948,42950],{"class":64,"line":231},[62,42947,42872],{"class":72},[62,42949,9155],{"class":122},[62,42951,38732],{"class":72},[62,42953,42954,42956,42958],{"class":64,"line":236},[62,42955,42872],{"class":72},[62,42957,38814],{"class":122},[62,42959,822],{"class":72},[62,42961,42962],{"class":64,"line":242},[62,42963,3731],{"class":72},[62,42965,42966],{"class":64,"line":247},[62,42967,79],{"emptyLinePlaceholder":13},[62,42969,42970,42972],{"class":64,"line":252},[62,42971,42836],{"class":72},[62,42973,13555],{"class":68},[62,42975,42976,42978,42980,42982,42984,42986],{"class":64,"line":257},[62,42977,26643],{"class":68},[62,42979,200],{"class":68},[62,42981,23301],{"class":122},[62,42983,38835],{"class":72},[62,42985,38838],{"class":889},[62,42987,768],{"class":72},[62,42989,42990,42992,42994,42996,42998,43000,43002,43005],{"class":64,"line":271},[62,42991,18523],{"class":68},[62,42993,42572],{"class":72},[62,42995,146],{"class":68},[62,42997,38713],{"class":72},[62,42999,38716],{"class":122},[62,43001,2109],{"class":72},[62,43003,43004],{"class":1675},"\"INSERT INTO post(id,title,slug,date,time_to_read,tags) values(?,?,?,?,?,?)\"",[62,43006,2212],{"class":72},[62,43008,43009,43011,43013,43015,43017,43019,43021,43023,43025,43027,43029,43031,43033,43035,43037,43039,43041],{"class":64,"line":281},[62,43010,42872],{"class":72},[62,43012,38861],{"class":122},[62,43014,3295],{"class":72},[62,43016,3298],{"class":122},[62,43018,6305],{"class":72},[62,43020,6283],{"class":122},[62,43022,38872],{"class":72},[62,43024,3196],{"class":122},[62,43026,38872],{"class":72},[62,43028,41300],{"class":122},[62,43030,38872],{"class":72},[62,43032,42475],{"class":122},[62,43034,38872],{"class":72},[62,43036,42480],{"class":122},[62,43038,38872],{"class":72},[62,43040,41306],{"class":122},[62,43042,3890],{"class":72},[62,43044,43045,43047,43049],{"class":64,"line":286},[62,43046,42872],{"class":72},[62,43048,1338],{"class":122},[62,43050,822],{"class":72},[62,43052,43053],{"class":64,"line":291},[62,43054,79],{"emptyLinePlaceholder":13},[62,43056,43057,43059,43062,43064,43066,43068,43070,43073,43075,43077,43079],{"class":64,"line":296},[62,43058,14999],{"class":72},[62,43060,43061],{"class":122},"state",[62,43063,42608],{"class":72},[62,43065,28328],{"class":68},[62,43067,22038],{"class":149},[62,43069,976],{"class":72},[62,43071,43072],{"class":1675},"\"Failed to create post \"",[62,43074,4507],{"class":68},[62,43076,5105],{"class":72},[62,43078,3196],{"class":122},[62,43080,1091],{"class":72},[62,43082,43083],{"class":64,"line":302},[62,43084,3731],{"class":72},[62,43086,43087],{"class":64,"line":308},[62,43088,79],{"emptyLinePlaceholder":13},[62,43090,43091,43093],{"class":64,"line":314},[62,43092,42836],{"class":72},[62,43094,13555],{"class":68},[62,43096,43097,43099,43101,43103,43105,43107,43109,43111],{"class":64,"line":320},[62,43098,26643],{"class":68},[62,43100,200],{"class":68},[62,43102,38909],{"class":122},[62,43104,38835],{"class":72},[62,43106,38838],{"class":889},[62,43108,8624],{"class":72},[62,43110,6283],{"class":889},[62,43112,768],{"class":72},[62,43114,43115,43118,43121,43123,43125,43127,43129,43132],{"class":64,"line":326},[62,43116,43117],{"class":68}," var",[62,43119,43120],{"class":72}," updated ",[62,43122,146],{"class":68},[62,43124,38713],{"class":72},[62,43126,38716],{"class":122},[62,43128,2109],{"class":72},[62,43130,43131],{"class":1675},"\"update post set title = ?, slug = ?, date = ?, time_to_read = ?, tags = ? where id = ?\"",[62,43133,2212],{"class":72},[62,43135,43136,43138,43140,43142,43144,43146,43148,43150,43152,43154,43156,43158,43160,43162,43164],{"class":64,"line":338},[62,43137,42872],{"class":72},[62,43139,38861],{"class":122},[62,43141,3295],{"class":72},[62,43143,3298],{"class":122},[62,43145,6305],{"class":72},[62,43147,3196],{"class":122},[62,43149,38872],{"class":72},[62,43151,41300],{"class":122},[62,43153,38872],{"class":72},[62,43155,42475],{"class":122},[62,43157,38872],{"class":72},[62,43159,42480],{"class":122},[62,43161,38872],{"class":72},[62,43163,41306],{"class":122},[62,43165,38957],{"class":72},[62,43167,43168,43170,43172],{"class":64,"line":343},[62,43169,42872],{"class":72},[62,43171,1338],{"class":122},[62,43173,822],{"class":72},[62,43175,43176],{"class":64,"line":357},[62,43177,79],{"emptyLinePlaceholder":13},[62,43179,43180,43182,43184,43187,43189,43191,43193,43196,43198,43200,43202],{"class":64,"line":366},[62,43181,14999],{"class":72},[62,43183,43061],{"class":122},[62,43185,43186],{"class":72},"(updated ",[62,43188,28328],{"class":68},[62,43190,22038],{"class":149},[62,43192,976],{"class":72},[62,43194,43195],{"class":1675},"\"Failed to update post \"",[62,43197,4507],{"class":68},[62,43199,5105],{"class":72},[62,43201,3196],{"class":122},[62,43203,1091],{"class":72},[62,43205,43206],{"class":64,"line":371},[62,43207,3731],{"class":72},[62,43209,43210],{"class":64,"line":376},[62,43211,79],{"emptyLinePlaceholder":13},[62,43213,43214,43216],{"class":64,"line":16333},[62,43215,42836],{"class":72},[62,43217,13555],{"class":68},[62,43219,43220,43222,43224,43226,43228,43230],{"class":64,"line":16349},[62,43221,26643],{"class":68},[62,43223,200],{"class":68},[62,43225,38982],{"class":122},[62,43227,1049],{"class":72},[62,43229,6283],{"class":889},[62,43231,768],{"class":72},[62,43233,43234,43236,43238,43240,43242,43244,43246,43249],{"class":64,"line":16365},[62,43235,43117],{"class":68},[62,43237,43120],{"class":72},[62,43239,146],{"class":68},[62,43241,38713],{"class":72},[62,43243,38716],{"class":122},[62,43245,2109],{"class":72},[62,43247,43248],{"class":1675},"\"delete from post where id = :id\"",[62,43250,2212],{"class":72},[62,43252,43253,43255,43257,43259,43261],{"class":64,"line":16381},[62,43254,42872],{"class":72},[62,43256,38791],{"class":122},[62,43258,2109],{"class":72},[62,43260,38796],{"class":1675},[62,43262,38799],{"class":72},[62,43264,43265,43267,43269],{"class":64,"line":16402},[62,43266,42872],{"class":72},[62,43268,1338],{"class":122},[62,43270,822],{"class":72},[62,43272,43273],{"class":64,"line":16411},[62,43274,79],{"emptyLinePlaceholder":13},[62,43276,43277,43279,43281,43283,43285,43287,43289,43292,43294],{"class":64,"line":16427},[62,43278,14999],{"class":72},[62,43280,43061],{"class":122},[62,43282,43186],{"class":72},[62,43284,28328],{"class":68},[62,43286,22038],{"class":149},[62,43288,976],{"class":72},[62,43290,43291],{"class":1675},"\"Failed to delete post \"",[62,43293,4507],{"class":68},[62,43295,42383],{"class":72},[62,43297,43298],{"class":64,"line":16448},[62,43299,3731],{"class":72},[62,43301,43302],{"class":64,"line":16457},[62,43303,79],{"emptyLinePlaceholder":13},[62,43305,43306],{"class":64,"line":16466},[62,43307,379],{"class":72},[22,43309,43310],{},"The JDBC client has a cleaner and much more readable interface compared to the JDBC template, making writing code smoother and more intuitive.",[636,43312,43314],{"id":43313},"step-6-using-the-new-post-service","Step 6: Using the new Post Service",[22,43316,43317,43318,43320,43321,43323,43324,43326,43327,43329],{},"As the final step, we'll swap the JDBC template version of our ",[59,43319,41346],{}," with the JDBC client version in the ",[59,43322,41342],{}," class. When the type is\n",[59,43325,41346],{}," are there are 2 implementations you need to be explicit about which implementation Spring should select. There are a few\nways to do this but one way is to use the ",[59,43328,19273],{}," annotation.",[52,43331,43333],{"className":54,"code":43332,"language":56,"meta":57,"style":57},"@RestController\n@RequestMapping(\"/api/posts\")\npublic class PostController {\n\n private final PostService postService;\n\n public PostController(@Qualifier(\"clientPostService\") PostService postService) {\n this.postService = postService;\n }\n\n // ...\n\n}\n",[59,43334,43335,43341,43353,43363,43367,43375,43379,43401,43411,43415,43419,43424,43428],{"__ignoreMap":57},[62,43336,43337,43339],{"class":64,"line":65},[62,43338,942],{"class":72},[62,43340,2342],{"class":68},[62,43342,43343,43345,43347,43349,43351],{"class":64,"line":76},[62,43344,942],{"class":72},[62,43346,10592],{"class":68},[62,43348,2109],{"class":72},[62,43350,41368],{"class":1675},[62,43352,2212],{"class":72},[62,43354,43355,43357,43359,43361],{"class":64,"line":82},[62,43356,116],{"class":68},[62,43358,119],{"class":68},[62,43360,41379],{"class":122},[62,43362,126],{"class":72},[62,43364,43365],{"class":64,"line":89},[62,43366,79],{"emptyLinePlaceholder":13},[62,43368,43369,43371,43373],{"class":64,"line":95},[62,43370,137],{"class":68},[62,43372,458],{"class":68},[62,43374,41394],{"class":72},[62,43376,43377],{"class":64,"line":101},[62,43378,79],{"emptyLinePlaceholder":13},[62,43380,43381,43383,43385,43387,43389,43391,43394,43397,43399],{"class":64,"line":107},[62,43382,194],{"class":68},[62,43384,41379],{"class":122},[62,43386,2475],{"class":72},[62,43388,19164],{"class":68},[62,43390,2109],{"class":72},[62,43392,43393],{"class":1675},"\"clientPostService\"",[62,43395,43396],{"class":72},") PostService ",[62,43398,41410],{"class":889},[62,43400,768],{"class":72},[62,43402,43403,43405,43407,43409],{"class":64,"line":113},[62,43404,2405],{"class":149},[62,43406,41419],{"class":72},[62,43408,146],{"class":68},[62,43410,41424],{"class":72},[62,43412,43413],{"class":64,"line":129},[62,43414,223],{"class":72},[62,43416,43417],{"class":64,"line":134},[62,43418,79],{"emptyLinePlaceholder":13},[62,43420,43421],{"class":64,"line":156},[62,43422,43423],{"class":85}," // ...\n",[62,43425,43426],{"class":64,"line":161},[62,43427,79],{"emptyLinePlaceholder":13},[62,43429,43430],{"class":64,"line":167},[62,43431,379],{"class":72},[52,43433,43435],{"className":54,"code":43434,"language":56,"meta":57,"style":57},"@SpringBootApplication\npublic class Application {\n\n public static void main(String[] args) {\n SpringApplication.run(Application.class, args);\n }\n\n @Bean\n CommandLineRunner commandLineRunner(@Qualifier(\"clientPostService\") PostService postService) {\n return args -> {\n postService.create(new Post(\"1234\", \"Hello World\", \"hello-world\", LocalDate.now(), 1, \"java, spring\"));\n };\n }\n\n}\n",[59,43436,43437,43443,43453,43457,43477,43485,43489,43493,43499,43519,43529,43574,43578,43582,43586],{"__ignoreMap":57},[62,43438,43439,43441],{"class":64,"line":65},[62,43440,942],{"class":72},[62,43442,2079],{"class":68},[62,43444,43445,43447,43449,43451],{"class":64,"line":76},[62,43446,116],{"class":68},[62,43448,119],{"class":68},[62,43450,2088],{"class":122},[62,43452,126],{"class":72},[62,43454,43455],{"class":64,"line":82},[62,43456,79],{"emptyLinePlaceholder":13},[62,43458,43459,43461,43463,43465,43467,43469,43471,43473,43475],{"class":64,"line":89},[62,43460,194],{"class":68},[62,43462,2101],{"class":68},[62,43464,200],{"class":68},[62,43466,2106],{"class":122},[62,43468,2109],{"class":72},[62,43470,973],{"class":68},[62,43472,2114],{"class":72},[62,43474,2117],{"class":889},[62,43476,768],{"class":72},[62,43478,43479,43481,43483],{"class":64,"line":95},[62,43480,2124],{"class":72},[62,43482,2127],{"class":122},[62,43484,2130],{"class":72},[62,43486,43487],{"class":64,"line":101},[62,43488,223],{"class":72},[62,43490,43491],{"class":64,"line":107},[62,43492,79],{"emptyLinePlaceholder":13},[62,43494,43495,43497],{"class":64,"line":113},[62,43496,2143],{"class":72},[62,43498,2146],{"class":68},[62,43500,43501,43503,43505,43507,43509,43511,43513,43515,43517],{"class":64,"line":129},[62,43502,2151],{"class":72},[62,43504,2154],{"class":122},[62,43506,2475],{"class":72},[62,43508,19164],{"class":68},[62,43510,2109],{"class":72},[62,43512,43393],{"class":1675},[62,43514,43396],{"class":72},[62,43516,41410],{"class":889},[62,43518,768],{"class":72},[62,43520,43521,43523,43525,43527],{"class":64,"line":134},[62,43522,360],{"class":68},[62,43524,2169],{"class":72},[62,43526,800],{"class":68},[62,43528,126],{"class":72},[62,43530,43531,43534,43536,43538,43540,43542,43544,43546,43548,43551,43553,43556,43559,43562,43565,43567,43569,43572],{"class":64,"line":156},[62,43532,43533],{"class":72}," postService.",[62,43535,14348],{"class":122},[62,43537,2109],{"class":72},[62,43539,2426],{"class":68},[62,43541,41321],{"class":122},[62,43543,2109],{"class":72},[62,43545,39883],{"class":1675},[62,43547,976],{"class":72},[62,43549,43550],{"class":1675},"\"Hello World\"",[62,43552,976],{"class":72},[62,43554,43555],{"class":1675},"\"hello-world\"",[62,43557,43558],{"class":72},", LocalDate.",[62,43560,43561],{"class":122},"now",[62,43563,43564],{"class":72},"(), ",[62,43566,6689],{"class":149},[62,43568,976],{"class":72},[62,43570,43571],{"class":1675},"\"java, spring\"",[62,43573,6979],{"class":72},[62,43575,43576],{"class":64,"line":161},[62,43577,2252],{"class":72},[62,43579,43580],{"class":64,"line":167},[62,43581,223],{"class":72},[62,43583,43584],{"class":64,"line":173},[62,43585,79],{"emptyLinePlaceholder":13},[62,43587,43588],{"class":64,"line":179},[62,43589,379],{"class":72},[22,43591,43592],{},"In navigating to our localhost API in the browser, we should see a collection of all the posts we have inserted and check individual post records by id. For non-existing records, a null result will be returned.",[26,43594,1499],{"id":1498},[22,43596,43597],{},"That concludes our exploration of the new JDBC Client in the Spring Framework 6.1 and Spring Boot 3.2. Once again, Spring has introduced a tool that simplifies otherwise complex tasks – a fluent API with easy setup for smooth use in your spring project.",[29685,43599,43600],{},[22,43601,43602],{},"\"Simplicity is the ultimate sophistication\" - Leonardo da Vinci.",[1527,43604,43605],{},"html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}html pre.shiki code .spoOn, html code.shiki .spoOn{--shiki-default:#E36209;--shiki-dark:#FFAB70;--shiki-sepia:#E36209}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}html pre.shiki code .s4-Ip, html code.shiki .s4-Ip{--shiki-default:#6A737D;--shiki-dark:#6A737D;--shiki-sepia:#6A737D}",{"title":57,"searchDepth":76,"depth":76,"links":43607},[43608,43609,43610,43618],{"id":41234,"depth":76,"text":41235},{"id":41250,"depth":76,"text":41251},{"id":41260,"depth":76,"text":41261,"children":43611},[43612,43613,43614,43615,43616,43617],{"id":41273,"depth":82,"text":41274},{"id":41287,"depth":82,"text":41288},{"id":41810,"depth":82,"text":41811},{"id":42001,"depth":82,"text":42002},{"id":42738,"depth":82,"text":42739},{"id":43313,"depth":82,"text":43314},{"id":1498,"depth":76,"text":1499},{"_id":43620,"path":43621,"title":43622,"description":43623,"meta":43624,"body":43629},"content/blog/2023/09/08/rest-client-first-look.md","/blog/2023/09/08/rest-client-first-look","A First Look at the new Rest Client in Spring Boot 3.2","This is my first look at the new Rest Client in Spring Boot 3.2. In this tutorial we will discuss what a client is, what are the different implementations of clients available and how to get started with the new Rest Client in Spring Framework 6.1 and Spring Boot 3.2",{"slug":43625,"date":43626,"published":13,"tags":43627,"author":17,"cover":43628,"excerpt":-1},"rest-client-first-look","2023-09-08T17:00:00.000Z",[2925],"./spring-boot-rest-client.png",{"type":19,"value":43630,"toc":45253},[43631,43634,43638,43647,43658,43672,43678,43681,43687,43691,43694,43699,43702,43706,43715,43718,43742,43748,44078,44081,44172,44178,44252,44255,44268,44271,44705,44901,44904,44908,44911,44914,45090,45242,45244,45247,45250],[22,43632,43633],{},"Greetings, folks! It's Dan Vega, Spring developer advocate at VMware. Today, I am thrilled to dive into Spring Boot 3.2 and its new REST client. But before we do, let's take a moment to examine our journey to this moment.",[26,43635,43637],{"id":43636},"from-start-to-rest-client","From Start to REST Client",[22,43639,43640,43641,43646],{},"The term \"client\" in the context of REST refers to a method for invoking another REST API within our organization, or perhaps a public API. In the Spring framework world, we've had numerous implementations of ",[677,43642,43645],{"href":43643,"rel":43644},"https://docs.spring.io/spring-framework/reference/integration/rest-clients.html",[681],"REST clients",", and today we'll touch on a few of those.",[22,43648,43649,43650,43654,43655,43657],{},"If you're developing a Spring MVC application, a blocking application, you'd traditionally use something like the ",[677,43651,38596],{"href":43652,"rel":43653},"https://docs.spring.io/spring-framework/reference/integration/rest-clients.html#rest-resttemplate",[681],". ",[59,43656,38596],{}," is fantastic — it offers numerous overloaded methods and the ability for us to construct a service that calls another service. However, the myriad of overloaded methods can lead to confusion about which method is appropriate for the scenario you're addressing.",[22,43659,43660,43661,43665,43666,43668,43669,43671],{},"Next up, we had the ",[677,43662,34866],{"href":43663,"rel":43664},"https://docs.spring.io/spring-framework/reference/web/webflux-webclient.html",[681],". In the reactive space, with Spring WebFlux, we needed a fresh client. The ",[59,43667,34866],{}," took a plethora of lessons from the ",[59,43670,38596],{}," and enhanced upon them, with the beautiful bonus of a fluent API, allowing a graceful and straightforward declaration of service-to-service communication.",[22,43673,43674,43675,43677],{},"Fast forward to the introduction of ",[59,43676,34870],{},". The RestClient took a lot from the WebClient and applied it to Spring MVC. The key advantage here is that you can use it in your MVC applications without having to bring in an external dependency.",[22,43679,43680],{},"What's really fascinating to note is the support for HTTP interfaces that came in Spring Boot 3 and Spring Framework 6. This support extends to the RestClient as well. This means you no longer need to include the Spring WebFlux dependency to utilize HTTP interfaces.",[22,43682,43683,43684,6277],{},"Let's create a small CRUD service application that uses the Rest client to make a call to a public API. Excited? Let's create a new application on ",[677,43685,2903],{"href":40207,"rel":43686},[681],[26,43688,43690],{"id":43689},"building-an-application","Building an Application",[22,43692,43693],{},"Start off by selecting a project type like Maven and choosing Java as your language. I'm using Spring's Boot 3.2.0, Milestone 2. If you're tuning in from the future, please go ahead with the most current milestone release of 3.2, or perhaps 3.2+ if it's publicly available.",[22,43695,43696],{},[653,43697],{"alt":24606,"src":43698},"/images/blog/2023/09/08/start-spring-io.png",[22,43700,43701],{},"Once you've entered your group and project names, choose Spring Web for your dependencies. Our application will be auto-configured, and we'll have access to the Rest client in all of our classes. Let's generate our application. Upon doing this, a zip file will be downloaded. Open it up in your IDE or text editor of choice - I'll be using IntelliJ Ultimate Edition.",[26,43703,43705],{"id":43704},"coding-the-application","Coding the Application",[22,43707,43708,43709,43714],{},"The application we're creating is aimed at calling a public service known as ",[677,43710,43713],{"href":43711,"rel":43712},"https://jsonplaceholder.typicode.com/",[681],"JSONPlaceholder"," – it's a super useful, free API we can use for testing. Our system will be modeled around the post schema of JSONPlaceholder.",[22,43716,43717],{},"To get started, you need to make new packages and create a Java record called Post to represent a post entity.",[52,43719,43721],{"className":54,"code":43720,"language":56,"meta":57,"style":57},"public record Post(Integer id, Integer userId, String title, String body){\n\n}\n",[59,43722,43723,43734,43738],{"__ignoreMap":57},[62,43724,43725,43727,43729,43731],{"class":64,"line":65},[62,43726,116],{"class":68},[62,43728,2996],{"class":68},[62,43730,41321],{"class":122},[62,43732,43733],{"class":72},"(Integer id, Integer userId, String title, String body){\n",[62,43735,43736],{"class":64,"line":76},[62,43737,79],{"emptyLinePlaceholder":13},[62,43739,43740],{"class":64,"line":82},[62,43741,379],{"class":72},[22,43743,43744,43745,43747],{},"Now let's create a PostController class marked with the ",[59,43746,40266],{}," annotation and set up a PostService through constructor injection. Put simply, controllers receive requests, delegate to another class for business logic, and return responses.",[52,43749,43751],{"className":54,"code":43750,"language":56,"meta":57,"style":57},"@RestController\n@RequestMapping(\"/api/posts\")\npublic class PostController {\n\n private final JsonPlaceholderService postService;\n\n public PostController(JsonPlaceholderService postService) {\n this.postService = postService;\n }\n\n @GetMapping(\"\")\n List\u003CPost> findAll() {\n return postService.findAll();\n }\n\n @GetMapping(\"/{id}\")\n Post findById(@PathVariable Integer id) {\n return postService.findById(id);\n }\n\n @PostMapping\n @ResponseStatus(HttpStatus.CREATED)\n Post create(@RequestBody Post post) {\n return postService.create(post);\n }\n\n @PutMapping(\"/{id}\")\n Post update(@PathVariable Integer id, @RequestBody Post post) {\n return postService.update(id, post);\n }\n\n @DeleteMapping(\"/{id}\")\n @ResponseStatus(HttpStatus.NO_CONTENT)\n void delete(@PathVariable Integer id) {\n postService.delete(id);\n }\n\n}\n",[59,43752,43753,43759,43771,43781,43785,43794,43798,43811,43821,43825,43829,43841,43853,43863,43867,43871,43883,43900,43910,43914,43918,43924,43932,43948,43958,43962,43966,43978,44002,44013,44017,44021,44033,44042,44058,44066,44070,44074],{"__ignoreMap":57},[62,43754,43755,43757],{"class":64,"line":65},[62,43756,942],{"class":72},[62,43758,2342],{"class":68},[62,43760,43761,43763,43765,43767,43769],{"class":64,"line":76},[62,43762,942],{"class":72},[62,43764,10592],{"class":68},[62,43766,2109],{"class":72},[62,43768,41368],{"class":1675},[62,43770,2212],{"class":72},[62,43772,43773,43775,43777,43779],{"class":64,"line":82},[62,43774,116],{"class":68},[62,43776,119],{"class":68},[62,43778,41379],{"class":122},[62,43780,126],{"class":72},[62,43782,43783],{"class":64,"line":89},[62,43784,79],{"emptyLinePlaceholder":13},[62,43786,43787,43789,43791],{"class":64,"line":95},[62,43788,137],{"class":68},[62,43790,458],{"class":68},[62,43792,43793],{"class":72}," JsonPlaceholderService postService;\n",[62,43795,43796],{"class":64,"line":101},[62,43797,79],{"emptyLinePlaceholder":13},[62,43799,43800,43802,43804,43807,43809],{"class":64,"line":107},[62,43801,194],{"class":68},[62,43803,41379],{"class":122},[62,43805,43806],{"class":72},"(JsonPlaceholderService ",[62,43808,41410],{"class":889},[62,43810,768],{"class":72},[62,43812,43813,43815,43817,43819],{"class":64,"line":113},[62,43814,2405],{"class":149},[62,43816,41419],{"class":72},[62,43818,146],{"class":68},[62,43820,41424],{"class":72},[62,43822,43823],{"class":64,"line":129},[62,43824,223],{"class":72},[62,43826,43827],{"class":64,"line":134},[62,43828,79],{"emptyLinePlaceholder":13},[62,43830,43831,43833,43835,43837,43839],{"class":64,"line":156},[62,43832,2143],{"class":72},[62,43834,2548],{"class":68},[62,43836,2109],{"class":72},[62,43838,25895],{"class":1675},[62,43840,2212],{"class":72},[62,43842,43843,43845,43847,43849,43851],{"class":64,"line":161},[62,43844,4396],{"class":72},[62,43846,38700],{"class":68},[62,43848,3135],{"class":72},[62,43850,10287],{"class":122},[62,43852,206],{"class":72},[62,43854,43855,43857,43859,43861],{"class":64,"line":167},[62,43856,360],{"class":68},[62,43858,41463],{"class":72},[62,43860,10287],{"class":122},[62,43862,822],{"class":72},[62,43864,43865],{"class":64,"line":173},[62,43866,223],{"class":72},[62,43868,43869],{"class":64,"line":179},[62,43870,79],{"emptyLinePlaceholder":13},[62,43872,43873,43875,43877,43879,43881],{"class":64,"line":185},[62,43874,2143],{"class":72},[62,43876,2548],{"class":68},[62,43878,2109],{"class":72},[62,43880,41486],{"class":1675},[62,43882,2212],{"class":72},[62,43884,43885,43888,43890,43892,43894,43896,43898],{"class":64,"line":191},[62,43886,43887],{"class":72}," Post ",[62,43889,38763],{"class":122},[62,43891,2475],{"class":72},[62,43893,23740],{"class":68},[62,43895,39510],{"class":72},[62,43897,6283],{"class":889},[62,43899,768],{"class":72},[62,43901,43902,43904,43906,43908],{"class":64,"line":209},[62,43903,360],{"class":68},[62,43905,41463],{"class":72},[62,43907,38763],{"class":122},[62,43909,23764],{"class":72},[62,43911,43912],{"class":64,"line":220},[62,43913,223],{"class":72},[62,43915,43916],{"class":64,"line":226},[62,43917,79],{"emptyLinePlaceholder":13},[62,43919,43920,43922],{"class":64,"line":231},[62,43921,2143],{"class":72},[62,43923,41533],{"class":68},[62,43925,43926,43928,43930],{"class":64,"line":236},[62,43927,2143],{"class":72},[62,43929,41540],{"class":68},[62,43931,41543],{"class":72},[62,43933,43934,43936,43938,43940,43942,43944,43946],{"class":64,"line":242},[62,43935,43887],{"class":72},[62,43937,14348],{"class":122},[62,43939,2475],{"class":72},[62,43941,2478],{"class":68},[62,43943,41556],{"class":72},[62,43945,38838],{"class":889},[62,43947,768],{"class":72},[62,43949,43950,43952,43954,43956],{"class":64,"line":247},[62,43951,360],{"class":68},[62,43953,41463],{"class":72},[62,43955,14348],{"class":122},[62,43957,5777],{"class":72},[62,43959,43960],{"class":64,"line":252},[62,43961,223],{"class":72},[62,43963,43964],{"class":64,"line":257},[62,43965,79],{"emptyLinePlaceholder":13},[62,43967,43968,43970,43972,43974,43976],{"class":64,"line":271},[62,43969,2143],{"class":72},[62,43971,41584],{"class":68},[62,43973,2109],{"class":72},[62,43975,41486],{"class":1675},[62,43977,2212],{"class":72},[62,43979,43980,43982,43984,43986,43988,43990,43992,43994,43996,43998,44000],{"class":64,"line":281},[62,43981,43887],{"class":72},[62,43983,1338],{"class":122},[62,43985,2475],{"class":72},[62,43987,23740],{"class":68},[62,43989,39510],{"class":72},[62,43991,6283],{"class":889},[62,43993,39583],{"class":72},[62,43995,2478],{"class":68},[62,43997,41556],{"class":72},[62,43999,38838],{"class":889},[62,44001,768],{"class":72},[62,44003,44004,44006,44008,44010],{"class":64,"line":286},[62,44005,360],{"class":68},[62,44007,41463],{"class":72},[62,44009,1338],{"class":122},[62,44011,44012],{"class":72},"(id, post);\n",[62,44014,44015],{"class":64,"line":291},[62,44016,223],{"class":72},[62,44018,44019],{"class":64,"line":296},[62,44020,79],{"emptyLinePlaceholder":13},[62,44022,44023,44025,44027,44029,44031],{"class":64,"line":302},[62,44024,2143],{"class":72},[62,44026,23719],{"class":68},[62,44028,2109],{"class":72},[62,44030,41486],{"class":1675},[62,44032,2212],{"class":72},[62,44034,44035,44037,44039],{"class":64,"line":308},[62,44036,2143],{"class":72},[62,44038,41540],{"class":68},[62,44040,44041],{"class":72},"(HttpStatus.NO_CONTENT)\n",[62,44043,44044,44046,44048,44050,44052,44054,44056],{"class":64,"line":314},[62,44045,11710],{"class":68},[62,44047,38982],{"class":122},[62,44049,2475],{"class":72},[62,44051,23740],{"class":68},[62,44053,39510],{"class":72},[62,44055,6283],{"class":889},[62,44057,768],{"class":72},[62,44059,44060,44062,44064],{"class":64,"line":320},[62,44061,41565],{"class":72},[62,44063,41666],{"class":122},[62,44065,23764],{"class":72},[62,44067,44068],{"class":64,"line":326},[62,44069,223],{"class":72},[62,44071,44072],{"class":64,"line":338},[62,44073,79],{"emptyLinePlaceholder":13},[62,44075,44076],{"class":64,"line":343},[62,44077,379],{"class":72},[22,44079,44080],{},"In the PostService class, we're going to create an instance for our client. This is where the action really starts. We use the builder method to pass in a base URL (the endpoint of the service you're calling) and build the RestClient.",[52,44082,44084],{"className":54,"code":44083,"language":56,"meta":57,"style":57},"@Service\npublic class PostService {\n\n private final RestClient restClient;\n\n public PostService() {\n restClient = RestClient.builder()\n .baseUrl(\"https://jsonplaceholder.typicode.com\")\n .build();\n }\n\n}\n",[59,44085,44086,44092,44102,44106,44114,44118,44126,44139,44152,44160,44164,44168],{"__ignoreMap":57},[62,44087,44088,44090],{"class":64,"line":65},[62,44089,942],{"class":72},[62,44091,945],{"class":68},[62,44093,44094,44096,44098,44100],{"class":64,"line":76},[62,44095,116],{"class":68},[62,44097,119],{"class":68},[62,44099,39116],{"class":122},[62,44101,126],{"class":72},[62,44103,44104],{"class":64,"line":82},[62,44105,79],{"emptyLinePlaceholder":13},[62,44107,44108,44110,44112],{"class":64,"line":89},[62,44109,137],{"class":68},[62,44111,458],{"class":68},[62,44113,11287],{"class":72},[62,44115,44116],{"class":64,"line":95},[62,44117,79],{"emptyLinePlaceholder":13},[62,44119,44120,44122,44124],{"class":64,"line":101},[62,44121,194],{"class":68},[62,44123,39116],{"class":122},[62,44125,206],{"class":72},[62,44127,44128,44130,44132,44135,44137],{"class":64,"line":107},[62,44129,40409],{"class":72},[62,44131,146],{"class":68},[62,44133,44134],{"class":72}," RestClient.",[62,44136,2160],{"class":122},[62,44138,2223],{"class":72},[62,44140,44141,44143,44145,44147,44150],{"class":64,"line":113},[62,44142,2418],{"class":72},[62,44144,11188],{"class":122},[62,44146,2109],{"class":72},[62,44148,44149],{"class":1675},"\"https://jsonplaceholder.typicode.com\"",[62,44151,2212],{"class":72},[62,44153,44154,44156,44158],{"class":64,"line":129},[62,44155,2418],{"class":72},[62,44157,2189],{"class":122},[62,44159,822],{"class":72},[62,44161,44162],{"class":64,"line":134},[62,44163,223],{"class":72},[62,44165,44166],{"class":64,"line":156},[62,44167,79],{"emptyLinePlaceholder":13},[62,44169,44170],{"class":64,"line":161},[62,44171,379],{"class":72},[22,44173,44174,44175,44177],{},"Then we need to create methods to interact with our service, such as a ",[59,44176,10287],{}," method to get all of the posts in the system.",[52,44179,44181],{"className":54,"code":44180,"language":56,"meta":57,"style":57},"public List\u003CPost> findAll() {\n return restClient.get()\n .uri(\"/posts\")\n .retrieve()\n .body(new ParameterizedTypeReference\u003CList\u003CPost>>() {});\n}\n",[59,44182,44183,44200,44210,44222,44230,44248],{"__ignoreMap":57},[62,44184,44185,44187,44189,44191,44193,44195,44198],{"class":64,"line":65},[62,44186,116],{"class":68},[62,44188,3835],{"class":72},[62,44190,760],{"class":68},[62,44192,38700],{"class":72},[62,44194,2583],{"class":68},[62,44196,44197],{"class":122}," findAll",[62,44199,206],{"class":72},[62,44201,44202,44204,44206,44208],{"class":64,"line":76},[62,44203,2599],{"class":68},[62,44205,11360],{"class":72},[62,44207,11363],{"class":122},[62,44209,2223],{"class":72},[62,44211,44212,44214,44216,44218,44220],{"class":64,"line":82},[62,44213,2610],{"class":72},[62,44215,11372],{"class":122},[62,44217,2109],{"class":72},[62,44219,39293],{"class":1675},[62,44221,2212],{"class":72},[62,44223,44224,44226,44228],{"class":64,"line":89},[62,44225,2610],{"class":72},[62,44227,11405],{"class":122},[62,44229,2223],{"class":72},[62,44231,44232,44234,44236,44238,44240,44243,44245],{"class":64,"line":95},[62,44233,2610],{"class":72},[62,44235,11414],{"class":122},[62,44237,2109],{"class":72},[62,44239,2426],{"class":68},[62,44241,44242],{"class":72}," ParameterizedTypeReference\u003CList\u003C",[62,44244,38700],{"class":68},[62,44246,44247],{"class":72},">>() {});\n",[62,44249,44250],{"class":64,"line":101},[62,44251,379],{"class":72},[22,44253,44254],{},"Just run your Spring Boot application, and if it starts up fine, you can test it out using an HTTP client to list the posts.",[52,44256,44259],{"className":44257,"code":44258,"language":11496,"meta":57,"style":57},"language-http shiki shiki-themes github-light github-dark github-light","GET http://localhost:8080/api/posts\n",[59,44260,44261],{"__ignoreMap":57},[62,44262,44263,44265],{"class":64,"line":65},[62,44264,40244],{"class":68},[62,44266,44267],{"class":72}," http://localhost:8080/api/posts\n",[22,44269,44270],{},"To further demonstrate the elegance of the RestClient, let's create a few more methods - for creating, updating, and deleting posts. Once you've written the methods, it's straightforward to test them all in an HTTP client.",[52,44272,44274],{"className":54,"code":44273,"language":56,"meta":57,"style":57},"@Service\npublic class PostService {\n\n private final RestClient restClient;\n\n public PostService() {\n restClient = RestClient.builder()\n .baseUrl(\"https://jsonplaceholder.typicode.com\")\n .build();\n }\n\n public List\u003CPost> findAll() {\n return restClient.get()\n .uri(\"/posts\")\n .retrieve()\n .body(new ParameterizedTypeReference\u003CList\u003CPost>>() {});\n }\n\n public Post findById(Integer id) {\n return restClient.get()\n .uri(\"/posts/{id}\", id)\n .retrieve()\n .body(Post.class);\n }\n\n Post create(Post post) {\n return restClient.post()\n .uri(\"/posts\")\n .contentType(MediaType.APPLICATION_JSON)\n .body(post)\n .retrieve()\n .body(Post.class);\n }\n\n Post update(Integer id, Post post) {\n return restClient.put()\n .uri(\"/posts/{id}\", id)\n .contentType(MediaType.APPLICATION_JSON)\n .body(post)\n .retrieve()\n .body(Post.class);\n }\n\n void delete(Integer id) {\n restClient.delete()\n .uri(\"/posts/{id}\", id)\n .retrieve()\n .toBodilessEntity();\n }\n\n\n}\n",[59,44275,44276,44282,44292,44296,44304,44308,44316,44328,44340,44348,44352,44356,44370,44380,44392,44400,44416,44420,44424,44438,44448,44460,44468,44476,44480,44484,44496,44506,44518,44528,44537,44545,44553,44557,44561,44578,44588,44600,44608,44616,44624,44632,44636,44640,44652,44661,44673,44681,44689,44693,44697,44701],{"__ignoreMap":57},[62,44277,44278,44280],{"class":64,"line":65},[62,44279,942],{"class":72},[62,44281,945],{"class":68},[62,44283,44284,44286,44288,44290],{"class":64,"line":76},[62,44285,116],{"class":68},[62,44287,119],{"class":68},[62,44289,39116],{"class":122},[62,44291,126],{"class":72},[62,44293,44294],{"class":64,"line":82},[62,44295,79],{"emptyLinePlaceholder":13},[62,44297,44298,44300,44302],{"class":64,"line":89},[62,44299,137],{"class":68},[62,44301,458],{"class":68},[62,44303,11287],{"class":72},[62,44305,44306],{"class":64,"line":95},[62,44307,79],{"emptyLinePlaceholder":13},[62,44309,44310,44312,44314],{"class":64,"line":101},[62,44311,194],{"class":68},[62,44313,39116],{"class":122},[62,44315,206],{"class":72},[62,44317,44318,44320,44322,44324,44326],{"class":64,"line":107},[62,44319,40409],{"class":72},[62,44321,146],{"class":68},[62,44323,44134],{"class":72},[62,44325,2160],{"class":122},[62,44327,2223],{"class":72},[62,44329,44330,44332,44334,44336,44338],{"class":64,"line":113},[62,44331,2418],{"class":72},[62,44333,11188],{"class":122},[62,44335,2109],{"class":72},[62,44337,44149],{"class":1675},[62,44339,2212],{"class":72},[62,44341,44342,44344,44346],{"class":64,"line":129},[62,44343,2418],{"class":72},[62,44345,2189],{"class":122},[62,44347,822],{"class":72},[62,44349,44350],{"class":64,"line":134},[62,44351,223],{"class":72},[62,44353,44354],{"class":64,"line":156},[62,44355,79],{"emptyLinePlaceholder":13},[62,44357,44358,44360,44362,44364,44366,44368],{"class":64,"line":161},[62,44359,194],{"class":68},[62,44361,3079],{"class":72},[62,44363,38700],{"class":68},[62,44365,3135],{"class":72},[62,44367,10287],{"class":122},[62,44369,206],{"class":72},[62,44371,44372,44374,44376,44378],{"class":64,"line":167},[62,44373,360],{"class":68},[62,44375,11360],{"class":72},[62,44377,11363],{"class":122},[62,44379,2223],{"class":72},[62,44381,44382,44384,44386,44388,44390],{"class":64,"line":173},[62,44383,2418],{"class":72},[62,44385,11372],{"class":122},[62,44387,2109],{"class":72},[62,44389,39293],{"class":1675},[62,44391,2212],{"class":72},[62,44393,44394,44396,44398],{"class":64,"line":179},[62,44395,2418],{"class":72},[62,44397,11405],{"class":122},[62,44399,2223],{"class":72},[62,44401,44402,44404,44406,44408,44410,44412,44414],{"class":64,"line":185},[62,44403,2418],{"class":72},[62,44405,11414],{"class":122},[62,44407,2109],{"class":72},[62,44409,2426],{"class":68},[62,44411,44242],{"class":72},[62,44413,38700],{"class":68},[62,44415,44247],{"class":72},[62,44417,44418],{"class":64,"line":191},[62,44419,223],{"class":72},[62,44421,44422],{"class":64,"line":209},[62,44423,79],{"emptyLinePlaceholder":13},[62,44425,44426,44428,44430,44432,44434,44436],{"class":64,"line":220},[62,44427,194],{"class":68},[62,44429,41556],{"class":72},[62,44431,38763],{"class":122},[62,44433,8601],{"class":72},[62,44435,6283],{"class":889},[62,44437,768],{"class":72},[62,44439,44440,44442,44444,44446],{"class":64,"line":226},[62,44441,360],{"class":68},[62,44443,11360],{"class":72},[62,44445,11363],{"class":122},[62,44447,2223],{"class":72},[62,44449,44450,44452,44454,44456,44458],{"class":64,"line":231},[62,44451,2418],{"class":72},[62,44453,11372],{"class":122},[62,44455,2109],{"class":72},[62,44457,39365],{"class":1675},[62,44459,38799],{"class":72},[62,44461,44462,44464,44466],{"class":64,"line":236},[62,44463,2418],{"class":72},[62,44465,11405],{"class":122},[62,44467,2223],{"class":72},[62,44469,44470,44472,44474],{"class":64,"line":242},[62,44471,2418],{"class":72},[62,44473,11414],{"class":122},[62,44475,39385],{"class":72},[62,44477,44478],{"class":64,"line":247},[62,44479,223],{"class":72},[62,44481,44482],{"class":64,"line":252},[62,44483,79],{"emptyLinePlaceholder":13},[62,44485,44486,44488,44490,44492,44494],{"class":64,"line":257},[62,44487,43887],{"class":72},[62,44489,14348],{"class":122},[62,44491,38835],{"class":72},[62,44493,38838],{"class":889},[62,44495,768],{"class":72},[62,44497,44498,44500,44502,44504],{"class":64,"line":271},[62,44499,360],{"class":68},[62,44501,11360],{"class":72},[62,44503,38838],{"class":122},[62,44505,2223],{"class":72},[62,44507,44508,44510,44512,44514,44516],{"class":64,"line":281},[62,44509,2418],{"class":72},[62,44511,11372],{"class":122},[62,44513,2109],{"class":72},[62,44515,39293],{"class":1675},[62,44517,2212],{"class":72},[62,44519,44520,44522,44525],{"class":64,"line":286},[62,44521,2418],{"class":72},[62,44523,44524],{"class":122},"contentType",[62,44526,44527],{"class":72},"(MediaType.APPLICATION_JSON)\n",[62,44529,44530,44532,44534],{"class":64,"line":291},[62,44531,2418],{"class":72},[62,44533,11414],{"class":122},[62,44535,44536],{"class":72},"(post)\n",[62,44538,44539,44541,44543],{"class":64,"line":296},[62,44540,2418],{"class":72},[62,44542,11405],{"class":122},[62,44544,2223],{"class":72},[62,44546,44547,44549,44551],{"class":64,"line":302},[62,44548,2418],{"class":72},[62,44550,11414],{"class":122},[62,44552,39385],{"class":72},[62,44554,44555],{"class":64,"line":308},[62,44556,223],{"class":72},[62,44558,44559],{"class":64,"line":314},[62,44560,79],{"emptyLinePlaceholder":13},[62,44562,44563,44565,44567,44569,44571,44574,44576],{"class":64,"line":320},[62,44564,43887],{"class":72},[62,44566,1338],{"class":122},[62,44568,8601],{"class":72},[62,44570,6283],{"class":889},[62,44572,44573],{"class":72},", Post ",[62,44575,38838],{"class":889},[62,44577,768],{"class":72},[62,44579,44580,44582,44584,44586],{"class":64,"line":326},[62,44581,360],{"class":68},[62,44583,11360],{"class":72},[62,44585,1212],{"class":122},[62,44587,2223],{"class":72},[62,44589,44590,44592,44594,44596,44598],{"class":64,"line":338},[62,44591,2418],{"class":72},[62,44593,11372],{"class":122},[62,44595,2109],{"class":72},[62,44597,39365],{"class":1675},[62,44599,38799],{"class":72},[62,44601,44602,44604,44606],{"class":64,"line":343},[62,44603,2418],{"class":72},[62,44605,44524],{"class":122},[62,44607,44527],{"class":72},[62,44609,44610,44612,44614],{"class":64,"line":357},[62,44611,2418],{"class":72},[62,44613,11414],{"class":122},[62,44615,44536],{"class":72},[62,44617,44618,44620,44622],{"class":64,"line":366},[62,44619,2418],{"class":72},[62,44621,11405],{"class":122},[62,44623,2223],{"class":72},[62,44625,44626,44628,44630],{"class":64,"line":371},[62,44627,2418],{"class":72},[62,44629,11414],{"class":122},[62,44631,39385],{"class":72},[62,44633,44634],{"class":64,"line":376},[62,44635,223],{"class":72},[62,44637,44638],{"class":64,"line":16333},[62,44639,79],{"emptyLinePlaceholder":13},[62,44641,44642,44644,44646,44648,44650],{"class":64,"line":16349},[62,44643,11710],{"class":68},[62,44645,38982],{"class":122},[62,44647,8601],{"class":72},[62,44649,6283],{"class":889},[62,44651,768],{"class":72},[62,44653,44654,44657,44659],{"class":64,"line":16365},[62,44655,44656],{"class":72}," restClient.",[62,44658,41666],{"class":122},[62,44660,2223],{"class":72},[62,44662,44663,44665,44667,44669,44671],{"class":64,"line":16381},[62,44664,2418],{"class":72},[62,44666,11372],{"class":122},[62,44668,2109],{"class":72},[62,44670,39365],{"class":1675},[62,44672,38799],{"class":72},[62,44674,44675,44677,44679],{"class":64,"line":16402},[62,44676,2418],{"class":72},[62,44678,11405],{"class":122},[62,44680,2223],{"class":72},[62,44682,44683,44685,44687],{"class":64,"line":16411},[62,44684,2418],{"class":72},[62,44686,40528],{"class":122},[62,44688,822],{"class":72},[62,44690,44691],{"class":64,"line":16427},[62,44692,223],{"class":72},[62,44694,44695],{"class":64,"line":16448},[62,44696,79],{"emptyLinePlaceholder":13},[62,44698,44699],{"class":64,"line":16457},[62,44700,79],{"emptyLinePlaceholder":13},[62,44702,44703],{"class":64,"line":16466},[62,44704,379],{"class":72},[52,44706,44708],{"className":44257,"code":44707,"language":11496,"meta":57,"style":57},"### List Posts\nGET http://localhost:8080/api/posts\n\n### Get Post by ID\nGET http://localhost:8080/api/posts/1\n\n### Create new Post\nPOST http://localhost:8080/api/posts\nContent-Type: application/json\n\n{\n \"title\": \"Dan's Test Title\",\n \"body\": \"Dan's Test Body\",\n \"userId\": 1\n}\n\n### Update Post\nPUT http://localhost:8080/api/posts/1\nContent-Type: application/json\n\n{\n \"id\": 1,\n \"title\": \"UPDATED TITLE\",\n \"body\": \"UPDATED BODY\",\n \"userId\": 1\n}\n\n### Delete Post\nDELETE http://localhost:8080/api/posts/1\n",[59,44709,44710,44715,44721,44725,44730,44737,44741,44746,44752,44762,44766,44770,44782,44794,44804,44808,44812,44817,44824,44832,44836,44840,44851,44862,44873,44881,44885,44889,44894],{"__ignoreMap":57},[62,44711,44712],{"class":64,"line":65},[62,44713,44714],{"class":85},"### List Posts\n",[62,44716,44717,44719],{"class":64,"line":76},[62,44718,40244],{"class":68},[62,44720,44267],{"class":72},[62,44722,44723],{"class":64,"line":82},[62,44724,79],{"emptyLinePlaceholder":13},[62,44726,44727],{"class":64,"line":89},[62,44728,44729],{"class":85},"### Get Post by ID\n",[62,44731,44732,44734],{"class":64,"line":95},[62,44733,40244],{"class":68},[62,44735,44736],{"class":72}," http://localhost:8080/api/posts/1\n",[62,44738,44739],{"class":64,"line":101},[62,44740,79],{"emptyLinePlaceholder":13},[62,44742,44743],{"class":64,"line":107},[62,44744,44745],{"class":85},"### Create new Post\n",[62,44747,44748,44750],{"class":64,"line":113},[62,44749,18386],{"class":68},[62,44751,44267],{"class":72},[62,44753,44754,44757,44759],{"class":64,"line":129},[62,44755,44756],{"class":1780},"Content-Type",[62,44758,1266],{"class":68},[62,44760,44761],{"class":1675}," application/json\n",[62,44763,44764],{"class":64,"line":134},[62,44765,79],{"emptyLinePlaceholder":13},[62,44767,44768],{"class":64,"line":156},[62,44769,3680],{"class":72},[62,44771,44772,44775,44777,44780],{"class":64,"line":161},[62,44773,44774],{"class":149}," \"title\"",[62,44776,3696],{"class":72},[62,44778,44779],{"class":1675},"\"Dan's Test Title\"",[62,44781,3338],{"class":72},[62,44783,44784,44787,44789,44792],{"class":64,"line":167},[62,44785,44786],{"class":149}," \"body\"",[62,44788,3696],{"class":72},[62,44790,44791],{"class":1675},"\"Dan's Test Body\"",[62,44793,3338],{"class":72},[62,44795,44796,44799,44801],{"class":64,"line":173},[62,44797,44798],{"class":149}," \"userId\"",[62,44800,3696],{"class":72},[62,44802,44803],{"class":149},"1\n",[62,44805,44806],{"class":64,"line":179},[62,44807,379],{"class":72},[62,44809,44810],{"class":64,"line":185},[62,44811,79],{"emptyLinePlaceholder":13},[62,44813,44814],{"class":64,"line":191},[62,44815,44816],{"class":85},"### Update Post\n",[62,44818,44819,44822],{"class":64,"line":209},[62,44820,44821],{"class":68},"PUT",[62,44823,44736],{"class":72},[62,44825,44826,44828,44830],{"class":64,"line":220},[62,44827,44756],{"class":1780},[62,44829,1266],{"class":68},[62,44831,44761],{"class":1675},[62,44833,44834],{"class":64,"line":226},[62,44835,79],{"emptyLinePlaceholder":13},[62,44837,44838],{"class":64,"line":231},[62,44839,3680],{"class":72},[62,44841,44842,44845,44847,44849],{"class":64,"line":236},[62,44843,44844],{"class":149}," \"id\"",[62,44846,3696],{"class":72},[62,44848,6689],{"class":149},[62,44850,3338],{"class":72},[62,44852,44853,44855,44857,44860],{"class":64,"line":242},[62,44854,44774],{"class":149},[62,44856,3696],{"class":72},[62,44858,44859],{"class":1675},"\"UPDATED TITLE\"",[62,44861,3338],{"class":72},[62,44863,44864,44866,44868,44871],{"class":64,"line":247},[62,44865,44786],{"class":149},[62,44867,3696],{"class":72},[62,44869,44870],{"class":1675},"\"UPDATED BODY\"",[62,44872,3338],{"class":72},[62,44874,44875,44877,44879],{"class":64,"line":252},[62,44876,44798],{"class":149},[62,44878,3696],{"class":72},[62,44880,44803],{"class":149},[62,44882,44883],{"class":64,"line":257},[62,44884,379],{"class":72},[62,44886,44887],{"class":64,"line":271},[62,44888,79],{"emptyLinePlaceholder":13},[62,44890,44891],{"class":64,"line":281},[62,44892,44893],{"class":85},"### Delete Post\n",[62,44895,44896,44899],{"class":64,"line":286},[62,44897,44898],{"class":68},"DELETE",[62,44900,44736],{"class":72},[22,44902,44903],{},"At this point, you can see how effortlessly we've made connections to a public API using the Rest Client. I hope what comes thorugh in the tutorial is the simplicity of the API and the readability of the code.",[26,44905,44907],{"id":44906},"http-interfaces-rest-client","HTTP Interfaces + REST Client",[22,44909,44910],{},"To save you from the manual implementation of all connectivity details, Spring Boot 3.0 introduced HTTP interfaces. Essentially, you define the contracts for your HTTP requests in an interface and allow Spring to fill in the implementation details at runtime.",[22,44912,44913],{},"The novelty here is that while you needed the WebClient to utilize HTTP interfaces in Spring Boot 3.0, Spring MVC applications that leverage RestClient can now use HTTP interfaces natively, sidestepping the need to add an additional dependency to your Maven configuration.",[52,44915,44917],{"className":54,"code":44916,"language":56,"meta":57,"style":57},"public interface JsonPlaceholderService {\n\n @GetExchange(\"/posts\")\n List\u003CPost> findAll();\n\n @GetExchange(\"/posts/{id}\")\n Post findById(Integer id);\n\n @PostExchange(\"/posts\")\n Post create(Post post);\n\n @PutExchange(\"/posts/{id}\")\n Post update(@PathVariable Integer id, Post post);\n\n @DeleteMapping(\"/posts/{id}\")\n void delete(@PathVariable Integer id);\n\n}\n",[59,44918,44919,44930,44934,44946,44958,44962,44974,44986,44990,45002,45014,45018,45030,45050,45054,45066,45082,45086],{"__ignoreMap":57},[62,44920,44921,44923,44925,44928],{"class":64,"line":65},[62,44922,116],{"class":68},[62,44924,8531],{"class":68},[62,44926,44927],{"class":122}," JsonPlaceholderService",[62,44929,126],{"class":72},[62,44931,44932],{"class":64,"line":76},[62,44933,79],{"emptyLinePlaceholder":13},[62,44935,44936,44938,44940,44942,44944],{"class":64,"line":82},[62,44937,2143],{"class":72},[62,44939,39452],{"class":68},[62,44941,2109],{"class":72},[62,44943,39293],{"class":1675},[62,44945,2212],{"class":72},[62,44947,44948,44950,44952,44954,44956],{"class":64,"line":89},[62,44949,4396],{"class":72},[62,44951,38700],{"class":68},[62,44953,3135],{"class":72},[62,44955,10287],{"class":122},[62,44957,822],{"class":72},[62,44959,44960],{"class":64,"line":95},[62,44961,79],{"emptyLinePlaceholder":13},[62,44963,44964,44966,44968,44970,44972],{"class":64,"line":101},[62,44965,2143],{"class":72},[62,44967,39452],{"class":68},[62,44969,2109],{"class":72},[62,44971,39365],{"class":1675},[62,44973,2212],{"class":72},[62,44975,44976,44978,44980,44982,44984],{"class":64,"line":107},[62,44977,43887],{"class":72},[62,44979,38763],{"class":122},[62,44981,8601],{"class":72},[62,44983,6283],{"class":889},[62,44985,1133],{"class":72},[62,44987,44988],{"class":64,"line":113},[62,44989,79],{"emptyLinePlaceholder":13},[62,44991,44992,44994,44996,44998,45000],{"class":64,"line":129},[62,44993,2143],{"class":72},[62,44995,39525],{"class":68},[62,44997,2109],{"class":72},[62,44999,39293],{"class":1675},[62,45001,2212],{"class":72},[62,45003,45004,45006,45008,45010,45012],{"class":64,"line":134},[62,45005,43887],{"class":72},[62,45007,14348],{"class":122},[62,45009,38835],{"class":72},[62,45011,38838],{"class":889},[62,45013,1133],{"class":72},[62,45015,45016],{"class":64,"line":156},[62,45017,79],{"emptyLinePlaceholder":13},[62,45019,45020,45022,45024,45026,45028],{"class":64,"line":161},[62,45021,2143],{"class":72},[62,45023,39560],{"class":68},[62,45025,2109],{"class":72},[62,45027,39365],{"class":1675},[62,45029,2212],{"class":72},[62,45031,45032,45034,45036,45038,45040,45042,45044,45046,45048],{"class":64,"line":167},[62,45033,43887],{"class":72},[62,45035,1338],{"class":122},[62,45037,2475],{"class":72},[62,45039,23740],{"class":68},[62,45041,39510],{"class":72},[62,45043,6283],{"class":889},[62,45045,44573],{"class":72},[62,45047,38838],{"class":889},[62,45049,1133],{"class":72},[62,45051,45052],{"class":64,"line":173},[62,45053,79],{"emptyLinePlaceholder":13},[62,45055,45056,45058,45060,45062,45064],{"class":64,"line":179},[62,45057,2143],{"class":72},[62,45059,23719],{"class":68},[62,45061,2109],{"class":72},[62,45063,39365],{"class":1675},[62,45065,2212],{"class":72},[62,45067,45068,45070,45072,45074,45076,45078,45080],{"class":64,"line":185},[62,45069,11710],{"class":68},[62,45071,38982],{"class":122},[62,45073,2475],{"class":72},[62,45075,23740],{"class":68},[62,45077,39510],{"class":72},[62,45079,6283],{"class":889},[62,45081,1133],{"class":72},[62,45083,45084],{"class":64,"line":191},[62,45085,79],{"emptyLinePlaceholder":13},[62,45087,45088],{"class":64,"line":209},[62,45089,379],{"class":72},[52,45091,45093],{"className":54,"code":45092,"language":56,"meta":57,"style":57},"@SpringBootApplication\npublic class Application {\n\n public static void main(String[] args) {\n SpringApplication.run(Application.class, args);\n }\n\n @Bean\n JsonPlaceholderService jsonPlaceholderService() {\n RestClient client = RestClient.create(\"https://jsonplaceholder.typicode.com\");\n HttpServiceProxyFactory factory = HttpServiceProxyFactory\n .builderFor(RestClientAdapter.create(client))\n .build();\n return factory.createClient(JsonPlaceholderService.class);\n }\n\n}\n",[59,45094,45095,45101,45111,45115,45135,45143,45147,45151,45157,45167,45184,45194,45209,45217,45230,45234,45238],{"__ignoreMap":57},[62,45096,45097,45099],{"class":64,"line":65},[62,45098,942],{"class":72},[62,45100,2079],{"class":68},[62,45102,45103,45105,45107,45109],{"class":64,"line":76},[62,45104,116],{"class":68},[62,45106,119],{"class":68},[62,45108,2088],{"class":122},[62,45110,126],{"class":72},[62,45112,45113],{"class":64,"line":82},[62,45114,79],{"emptyLinePlaceholder":13},[62,45116,45117,45119,45121,45123,45125,45127,45129,45131,45133],{"class":64,"line":89},[62,45118,194],{"class":68},[62,45120,2101],{"class":68},[62,45122,200],{"class":68},[62,45124,2106],{"class":122},[62,45126,2109],{"class":72},[62,45128,973],{"class":68},[62,45130,2114],{"class":72},[62,45132,2117],{"class":889},[62,45134,768],{"class":72},[62,45136,45137,45139,45141],{"class":64,"line":95},[62,45138,2124],{"class":72},[62,45140,2127],{"class":122},[62,45142,2130],{"class":72},[62,45144,45145],{"class":64,"line":101},[62,45146,223],{"class":72},[62,45148,45149],{"class":64,"line":107},[62,45150,79],{"emptyLinePlaceholder":13},[62,45152,45153,45155],{"class":64,"line":113},[62,45154,2143],{"class":72},[62,45156,2146],{"class":68},[62,45158,45159,45162,45165],{"class":64,"line":129},[62,45160,45161],{"class":72}," JsonPlaceholderService ",[62,45163,45164],{"class":122},"jsonPlaceholderService",[62,45166,206],{"class":72},[62,45168,45169,45172,45174,45176,45178,45180,45182],{"class":64,"line":134},[62,45170,45171],{"class":72}," RestClient client ",[62,45173,146],{"class":68},[62,45175,44134],{"class":72},[62,45177,14348],{"class":122},[62,45179,2109],{"class":72},[62,45181,44149],{"class":1675},[62,45183,1133],{"class":72},[62,45185,45186,45189,45191],{"class":64,"line":156},[62,45187,45188],{"class":72}," HttpServiceProxyFactory factory ",[62,45190,146],{"class":68},[62,45192,45193],{"class":72}," HttpServiceProxyFactory\n",[62,45195,45196,45198,45201,45204,45206],{"class":64,"line":161},[62,45197,2418],{"class":72},[62,45199,45200],{"class":122},"builderFor",[62,45202,45203],{"class":72},"(RestClientAdapter.",[62,45205,14348],{"class":122},[62,45207,45208],{"class":72},"(client))\n",[62,45210,45211,45213,45215],{"class":64,"line":167},[62,45212,2418],{"class":72},[62,45214,2189],{"class":122},[62,45216,822],{"class":72},[62,45218,45219,45221,45224,45227],{"class":64,"line":173},[62,45220,360],{"class":68},[62,45222,45223],{"class":72}," factory.",[62,45225,45226],{"class":122},"createClient",[62,45228,45229],{"class":72},"(JsonPlaceholderService.class);\n",[62,45231,45232],{"class":64,"line":179},[62,45233,223],{"class":72},[62,45235,45236],{"class":64,"line":185},[62,45237,79],{"emptyLinePlaceholder":13},[62,45239,45240],{"class":64,"line":191},[62,45241,379],{"class":72},[26,45243,32553],{"id":32552},[22,45245,45246],{},"And that's it, folks! With the new RestClient, you'll find your Spring Boot app development journey easier and more enjoyable. The fluent API, fantastic readability, and compatibility with HTTP Interfaces make a compelling case for adopting the RestClient.",[22,45248,45249],{},"Remember, your feedback is valuable. So feel free to drop a comment if you've started tinkering with the RestClient and what you think about it.",[1527,45251,45252],{},"html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}html pre.shiki code .spoOn, html code.shiki .spoOn{--shiki-default:#E36209;--shiki-dark:#FFAB70;--shiki-sepia:#E36209}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}html pre.shiki code .s4-Ip, html code.shiki .s4-Ip{--shiki-default:#6A737D;--shiki-dark:#6A737D;--shiki-sepia:#6A737D}html pre.shiki code .suaqH, html code.shiki .suaqH{--shiki-default:#22863A;--shiki-dark:#85E89D;--shiki-sepia:#22863A}",{"title":57,"searchDepth":76,"depth":76,"links":45254},[45255,45256,45257,45258,45259],{"id":43636,"depth":76,"text":43637},{"id":43689,"depth":76,"text":43690},{"id":43704,"depth":76,"text":43705},{"id":44906,"depth":76,"text":44907},{"id":32552,"depth":76,"text":32553},{"_id":45261,"path":45262,"title":45263,"description":45264,"meta":45265,"body":45270},"content/blog/2023/07/17/pgadmin-docker-compose.md","/blog/2023/07/17/pgadmin-docker-compose","Streamline Your Development: Spring Boot with PostgreSQL & pgAdmin in Docker","In this article you will learn about the new Docker Compose module in Spring Boot 3.1 and how to connect to a Postgres database from your application. You will also learn how to use psql in the shell and pgAdmin to manage your database.",{"slug":45266,"date":45267,"published":13,"tags":45268,"author":17,"cover":45269,"excerpt":-1},"pgadmin-docker-compose","2023-07-17T17:00:00.000Z",[2925],"./pgAdminDockerCompose.png",{"type":19,"value":45271,"toc":46202},[45272,45280,45284,45287,45290,45298,45303,45307,45314,45321,45324,45328,45331,45334,45463,45469,45476,45514,45518,45521,45837,45840,45843,45847,45853,45885,45900,45906,45909,45913,45921,45927,45997,46012,46043,46053,46182,46185,46191,46193,46196,46199],[22,45273,45274,45275,45279],{},"In a ",[677,45276,24580],{"href":45277,"rel":45278},"https://danvega.dev/blog/2023/04/26/spring-boot-docker-compose/",[681]," we looked at the new Docker Compose feature that was added to Spring Boot 3.1. In that article (and accompanying video) Spring Boot 3.1 had yet to be released and since then a couple of things have changed so I decided to follow it up with another tutorial. I also received a few questions around how to gain visibility into a PostgreSQL database when using it inside of a container so I will try and answer that here as well.",[26,45281,45283],{"id":45282},"why-the-follow-up","Why the Follow-up?",[22,45285,45286],{},"Firstly, when I recorded the previous video, version 3.1 wasn't released yet. So we were peeking at an unreleased feature. Now that 3.1 is out, I am excited to show you a new development - the automatic generation of the Docker Compose file when you create a project.",[22,45288,45289],{},"Secondly, in the last video, we took a gander at the Postgres database service. I received several queries about gaining insights and exploring the tables and data in the Postgres database container. So today, I'll be addressing this by demonstrating two options - using psql in a shell, and using PG Admin, a tool that provides a graphical exploration of our database.",[22,45291,45292,45293,45297],{},"So without further ado, let's head over to ",[677,45294,2903],{"href":45295,"rel":45296},"http://start.spring.io/",[681]," and get started.",[22,45299,45300],{},[653,45301],{"alt":24606,"src":45302},"/images/blog/2023/07/17/spring-init.png",[26,45304,45306],{"id":45305},"getting-started-with-spring-io","Getting Started with Spring IO",[22,45308,45309,45310,45313],{},"At ",[677,45311,2903],{"href":45295,"rel":45312},[681],", choose a Maven project using Java and the latest version of Spring Boot - 3.1.1, at the time of this recording. For the group ID, I've chosen \"dev.danvega\" and named the project \"pgAdmin.\" I've selected Java 17 and included the following dependencies: the web dependency, Spring Data JPA, Postgres driver, and Spring Boot's Docker Compose support.",[22,45315,45316,45317,45320],{},"Now, let's explore the project quickly. You'll notice that a ",[59,45318,45319],{},"compose.yml"," is already created for you - no need to manually write the file. It's generated automatically because we included the Docker Compose module and defined a service that needs to be written into it.",[22,45322,45323],{},"Following this, simply generate the project - you'll download a zip file which you can open up in whatever IDE or text editor you prefer. I'll be using the IntelliJ Ultimate Edition.",[26,45325,45327],{"id":45326},"writing-a-simple-application-with-conference-events-and-sessions","Writing a Simple Application with Conference Events and Sessions",[22,45329,45330],{},"We're working on a simple application called \"Sessions,\" where we can manage events with sessions and speakers. The aim is to create a type of data that we can save to a database.",[22,45332,45333],{},"Then, we create a new package named \"event\" and a new entity called \"Event.\" The Event class looks like this:",[52,45335,45337],{"className":54,"code":45336,"language":56,"meta":57,"style":57},"@Entity\npublic class Event {\n\n @Id\n private Integer id;\n private String name;\n @Column(columnDefinition = \"TEXT\")\n private String description;\n private LocalDate startDate;\n private LocalDate endDate;\n private LocalDate cfpStartDate;\n private LocalDate cfpEndDate;\n private String location;\n private String website;\n\n // constructors, getters,setters & toString\n\n}\n",[59,45338,45339,45345,45356,45360,45366,45373,45379,45398,45404,45411,45418,45425,45432,45439,45446,45450,45455,45459],{"__ignoreMap":57},[62,45340,45341,45343],{"class":64,"line":65},[62,45342,942],{"class":72},[62,45344,8999],{"class":68},[62,45346,45347,45349,45351,45354],{"class":64,"line":76},[62,45348,116],{"class":68},[62,45350,119],{"class":68},[62,45352,45353],{"class":122}," Event",[62,45355,126],{"class":72},[62,45357,45358],{"class":64,"line":82},[62,45359,79],{"emptyLinePlaceholder":13},[62,45361,45362,45364],{"class":64,"line":89},[62,45363,2143],{"class":72},[62,45365,22298],{"class":68},[62,45367,45368,45370],{"class":64,"line":95},[62,45369,137],{"class":68},[62,45371,45372],{"class":72}," Integer id;\n",[62,45374,45375,45377],{"class":64,"line":101},[62,45376,137],{"class":68},[62,45378,9406],{"class":72},[62,45380,45381,45383,45386,45388,45391,45393,45396],{"class":64,"line":107},[62,45382,2143],{"class":72},[62,45384,45385],{"class":68},"Column",[62,45387,2109],{"class":72},[62,45389,45390],{"class":149},"columnDefinition",[62,45392,2556],{"class":68},[62,45394,45395],{"class":1675}," \"TEXT\"",[62,45397,2212],{"class":72},[62,45399,45400,45402],{"class":64,"line":113},[62,45401,137],{"class":68},[62,45403,23140],{"class":72},[62,45405,45406,45408],{"class":64,"line":129},[62,45407,137],{"class":68},[62,45409,45410],{"class":72}," LocalDate startDate;\n",[62,45412,45413,45415],{"class":64,"line":134},[62,45414,137],{"class":68},[62,45416,45417],{"class":72}," LocalDate endDate;\n",[62,45419,45420,45422],{"class":64,"line":156},[62,45421,137],{"class":68},[62,45423,45424],{"class":72}," LocalDate cfpStartDate;\n",[62,45426,45427,45429],{"class":64,"line":161},[62,45428,137],{"class":68},[62,45430,45431],{"class":72}," LocalDate cfpEndDate;\n",[62,45433,45434,45436],{"class":64,"line":167},[62,45435,137],{"class":68},[62,45437,45438],{"class":72}," String location;\n",[62,45440,45441,45443],{"class":64,"line":173},[62,45442,137],{"class":68},[62,45444,45445],{"class":72}," String website;\n",[62,45447,45448],{"class":64,"line":179},[62,45449,79],{"emptyLinePlaceholder":13},[62,45451,45452],{"class":64,"line":185},[62,45453,45454],{"class":85}," // constructors, getters,setters & toString\n",[62,45456,45457],{"class":64,"line":191},[62,45458,79],{"emptyLinePlaceholder":13},[62,45460,45461],{"class":64,"line":209},[62,45462,379],{"class":72},[22,45464,45465,45466,22831],{},"An event has properties such as ID, name, description, start date, end date, CFP start and end date (Call for Proposals), location, and a website. We then need some constructors, getters, and setters for our Event class, and a ",[59,45467,45468],{},"toString()",[22,45470,45471,45472,45475],{},"Now, we need a repository for \"Event\" - we can simply create an interface and have it extend the ",[59,45473,45474],{},"ListCrudRepository"," interface in Spring Data.",[52,45477,45479],{"className":54,"code":45478,"language":56,"meta":57,"style":57},"public interface EventRepository extends ListCrudRepository\u003CEvent,Integer> {\n\n}\n",[59,45480,45481,45506,45510],{"__ignoreMap":57},[62,45482,45483,45485,45487,45490,45492,45495,45497,45500,45502,45504],{"class":64,"line":65},[62,45484,116],{"class":68},[62,45486,8531],{"class":68},[62,45488,45489],{"class":122}," EventRepository",[62,45491,8537],{"class":68},[62,45493,45494],{"class":122}," ListCrudRepository",[62,45496,760],{"class":72},[62,45498,45499],{"class":68},"Event",[62,45501,32225],{"class":72},[62,45503,979],{"class":68},[62,45505,8552],{"class":72},[62,45507,45508],{"class":64,"line":76},[62,45509,79],{"emptyLinePlaceholder":13},[62,45511,45512],{"class":64,"line":82},[62,45513,379],{"class":72},[26,45515,45517],{"id":45516},"populating-the-database","Populating the Database",[22,45519,45520],{},"To insert some data into the tables, we create a bean of the Command Line Runner type. In this runner, we create an instance of the event repository:",[52,45522,45524],{"className":54,"code":45523,"language":56,"meta":57,"style":57},"@SpringBootApplication\npublic class Application {\n\n private static final Logger log = LoggerFactory.getLogger(Application.class);\n\n public static void main(String[] args) {\n SpringApplication.run(Application.class, args);\n }\n\n @Bean\n CommandLineRunner commandLineRunner(EventRepository repository) {\n return args -> {\n // persist 1 event\n if(repository.count() == 0) {\n var event = new Event(1,\n \"SpringOne at VMware Explore\",\n \"Join us at the biggest gathering of Spring enthusiasts\",\n LocalDate.of(2023,8,21),\n LocalDate.of(2023,8,24),\n LocalDate.now().minusDays(180),\n LocalDate.now().minusDays(90),\n \"Las Vegas, NV\",\n \"https://springone.io/\");\n\n repository.save(event);\n log.info(\"Event created: \" + event.getName());\n }\n };\n }\n\n}\n",[59,45525,45526,45532,45542,45546,45564,45568,45588,45596,45600,45604,45610,45623,45633,45638,45656,45675,45682,45689,45712,45733,45751,45768,45775,45782,45786,45796,45817,45821,45825,45829,45833],{"__ignoreMap":57},[62,45527,45528,45530],{"class":64,"line":65},[62,45529,942],{"class":72},[62,45531,2079],{"class":68},[62,45533,45534,45536,45538,45540],{"class":64,"line":76},[62,45535,116],{"class":68},[62,45537,119],{"class":68},[62,45539,2088],{"class":122},[62,45541,126],{"class":72},[62,45543,45544],{"class":64,"line":82},[62,45545,79],{"emptyLinePlaceholder":13},[62,45547,45548,45550,45552,45554,45556,45558,45560,45562],{"class":64,"line":89},[62,45549,137],{"class":68},[62,45551,2101],{"class":68},[62,45553,458],{"class":68},[62,45555,3061],{"class":72},[62,45557,146],{"class":68},[62,45559,3066],{"class":72},[62,45561,3069],{"class":122},[62,45563,39733],{"class":72},[62,45565,45566],{"class":64,"line":95},[62,45567,79],{"emptyLinePlaceholder":13},[62,45569,45570,45572,45574,45576,45578,45580,45582,45584,45586],{"class":64,"line":101},[62,45571,194],{"class":68},[62,45573,2101],{"class":68},[62,45575,200],{"class":68},[62,45577,2106],{"class":122},[62,45579,2109],{"class":72},[62,45581,973],{"class":68},[62,45583,2114],{"class":72},[62,45585,2117],{"class":889},[62,45587,768],{"class":72},[62,45589,45590,45592,45594],{"class":64,"line":107},[62,45591,2124],{"class":72},[62,45593,2127],{"class":122},[62,45595,2130],{"class":72},[62,45597,45598],{"class":64,"line":113},[62,45599,223],{"class":72},[62,45601,45602],{"class":64,"line":129},[62,45603,79],{"emptyLinePlaceholder":13},[62,45605,45606,45608],{"class":64,"line":134},[62,45607,2143],{"class":72},[62,45609,2146],{"class":68},[62,45611,45612,45614,45616,45619,45621],{"class":64,"line":156},[62,45613,2151],{"class":72},[62,45615,2154],{"class":122},[62,45617,45618],{"class":72},"(EventRepository ",[62,45620,23540],{"class":889},[62,45622,768],{"class":72},[62,45624,45625,45627,45629,45631],{"class":64,"line":161},[62,45626,360],{"class":68},[62,45628,2169],{"class":72},[62,45630,800],{"class":68},[62,45632,126],{"class":72},[62,45634,45635],{"class":64,"line":167},[62,45636,45637],{"class":85}," // persist 1 event\n",[62,45639,45640,45642,45645,45648,45650,45652,45654],{"class":64,"line":173},[62,45641,1142],{"class":68},[62,45643,45644],{"class":72},"(repository.",[62,45646,45647],{"class":122},"count",[62,45649,5398],{"class":72},[62,45651,28328],{"class":68},[62,45653,150],{"class":149},[62,45655,768],{"class":72},[62,45657,45658,45660,45663,45665,45667,45669,45671,45673],{"class":64,"line":179},[62,45659,13198],{"class":68},[62,45661,45662],{"class":72}," event ",[62,45664,146],{"class":68},[62,45666,466],{"class":68},[62,45668,45353],{"class":122},[62,45670,2109],{"class":72},[62,45672,6689],{"class":149},[62,45674,3338],{"class":72},[62,45676,45677,45680],{"class":64,"line":185},[62,45678,45679],{"class":1675}," \"SpringOne at VMware Explore\"",[62,45681,3338],{"class":72},[62,45683,45684,45687],{"class":64,"line":191},[62,45685,45686],{"class":1675}," \"Join us at the biggest gathering of Spring enthusiasts\"",[62,45688,3338],{"class":72},[62,45690,45691,45694,45696,45698,45701,45703,45705,45707,45710],{"class":64,"line":209},[62,45692,45693],{"class":72}," LocalDate.",[62,45695,3298],{"class":122},[62,45697,2109],{"class":72},[62,45699,45700],{"class":149},"2023",[62,45702,32225],{"class":72},[62,45704,26743],{"class":149},[62,45706,32225],{"class":72},[62,45708,45709],{"class":149},"21",[62,45711,3324],{"class":72},[62,45713,45714,45716,45718,45720,45722,45724,45726,45728,45731],{"class":64,"line":220},[62,45715,45693],{"class":72},[62,45717,3298],{"class":122},[62,45719,2109],{"class":72},[62,45721,45700],{"class":149},[62,45723,32225],{"class":72},[62,45725,26743],{"class":149},[62,45727,32225],{"class":72},[62,45729,45730],{"class":149},"24",[62,45732,3324],{"class":72},[62,45734,45735,45737,45739,45741,45744,45746,45749],{"class":64,"line":226},[62,45736,45693],{"class":72},[62,45738,43561],{"class":122},[62,45740,3229],{"class":72},[62,45742,45743],{"class":122},"minusDays",[62,45745,2109],{"class":72},[62,45747,45748],{"class":149},"180",[62,45750,3324],{"class":72},[62,45752,45753,45755,45757,45759,45761,45763,45766],{"class":64,"line":231},[62,45754,45693],{"class":72},[62,45756,43561],{"class":122},[62,45758,3229],{"class":72},[62,45760,45743],{"class":122},[62,45762,2109],{"class":72},[62,45764,45765],{"class":149},"90",[62,45767,3324],{"class":72},[62,45769,45770,45773],{"class":64,"line":236},[62,45771,45772],{"class":1675}," \"Las Vegas, NV\"",[62,45774,3338],{"class":72},[62,45776,45777,45780],{"class":64,"line":242},[62,45778,45779],{"class":1675}," \"https://springone.io/\"",[62,45781,1133],{"class":72},[62,45783,45784],{"class":64,"line":247},[62,45785,79],{"emptyLinePlaceholder":13},[62,45787,45788,45791,45793],{"class":64,"line":252},[62,45789,45790],{"class":72}," repository.",[62,45792,22562],{"class":122},[62,45794,45795],{"class":72},"(event);\n",[62,45797,45798,45801,45803,45805,45808,45810,45813,45815],{"class":64,"line":257},[62,45799,45800],{"class":72}," log.",[62,45802,12688],{"class":122},[62,45804,2109],{"class":72},[62,45806,45807],{"class":1675},"\"Event created: \"",[62,45809,4507],{"class":68},[62,45811,45812],{"class":72}," event.",[62,45814,12678],{"class":122},[62,45816,1091],{"class":72},[62,45818,45819],{"class":64,"line":271},[62,45820,861],{"class":72},[62,45822,45823],{"class":64,"line":281},[62,45824,2252],{"class":72},[62,45826,45827],{"class":64,"line":286},[62,45828,223],{"class":72},[62,45830,45831],{"class":64,"line":291},[62,45832,79],{"emptyLinePlaceholder":13},[62,45834,45835],{"class":64,"line":296},[62,45836,379],{"class":72},[22,45838,45839],{},"If the repository count is zero (meaning the database is freshly created and empty), we create and save an event to it. Then, we log a message with the event's name.",[22,45841,45842],{},"And that's all! Despite not having set up a database connection, our Docker Compose file will manage to successfully start the database.",[26,45844,45846],{"id":45845},"accessing-postgresql-database","Accessing PostgreSQL Database",[22,45848,45849,45850,2755],{},"We now want to verify that our function ran as expected by checking the database for the new entry. For that, we use ",[59,45851,45852],{},"docker exec -it",[52,45854,45856],{"className":1663,"code":45855,"language":1665,"meta":57,"style":57},"docker exec -it my_postgres_container psql -U my_user -d my_database\n",[59,45857,45858],{"__ignoreMap":57},[62,45859,45860,45862,45865,45868,45871,45874,45877,45880,45882],{"class":64,"line":65},[62,45861,1672],{"class":122},[62,45863,45864],{"class":1675}," exec",[62,45866,45867],{"class":149}," -it",[62,45869,45870],{"class":1675}," my_postgres_container",[62,45872,45873],{"class":1675}," psql",[62,45875,45876],{"class":149}," -U",[62,45878,45879],{"class":1675}," my_user",[62,45881,31723],{"class":149},[62,45883,45884],{"class":1675}," my_database\n",[22,45886,45887,45888,45891,45892,45895,45896,45899],{},"The command above will help you connect to a running PostgreSQL container from your terminal, where you can explore the data. Run ",[59,45889,45890],{},"dt"," to list tables, ",[59,45893,45894],{},"\\c"," to connect to a database or ",[59,45897,45898],{},"select"," statements to fetch data.",[22,45901,45902],{},[653,45903],{"alt":45904,"src":45905},"PSQL","/images/blog/2023/07/17/psql.png",[22,45907,45908],{},"A better way to view and manage your Postgres data is through a tool like pgAdmin.",[26,45910,45912],{"id":45911},"exploring-data-with-pg-admin","Exploring Data with PG Admin",[22,45914,45915,45916,45920],{},"You'll find more on PG Admin at ",[677,45917,45918],{"href":45918,"rel":45919},"https://www.pgadmin.org/",[681],", offering a feature-rich administration and development tool for PostgreSQL. It allows you to comfortably view tables, index, and data within your database.",[22,45922,45923,45924,1266],{},"To incorporate PG Admin into your Docker Compose module, add the following code to your ",[59,45925,45926],{},"docker-compose.yml",[52,45928,45930],{"className":10993,"code":45929,"language":10995,"meta":57,"style":57},"pgadmin:\n image: dpage/pgadmin4:latest\n environment:\n PGADMIN_DEFAULT_EMAIL: admin@localhost.com\n PGADMIN_DEFAULT_PASSWORD: admin\n PGADMIN_LISTEN_PORT: 5050\n ports:\n - '5050:5050'\n",[59,45931,45932,45939,45948,45954,45964,45974,45984,45990],{"__ignoreMap":57},[62,45933,45934,45937],{"class":64,"line":65},[62,45935,45936],{"class":1780},"pgadmin",[62,45938,11005],{"class":72},[62,45940,45941,45943,45945],{"class":64,"line":76},[62,45942,17503],{"class":1780},[62,45944,3696],{"class":72},[62,45946,45947],{"class":1675},"dpage/pgadmin4:latest\n",[62,45949,45950,45952],{"class":64,"line":82},[62,45951,17513],{"class":1780},[62,45953,11005],{"class":72},[62,45955,45956,45959,45961],{"class":64,"line":89},[62,45957,45958],{"class":1780}," PGADMIN_DEFAULT_EMAIL",[62,45960,3696],{"class":72},[62,45962,45963],{"class":1675},"admin@localhost.com\n",[62,45965,45966,45969,45971],{"class":64,"line":95},[62,45967,45968],{"class":1780}," PGADMIN_DEFAULT_PASSWORD",[62,45970,3696],{"class":72},[62,45972,45973],{"class":1675},"admin\n",[62,45975,45976,45979,45981],{"class":64,"line":101},[62,45977,45978],{"class":1780}," PGADMIN_LISTEN_PORT",[62,45980,3696],{"class":72},[62,45982,45983],{"class":149},"5050\n",[62,45985,45986,45988],{"class":64,"line":107},[62,45987,17550],{"class":1780},[62,45989,11005],{"class":72},[62,45991,45992,45994],{"class":64,"line":113},[62,45993,17557],{"class":72},[62,45995,45996],{"class":1675},"'5050:5050'\n",[22,45998,45999,46000,46003,46004,46007,46008,46011],{},"Now, upon running the application and visiting ",[59,46001,46002],{},"localhost:5050",", you'll be welcomed with the login screen for PG Admin. On successful login, hit ",[59,46005,46006],{},"Add New Server",", and use your ",[59,46009,46010],{},"docker inspect"," command to fetch the container's IP address, or set the container's name as the hostname/post.",[52,46013,46015],{"className":1663,"code":46014,"language":1665,"meta":57,"style":57},"docker ps\ndocker inspect 7738686e136d | grep IPAddress\n",[59,46016,46017,46024],{"__ignoreMap":57},[62,46018,46019,46021],{"class":64,"line":65},[62,46020,1672],{"class":122},[62,46022,46023],{"class":1675}," ps\n",[62,46025,46026,46028,46031,46034,46037,46040],{"class":64,"line":76},[62,46027,1672],{"class":122},[62,46029,46030],{"class":1675}," inspect",[62,46032,46033],{"class":1675}," 7738686e136d",[62,46035,46036],{"class":68}," |",[62,46038,46039],{"class":122}," grep",[62,46041,46042],{"class":1675}," IPAddress\n",[22,46044,46045,46046,46049,46050,46052],{},"However, there's a minor flaw. Every time the application is restarted, you'll have to log back into PG Admin. To avoid this, explore the Github repository published by my coworker, Adib Saikali. The repository includes a ",[59,46047,46048],{},"servers.json"," file with login details for PG Admin, and a ",[59,46051,45926],{}," with volumes for PostgreSQL and PG Admin.",[52,46054,46056],{"className":3671,"code":46055,"language":3673,"meta":57,"style":57},"{\n \"Servers\": {\n \"1\": {\n \"Name\": \"Docker Compose\",\n \"Group\": \"Servers\",\n \"Port\": 5432,\n \"Username\": \"dvega\",\n \"Host\": \"sessionz_postgres\",\n \"SSLMode\": \"prefer\",\n \"MaintenanceDB\": \"sessionz\",\n \"PassFile\": \"/tmp/pgpassfile\"\n }\n }\n}\n",[59,46057,46058,46062,46069,46076,46088,46100,46112,46124,46136,46148,46160,46170,46174,46178],{"__ignoreMap":57},[62,46059,46060],{"class":64,"line":65},[62,46061,3680],{"class":72},[62,46063,46064,46067],{"class":64,"line":76},[62,46065,46066],{"class":149}," \"Servers\"",[62,46068,3688],{"class":72},[62,46070,46071,46074],{"class":64,"line":82},[62,46072,46073],{"class":149}," \"1\"",[62,46075,3688],{"class":72},[62,46077,46078,46081,46083,46086],{"class":64,"line":89},[62,46079,46080],{"class":149}," \"Name\"",[62,46082,3696],{"class":72},[62,46084,46085],{"class":1675},"\"Docker Compose\"",[62,46087,3338],{"class":72},[62,46089,46090,46093,46095,46098],{"class":64,"line":95},[62,46091,46092],{"class":149}," \"Group\"",[62,46094,3696],{"class":72},[62,46096,46097],{"class":1675},"\"Servers\"",[62,46099,3338],{"class":72},[62,46101,46102,46105,46107,46110],{"class":64,"line":101},[62,46103,46104],{"class":149}," \"Port\"",[62,46106,3696],{"class":72},[62,46108,46109],{"class":149},"5432",[62,46111,3338],{"class":72},[62,46113,46114,46117,46119,46122],{"class":64,"line":107},[62,46115,46116],{"class":149}," \"Username\"",[62,46118,3696],{"class":72},[62,46120,46121],{"class":1675},"\"dvega\"",[62,46123,3338],{"class":72},[62,46125,46126,46129,46131,46134],{"class":64,"line":113},[62,46127,46128],{"class":149}," \"Host\"",[62,46130,3696],{"class":72},[62,46132,46133],{"class":1675},"\"sessionz_postgres\"",[62,46135,3338],{"class":72},[62,46137,46138,46141,46143,46146],{"class":64,"line":129},[62,46139,46140],{"class":149}," \"SSLMode\"",[62,46142,3696],{"class":72},[62,46144,46145],{"class":1675},"\"prefer\"",[62,46147,3338],{"class":72},[62,46149,46150,46153,46155,46158],{"class":64,"line":134},[62,46151,46152],{"class":149}," \"MaintenanceDB\"",[62,46154,3696],{"class":72},[62,46156,46157],{"class":1675},"\"sessionz\"",[62,46159,3338],{"class":72},[62,46161,46162,46165,46167],{"class":64,"line":156},[62,46163,46164],{"class":149}," \"PassFile\"",[62,46166,3696],{"class":72},[62,46168,46169],{"class":1675},"\"/tmp/pgpassfile\"\n",[62,46171,46172],{"class":64,"line":161},[62,46173,223],{"class":72},[62,46175,46176],{"class":64,"line":167},[62,46177,3731],{"class":72},[62,46179,46180],{"class":64,"line":173},[62,46181,379],{"class":72},[22,46183,46184],{},"Now each time you restart the application you will be automatically logged into pgAdmin.",[22,46186,46187],{},[653,46188],{"alt":46189,"src":46190},"pgAdmin","/images/blog/2023/07/17/pgadmin.png",[26,46192,1499],{"id":1498},[22,46194,46195],{},"We've covered the Docker Compose module in Spring Boot 3.1 and learned how to connect to a Postgres database from our application. We also explored how to use psql in the shell and pgAdmin to manage our database smoothly. This Docker Compose Module will streamline your development process, making it much simpler to view and manage your data.",[22,46197,46198],{},"Thanks Adib, for the invaluable Github reposit",[1527,46200,46201],{},"html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}html pre.shiki code .s4-Ip, html code.shiki .s4-Ip{--shiki-default:#6A737D;--shiki-dark:#6A737D;--shiki-sepia:#6A737D}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .spoOn, html code.shiki .spoOn{--shiki-default:#E36209;--shiki-dark:#FFAB70;--shiki-sepia:#E36209}html pre.shiki code .suaqH, html code.shiki .suaqH{--shiki-default:#22863A;--shiki-dark:#85E89D;--shiki-sepia:#22863A}",{"title":57,"searchDepth":76,"depth":76,"links":46203},[46204,46205,46206,46207,46208,46209,46210],{"id":45282,"depth":76,"text":45283},{"id":45305,"depth":76,"text":45306},{"id":45326,"depth":76,"text":45327},{"id":45516,"depth":76,"text":45517},{"id":45845,"depth":76,"text":45846},{"id":45911,"depth":76,"text":45912},{"id":1498,"depth":76,"text":1499},{"_id":46212,"path":46213,"title":46214,"description":46215,"meta":46216,"body":46221},"content/blog/2023/07/13/graphql-schema-mapping-inspection.md","/blog/2023/07/13/graphql-schema-mapping-inspection","Spring for GraphQL Schema Mapping Inspection Report","Join VMware's Spring Developer Advocate, Dan Vega, as he explores the exciting new releases in Spring for GraphQL. Dive into the enhanced features of the inspection report, walk through how to create a project, and more!",{"slug":46217,"date":46218,"published":13,"tags":46219,"author":17,"cover":46220,"excerpt":-1},"graphql-schema-mapping-inspection","2023-07-13T15:00:00.000Z",[2925,8507],"./graphql-schema-inspection.png",{"type":19,"value":46222,"toc":47059},[46223,46226,46229,46232,46236,46239,46242,46245,46248,46252,46259,46264,46267,46271,46285,46311,46334,46393,46408,46416,46446,46552,46625,46628,46632,46651,46676,46681,46685,46688,46702,46713,46718,46820,46836,46981,46991,47046,47048,47051,47054,47056],[22,46224,46225],{},"If you have been following this channel, you know that Im am a big fan of GraphQL, especially when paired with Spring. Today, we will delve into some exciting new releases in Spring for GraphQL and share how they can improve your development workflow.",[22,46227,46228],{},"I've created a number of tutorials centred on these recent updates but thought it appropriate to kick things off by examining the inspection report. A lesser-known feature to many, the inspection report can be an absolute game changer in how you troubleshoot issues in your GraphQL schema.",[22,46230,46231],{},"Let's dive in!",[26,46233,46235],{"id":46234},"what-is-the-inspection-report-in-graphql","What is the Inspection Report in GraphQL?",[22,46237,46238],{},"While working with GraphQL, you often start with writing your schema, in what's known as the schema-first approach. You may write a controller with methods typically referred to as \"data fetchers\" or \"data resolvers\" that pair with your schema. There are certain situations where mismatches can occur between the schema and the class on the Java side or between the schema and controller methods. These mismatches tend to create confusion since it might be challenging to pinpoint where things went awry.",[22,46240,46241],{},"Often, it's difficult to determine if the returned null is genuine because no data was found, or it resulted from a missing mapping. This is where the inspection report swoops in to save the day, providing a seamless way to troubleshoot these inconsistencies.",[22,46243,46244],{},"By enabling an opt-in inspection feature, you can configure your system to generate a report delineating what's happening in your schema. This, in turn, allows you to spot what's missing quickly and efficiently.",[22,46246,46247],{},"Today, we'll create a sample application around the idea of conference events and sessions using this fabulous feature. Our mission will be to establish a simple project, thereby illustrating how to use the inspection report and how it can significantly improve your GraphQL debugging experience.",[26,46249,46251],{"id":46250},"beginning-our-project-the-building-blocks","Beginning Our Project: The Building Blocks",[22,46253,46254,46255,46258],{},"To kick off our project, we'll use ",[677,46256,24606],{"href":1744,"rel":46257},[681],". We will choose a Maven project using Java, with Web and Spring for GraphQL as our only dependencies.",[22,46260,46261],{},[653,46262],{"alt":24606,"src":46263},"/images/blog/2023/07/13/spring-init.png",[22,46265,46266],{},"Please note that the version used in this blog is 3.1.1, which is the latest version at the time of writing. With our project generated and opened in our favorite ID or editor, we will commence with writing some code.",[636,46268,46270],{"id":46269},"adding-a-schema","Adding a Schema",[22,46272,46273,46274,46277,46278,46281,46282,46284],{},"Create a new file called ",[59,46275,46276],{},"schema.graphqls"," in ",[59,46279,46280],{},"/src/main/resources/graphql"," which will contain our schema. In terms of creating a schema, we have the freedom to start with any root-level operation types, such as query, mutation, and subscription. In this case, we'll begin by modelling a conference event as an object type, which we'll name ",[59,46283,45499],{},". It features properties such as:",[915,46286,46287,46289,46291,46293,46296,46299,46302,46305,46308],{},[37,46288,8845],{},[37,46290,34914],{},[37,46292,11939],{},[37,46294,46295],{},"Start date",[37,46297,46298],{},"End date",[37,46300,46301],{},"CFP start date (Call for Proposals start date)",[37,46303,46304],{},"CFP end date",[37,46306,46307],{},"Location",[37,46309,46310],{},"Website",[22,46312,46313,46314,46316,46317,19931,46319,46322,46323,19931,46326,46329,46330,46333],{},"The event details are expected to be of a certain type. For instance, the ",[59,46315,8845],{}," is an identifier type, ",[59,46318,3107],{},[59,46320,46321],{},"location"," are strings, ",[59,46324,46325],{},"start date",[59,46327,46328],{},"end date"," are dates, while ",[59,46331,46332],{},"website"," is a URL.",[52,46335,46337],{"className":8822,"code":46336,"language":8824,"meta":57,"style":57},"type Event {\n id: ID!\n name: String!\n description: String\n startDate: Date\n endDate: Date\n cfpStartDate: Date\n cfpEndDate: Date\n location: String\n website: URL\n}\n",[59,46338,46339,46344,46349,46354,46359,46364,46369,46374,46379,46384,46389],{"__ignoreMap":57},[62,46340,46341],{"class":64,"line":65},[62,46342,46343],{},"type Event {\n",[62,46345,46346],{"class":64,"line":76},[62,46347,46348],{}," id: ID!\n",[62,46350,46351],{"class":64,"line":82},[62,46352,46353],{}," name: String!\n",[62,46355,46356],{"class":64,"line":89},[62,46357,46358],{}," description: String\n",[62,46360,46361],{"class":64,"line":95},[62,46362,46363],{}," startDate: Date\n",[62,46365,46366],{"class":64,"line":101},[62,46367,46368],{}," endDate: Date\n",[62,46370,46371],{"class":64,"line":107},[62,46372,46373],{}," cfpStartDate: Date\n",[62,46375,46376],{"class":64,"line":113},[62,46377,46378],{}," cfpEndDate: Date\n",[62,46380,46381],{"class":64,"line":129},[62,46382,46383],{}," location: String\n",[62,46385,46386],{"class":64,"line":134},[62,46387,46388],{}," website: URL\n",[62,46390,46391],{"class":64,"line":156},[62,46392,379],{},[22,46394,46395,46396,19931,46399,46402,46403,2755],{},"Notice that the ",[59,46397,46398],{},"Date",[59,46400,46401],{},"URL"," are not built-in types which means we need to declare them as extended scalars. If you need more examples on how to handle extended scalar types in GraphQL, check out this ",[677,46404,46407],{"href":46405,"rel":46406},"https://youtu.be/ooknmgr4WiA",[681],"previous video tutorial",[22,46409,46410,46411,19931,46413,46415],{},"Here's some code on how you can add the ",[59,46412,46398],{},[59,46414,46401],{}," extended scalars.",[52,46417,46419],{"className":1769,"code":46418,"language":1771,"meta":57,"style":57},"\u003Cdependency>\n \u003CgroupId>com.graphql-java\u003C/groupId>\n \u003CartifactId>graphql-java-extended-scalars\u003C/artifactId>\n \u003Cversion>20.0\u003C/version>\n\u003C/dependency>\n",[59,46420,46421,46426,46431,46436,46441],{"__ignoreMap":57},[62,46422,46423],{"class":64,"line":65},[62,46424,46425],{},"\u003Cdependency>\n",[62,46427,46428],{"class":64,"line":76},[62,46429,46430],{}," \u003CgroupId>com.graphql-java\u003C/groupId>\n",[62,46432,46433],{"class":64,"line":82},[62,46434,46435],{}," \u003CartifactId>graphql-java-extended-scalars\u003C/artifactId>\n",[62,46437,46438],{"class":64,"line":89},[62,46439,46440],{}," \u003Cversion>20.0\u003C/version>\n",[62,46442,46443],{"class":64,"line":95},[62,46444,46445],{},"\u003C/dependency>\n",[52,46447,46449],{"className":54,"code":46448,"language":56,"meta":57,"style":57},"@Configuration\npublic class GraphQlConfiguration {\n\n private static final Logger log = LoggerFactory.getLogger(GraphQlConfiguration.class);\n\n @Bean\n public RuntimeWiringConfigurer runtimeWiringConfigurer() {\n return wiringBuilder -> wiringBuilder\n .scalar(ExtendedScalars.Date)\n .scalar(ExtendedScalars.Url);\n }\n}\n",[59,46450,46451,46457,46468,46472,46491,46495,46501,46513,46525,46535,46544,46548],{"__ignoreMap":57},[62,46452,46453,46455],{"class":64,"line":65},[62,46454,942],{"class":72},[62,46456,11133],{"class":68},[62,46458,46459,46461,46463,46466],{"class":64,"line":76},[62,46460,116],{"class":68},[62,46462,119],{"class":68},[62,46464,46465],{"class":122}," GraphQlConfiguration",[62,46467,126],{"class":72},[62,46469,46470],{"class":64,"line":82},[62,46471,79],{"emptyLinePlaceholder":13},[62,46473,46474,46476,46478,46480,46482,46484,46486,46488],{"class":64,"line":89},[62,46475,137],{"class":68},[62,46477,2101],{"class":68},[62,46479,458],{"class":68},[62,46481,3061],{"class":72},[62,46483,146],{"class":68},[62,46485,3066],{"class":72},[62,46487,3069],{"class":122},[62,46489,46490],{"class":72},"(GraphQlConfiguration.class);\n",[62,46492,46493],{"class":64,"line":95},[62,46494,79],{"emptyLinePlaceholder":13},[62,46496,46497,46499],{"class":64,"line":101},[62,46498,2143],{"class":72},[62,46500,2146],{"class":68},[62,46502,46503,46505,46508,46511],{"class":64,"line":107},[62,46504,194],{"class":68},[62,46506,46507],{"class":72}," RuntimeWiringConfigurer ",[62,46509,46510],{"class":122},"runtimeWiringConfigurer",[62,46512,206],{"class":72},[62,46514,46515,46517,46520,46522],{"class":64,"line":113},[62,46516,360],{"class":68},[62,46518,46519],{"class":72}," wiringBuilder ",[62,46521,800],{"class":68},[62,46523,46524],{"class":72}," wiringBuilder\n",[62,46526,46527,46529,46532],{"class":64,"line":129},[62,46528,2418],{"class":72},[62,46530,46531],{"class":122},"scalar",[62,46533,46534],{"class":72},"(ExtendedScalars.Date)\n",[62,46536,46537,46539,46541],{"class":64,"line":134},[62,46538,2418],{"class":72},[62,46540,46531],{"class":122},[62,46542,46543],{"class":72},"(ExtendedScalars.Url);\n",[62,46545,46546],{"class":64,"line":156},[62,46547,223],{"class":72},[62,46549,46550],{"class":64,"line":161},[62,46551,379],{"class":72},[52,46553,46555],{"className":8822,"code":46554,"language":8824,"meta":57,"style":57},"scalar Date @specifiedBy(url:\"https://tools.ietf.org/html/rfc3339\")\nscalar Url @specifiedBy(url:\"https://www.w3.org/Addressing/URL/url-spec.txt\")\ntype Event {\n id: ID!\n name: String!\n description: String!\n startDate: Date!\n endDate: Date!\n cfpStartDate: Date!\n cfpEndDate: Date!\n location: String\n website: Url\n sessions(first: Int,last: Int,before: String,after: String): SessionConnection\n}\n\n",[59,46556,46557,46562,46567,46571,46576,46581,46586,46591,46596,46601,46606,46611,46616,46621],{"__ignoreMap":57},[62,46558,46559],{"class":64,"line":65},[62,46560,46561],{},"scalar Date @specifiedBy(url:\"https://tools.ietf.org/html/rfc3339\")\n",[62,46563,46564],{"class":64,"line":76},[62,46565,46566],{},"scalar Url @specifiedBy(url:\"https://www.w3.org/Addressing/URL/url-spec.txt\")\n",[62,46568,46569],{"class":64,"line":82},[62,46570,46343],{},[62,46572,46573],{"class":64,"line":89},[62,46574,46575],{}," id: ID!\n",[62,46577,46578],{"class":64,"line":95},[62,46579,46580],{}," name: String!\n",[62,46582,46583],{"class":64,"line":101},[62,46584,46585],{}," description: String!\n",[62,46587,46588],{"class":64,"line":107},[62,46589,46590],{}," startDate: Date!\n",[62,46592,46593],{"class":64,"line":113},[62,46594,46595],{}," endDate: Date!\n",[62,46597,46598],{"class":64,"line":129},[62,46599,46600],{}," cfpStartDate: Date!\n",[62,46602,46603],{"class":64,"line":134},[62,46604,46605],{}," cfpEndDate: Date!\n",[62,46607,46608],{"class":64,"line":156},[62,46609,46610],{}," location: String\n",[62,46612,46613],{"class":64,"line":161},[62,46614,46615],{}," website: Url\n",[62,46617,46618],{"class":64,"line":167},[62,46619,46620],{}," sessions(first: Int,last: Int,before: String,after: String): SessionConnection\n",[62,46622,46623],{"class":64,"line":173},[62,46624,379],{},[22,46626,46627],{},"At this point, we have a schema in place for our GraphQL API.",[26,46629,46631],{"id":46630},"java-side-adding-a-spring-record","Java Side: Adding a Spring Record",[22,46633,46634,46635,46638,46639,46642,46643,46645,46646,46648,46649,2755],{},"To mirror the GraphQL schema in our Java code, we create a new package for events, ",[59,46636,46637],{},"com.example.demo.event"," package. This will contain a ",[59,46640,46641],{},"spring record"," named ",[59,46644,45499],{},", which will contain two properties initially: integer ",[59,46647,6283],{}," and string ",[59,46650,3107],{},[52,46652,46654],{"className":54,"code":46653,"language":56,"meta":57,"style":57},"public record Event(@NotBlank Integer id,@NotBlank String name) {}\n",[59,46655,46656],{"__ignoreMap":57},[62,46657,46658,46660,46662,46664,46666,46668,46671,46673],{"class":64,"line":65},[62,46659,116],{"class":68},[62,46661,2996],{"class":68},[62,46663,45353],{"class":122},[62,46665,2475],{"class":72},[62,46667,22311],{"class":68},[62,46669,46670],{"class":72}," Integer id,@",[62,46672,22311],{"class":68},[62,46674,46675],{"class":72}," String name) {}\n",[22,46677,46678,46679,2755],{},"When we define a query and then run the application, we notice that everything starts up correctly with no obvious indications of any mismatches. However, when we run some query requests, they return ",[59,46680,3256],{},[26,46682,46684],{"id":46683},"how-to-enable-graphql-inspection-reports-unmasking-mismatches","How to Enable GraphQL Inspection Reports: Unmasking Mismatches",[22,46686,46687],{},"To avoid confusion and make diagnosing mismatches easier, we can enable the inspection feature of GraphQL to check that all schema fields have a corresponding data fetcher. There are two main steps involved in enabling inspection reports:",[34,46689,46690],{},[37,46691,46692,46693,34867,46695,46697,46698,46701],{},"Enable introspection in the ",[59,46694,17005],{},[59,46696,1265],{}," file by setting the property ",[59,46699,46700],{},"spring.graphql.schema.introspection.enabled"," to true:",[52,46703,46705],{"className":1269,"code":46704,"language":1271,"meta":57,"style":57},"spring.graphql.schema.introspection.enabled=true\n",[59,46706,46707],{"__ignoreMap":57},[62,46708,46709,46711],{"class":64,"line":65},[62,46710,46700],{"class":68},[62,46712,1281],{"class":72},[34,46714,46715],{"start":76},[37,46716,46717],{},"Add a custom bean to the source builder of your GraphQL API which will handle the report. You access the report and choose what to do with it. In our case, we'll log it as a string.",[52,46719,46721],{"className":54,"code":46720,"language":56,"meta":57,"style":57},"@Configuration\npublic class GraphQlConfiguration {\n\n private static final Logger log = LoggerFactory.getLogger(GraphQlConfiguration.class);\n\n @Bean\n GraphQlSourceBuilderCustomizer inspectionCustomizer() {\n return source -> source.inspectSchemaMappings(report -> log.info(report.toString()));\n }\n}\n",[59,46722,46723,46729,46739,46743,46761,46765,46771,46781,46812,46816],{"__ignoreMap":57},[62,46724,46725,46727],{"class":64,"line":65},[62,46726,942],{"class":72},[62,46728,11133],{"class":68},[62,46730,46731,46733,46735,46737],{"class":64,"line":76},[62,46732,116],{"class":68},[62,46734,119],{"class":68},[62,46736,46465],{"class":122},[62,46738,126],{"class":72},[62,46740,46741],{"class":64,"line":82},[62,46742,79],{"emptyLinePlaceholder":13},[62,46744,46745,46747,46749,46751,46753,46755,46757,46759],{"class":64,"line":89},[62,46746,137],{"class":68},[62,46748,2101],{"class":68},[62,46750,458],{"class":68},[62,46752,3061],{"class":72},[62,46754,146],{"class":68},[62,46756,3066],{"class":72},[62,46758,3069],{"class":122},[62,46760,46490],{"class":72},[62,46762,46763],{"class":64,"line":95},[62,46764,79],{"emptyLinePlaceholder":13},[62,46766,46767,46769],{"class":64,"line":101},[62,46768,2143],{"class":72},[62,46770,2146],{"class":68},[62,46772,46773,46776,46779],{"class":64,"line":107},[62,46774,46775],{"class":72}," GraphQlSourceBuilderCustomizer ",[62,46777,46778],{"class":122},"inspectionCustomizer",[62,46780,206],{"class":72},[62,46782,46783,46785,46788,46790,46793,46796,46799,46801,46803,46805,46808,46810],{"class":64,"line":113},[62,46784,360],{"class":68},[62,46786,46787],{"class":72}," source ",[62,46789,800],{"class":68},[62,46791,46792],{"class":72}," source.",[62,46794,46795],{"class":122},"inspectSchemaMappings",[62,46797,46798],{"class":72},"(report ",[62,46800,800],{"class":68},[62,46802,39905],{"class":72},[62,46804,12688],{"class":122},[62,46806,46807],{"class":72},"(report.",[62,46809,23175],{"class":122},[62,46811,5110],{"class":72},[62,46813,46814],{"class":64,"line":129},[62,46815,223],{"class":72},[62,46817,46818],{"class":64,"line":134},[62,46819,379],{"class":72},[22,46821,46822,46823,46826,46827,19931,46829,46831,46832,46835],{},"The complete configuration of the ",[59,46824,46825],{},"GraphQLConfig"," class also includes declaring exposure of extended scalars, such as ",[59,46828,46398],{},[59,46830,46401],{},", using a ",[59,46833,46834],{},"RuntimeWiringConfigurer"," bean. Here's how that entire class could look like:",[52,46837,46839],{"className":54,"code":46838,"language":56,"meta":57,"style":57},"@Configuration\npublic class GraphQlConfiguration {\n\n private static final Logger log = LoggerFactory.getLogger(GraphQlConfiguration.class);\n\n @Bean\n public RuntimeWiringConfigurer runtimeWiringConfigurer() {\n return wiringBuilder -> wiringBuilder\n .scalar(ExtendedScalars.Date)\n .scalar(ExtendedScalars.Url);\n }\n\n @Bean\n GraphQlSourceBuilderCustomizer inspectionCustomizer() {\n return source -> source.inspectSchemaMappings(report -> log.info(report.toString()));\n }\n}\n",[59,46840,46841,46847,46857,46861,46879,46883,46889,46899,46909,46917,46925,46929,46933,46939,46947,46973,46977],{"__ignoreMap":57},[62,46842,46843,46845],{"class":64,"line":65},[62,46844,942],{"class":72},[62,46846,11133],{"class":68},[62,46848,46849,46851,46853,46855],{"class":64,"line":76},[62,46850,116],{"class":68},[62,46852,119],{"class":68},[62,46854,46465],{"class":122},[62,46856,126],{"class":72},[62,46858,46859],{"class":64,"line":82},[62,46860,79],{"emptyLinePlaceholder":13},[62,46862,46863,46865,46867,46869,46871,46873,46875,46877],{"class":64,"line":89},[62,46864,137],{"class":68},[62,46866,2101],{"class":68},[62,46868,458],{"class":68},[62,46870,3061],{"class":72},[62,46872,146],{"class":68},[62,46874,3066],{"class":72},[62,46876,3069],{"class":122},[62,46878,46490],{"class":72},[62,46880,46881],{"class":64,"line":95},[62,46882,79],{"emptyLinePlaceholder":13},[62,46884,46885,46887],{"class":64,"line":101},[62,46886,2143],{"class":72},[62,46888,2146],{"class":68},[62,46890,46891,46893,46895,46897],{"class":64,"line":107},[62,46892,194],{"class":68},[62,46894,46507],{"class":72},[62,46896,46510],{"class":122},[62,46898,206],{"class":72},[62,46900,46901,46903,46905,46907],{"class":64,"line":113},[62,46902,360],{"class":68},[62,46904,46519],{"class":72},[62,46906,800],{"class":68},[62,46908,46524],{"class":72},[62,46910,46911,46913,46915],{"class":64,"line":129},[62,46912,2418],{"class":72},[62,46914,46531],{"class":122},[62,46916,46534],{"class":72},[62,46918,46919,46921,46923],{"class":64,"line":134},[62,46920,2418],{"class":72},[62,46922,46531],{"class":122},[62,46924,46543],{"class":72},[62,46926,46927],{"class":64,"line":156},[62,46928,223],{"class":72},[62,46930,46931],{"class":64,"line":161},[62,46932,79],{"emptyLinePlaceholder":13},[62,46934,46935,46937],{"class":64,"line":167},[62,46936,2143],{"class":72},[62,46938,2146],{"class":68},[62,46940,46941,46943,46945],{"class":64,"line":173},[62,46942,46775],{"class":72},[62,46944,46778],{"class":122},[62,46946,206],{"class":72},[62,46948,46949,46951,46953,46955,46957,46959,46961,46963,46965,46967,46969,46971],{"class":64,"line":179},[62,46950,360],{"class":68},[62,46952,46787],{"class":72},[62,46954,800],{"class":68},[62,46956,46792],{"class":72},[62,46958,46795],{"class":122},[62,46960,46798],{"class":72},[62,46962,800],{"class":68},[62,46964,39905],{"class":72},[62,46966,12688],{"class":122},[62,46968,46807],{"class":72},[62,46970,23175],{"class":122},[62,46972,5110],{"class":72},[62,46974,46975],{"class":64,"line":185},[62,46976,223],{"class":72},[62,46978,46979],{"class":64,"line":191},[62,46980,379],{"class":72},[22,46982,46983,46984,46987,46988,46990],{},"With the inspection report enabled, it becomes straightforward to identify that there is an \"unmapped field\" ",[59,46985,46986],{},"events"," in our query. We can infer that the query ",[59,46989,46986],{}," does not map to any controller method in our schema. If you run the application you should see the report logged to the console:",[52,46992,46994],{"className":1663,"code":46993,"language":1665,"meta":57,"style":57},"Unmapped fields: {Event=[description, startDate, endDate, cfpStartDate, cfpEndDate, location, website]}\nUnmapped registrations: {}\nSkipped types: []\n",[59,46995,46996,47025,47035],{"__ignoreMap":57},[62,46997,46998,47001,47004,47007,47010,47013,47016,47019,47022],{"class":64,"line":65},[62,46999,47000],{"class":122},"Unmapped",[62,47002,47003],{"class":1675}," fields:",[62,47005,47006],{"class":1675}," {Event=[description,",[62,47008,47009],{"class":1675}," startDate,",[62,47011,47012],{"class":1675}," endDate,",[62,47014,47015],{"class":1675}," cfpStartDate,",[62,47017,47018],{"class":1675}," cfpEndDate,",[62,47020,47021],{"class":1675}," location,",[62,47023,47024],{"class":1675}," website]}\n",[62,47026,47027,47029,47032],{"class":64,"line":76},[62,47028,47000],{"class":122},[62,47030,47031],{"class":1675}," registrations:",[62,47033,47034],{"class":1675}," {}\n",[62,47036,47037,47040,47043],{"class":64,"line":82},[62,47038,47039],{"class":122},"Skipped",[62,47041,47042],{"class":1675}," types:",[62,47044,47045],{"class":72}," []\n",[26,47047,32553],{"id":32552},[22,47049,47050],{},"While it may seem subtle, the inspection report is an incredibly powerful feature in Spring for GraphQL. It enhances the ease of troubleshooting potential mismatches between your GraphQL schema and Java code, saving you lots of time in your debugging.",[22,47052,47053],{},"I'll be creating more tutorials on Spring GraphQL, including best practices on paging and sorting. If you find this interest in these topics, please let me know.",[22,47055,1525],{},[1527,47057,47058],{},"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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}",{"title":57,"searchDepth":76,"depth":76,"links":47060},[47061,47062,47065,47066,47067],{"id":46234,"depth":76,"text":46235},{"id":46250,"depth":76,"text":46251,"children":47063},[47064],{"id":46269,"depth":82,"text":46270},{"id":46630,"depth":76,"text":46631},{"id":46683,"depth":76,"text":46684},{"id":32552,"depth":76,"text":32553},{"_id":47069,"path":47070,"title":47071,"description":47072,"meta":47073,"body":47079},"content/blog/2023/06/30/aws-lambda-spring-boot-3.md","/blog/2023/06/30/aws-lambda-spring-boot-3","Deploying Spring Boot 3 Applications to AWS Lambda","Discover effective solutions for hosting your personal spring boot projects without hefty costs. Boost your skills with Dan Vega, your spring developer advocate, while exploring the capabilities of AWS Lambda.",{"slug":47074,"date":47075,"published":13,"tags":47076,"author":17,"cover":47078,"excerpt":-1},"aws-lambda-spring-boot-3","2023-06-30T15:00:00.000Z",[2925,47077],"AWS","./aws-lambda-spring-boot-3-thumbnail.png",{"type":19,"value":47080,"toc":48192},[47081,47084,47088,47091,47094,47098,47101,47104,47107,47111,47114,47122,47125,47131,47186,47204,47207,47219,47243,47255,47673,47680,47732,47883,48080,48083,48087,48090,48102,48108,48111,48117,48120,48123,48134,48137,48140,48143,48147,48150,48153,48160,48166,48169,48172,48175,48177,48180,48183,48186,48189],[22,47082,47083],{},"Welcome to this detailed and comprehensive blog post about deploying Spring Boot applications to AWS Lambda! If you've been searching for a solution to hosting your personal Spring Boot projects without breaking the bank, you're in the right place. In this blog post, we'll cover everything you need to know about deploying your Spring Boot apps to AWS Lambda, including why you might want to do it, the benefits of serverless computing, and a step-by-step guide on how to get started.",[26,47085,47087],{"id":47086},"why-deploy-to-aws-lambda","Why Deploy to AWS Lambda?",[22,47089,47090],{},"Before we dive into the details of deploying to AWS Lambda, let's take a moment to discuss why you might want to choose this approach. Traditionally, when deploying a Spring Boot application, you would package it as a JAR file and deploy it to a server or containerized it using Docker and deploy it to Kubernetes. While this approach works well, it comes with the responsibility of managing and maintaining your own servers, including patching, security, and scaling.",[22,47092,47093],{},"AWS Lambda, on the other hand, is a serverless compute service that allows you to run code without the need to provision or manage servers. With AWS Lambda, you only pay for the time that you consume, which makes it an ideal solution for personal projects or applications with variable traffic that can scale to zero. By deploying your Spring Boot applications to AWS Lambda, you can offload the burden of server management and focus on developing and delivering your applications.",[26,47095,47097],{"id":47096},"the-solution-aws-lambda","The Solution: AWS Lambda",[22,47099,47100],{},"So, what is the solution to your hosting problem? The answer lies in AWS Lambda. In this blog post, we'll be exploring how to deploy entire Spring Boot applications to AWS Lambda, not just individual functions as service, which we've covered in the past.",[22,47102,47103],{},"By leveraging AWS Lambda, you can deploy your Spring Boot applications as serverless functions, taking advantage of the benefits of serverless computing, such as automatic scaling, high availability, and reduced costs. As we mentioned earlier, AWS Lambda is a pay-per-use service, and even better, the free tier includes 1 million free requests per month. This means that for most personal projects, you can deploy them to AWS Lambda without incurring any additional costs.",[22,47105,47106],{},"It's important to note that while we'll be focusing on personal projects in this blog post, AWS Lambda is a versatile solution that can be used for a variety of use cases. Any application with variable traffic that can scale to zero is a great candidate for AWS Lambda.",[26,47108,47110],{"id":47109},"getting-started-with-aws-lambda-and-spring-boot","Getting Started with AWS Lambda and Spring Boot",[22,47112,47113],{},"Now that we understand why AWS Lambda is a great solution for hosting our personal Spring Boot projects, let's dive into the details of how we can deploy our applications to AWS Lambda.",[22,47115,47116,47117,47121],{},"In the ",[677,47118,33885],{"href":47119,"rel":47120},"https://github.com/danvega/serverless-api",[681]," associated with this blog post, you'll find all the code and resources you need to follow along. The first step is to create a new Spring Boot project using a Maven archetype specifically designed for deploying Spring Boot applications to AWS Lambda.",[22,47123,47124],{},"To create the project using IntelliJ, you can use the Maven archetype generator. Alternatively, you can run the following command from the command line:",[22,47126,47127],{},[653,47128],{"alt":47129,"src":47130},"Maven Archetype","/images/blog/2023/06/30/6Kpi6DOLQUkVjHS53ssf-378.57.png",[52,47132,47134],{"className":1663,"code":47133,"language":1665,"meta":57,"style":57},"mvn archetype:generate \\-DarchetypeGroupId=com.amazonaws.serverless.archetypes \\-DarchetypeArtifactId=aws-serverless-spring-java-archetype \\-DarchetypeVersion=1.0.0 \\-DgroupId=com.example \\-DartifactId=my-spring-boot-app \\-Dversion=1.0.0 \\-DinteractiveMode=false\n",[59,47135,47136],{"__ignoreMap":57},[62,47137,47138,47140,47143,47146,47149,47151,47154,47156,47159,47162,47164,47167,47169,47172,47174,47177,47179,47181,47184],{"class":64,"line":65},[62,47139,3639],{"class":122},[62,47141,47142],{"class":1675}," archetype:generate",[62,47144,47145],{"class":149}," \\-",[62,47147,47148],{"class":1675},"DarchetypeGroupId=com.amazonaws.serverless.archetypes",[62,47150,47145],{"class":149},[62,47152,47153],{"class":1675},"DarchetypeArtifactId=aws-serverless-spring-java-archetype",[62,47155,47145],{"class":149},[62,47157,47158],{"class":1675},"DarchetypeVersion=",[62,47160,47161],{"class":149},"1.0.0",[62,47163,47145],{"class":149},[62,47165,47166],{"class":1675},"DgroupId=com.example",[62,47168,47145],{"class":149},[62,47170,47171],{"class":1675},"DartifactId=my-spring-boot-app",[62,47173,47145],{"class":149},[62,47175,47176],{"class":1675},"Dversion=",[62,47178,47161],{"class":149},[62,47180,47145],{"class":149},[62,47182,47183],{"class":1675},"DinteractiveMode=",[62,47185,40782],{"class":149},[22,47187,47188,47189,47191,47192,47195,47196,47199,47200,47203],{},"Once you've created the project, you'll notice a few important dependencies in the ",[59,47190,1765],{}," file. These dependencies include ",[59,47193,47194],{},"aws-serverless-java-container-springboot3",", which enables running Spring Boot applications on AWS Lambda, and ",[59,47197,47198],{},"spring-boot-starter-web",", which provides the necessary components for building a REST API. Additionally, the ",[59,47201,47202],{},"assembly-zip"," profile is used to package the application as a ZIP file.",[22,47205,47206],{},"Next, we're going to start building out our Spring Boot REST API. In this example, we'll be creating a blog post API. Our API will have CRUD (Create, Read, Update, Delete) functionality for managing blog posts.",[22,47208,47209,47210,47212,47213,976,47215,4201,47217,2755],{},"We'll start by creating a Java record called ",[59,47211,38700],{},", which represents a blog post. This record will have properties for ",[59,47214,6283],{},[59,47216,3196],{},[59,47218,11414],{},[52,47220,47222],{"className":54,"code":47221,"language":56,"meta":57,"style":57},"public record Post(Integer id, String title, String body) {\n\n}\n",[59,47223,47224,47235,47239],{"__ignoreMap":57},[62,47225,47226,47228,47230,47232],{"class":64,"line":65},[62,47227,116],{"class":68},[62,47229,2996],{"class":68},[62,47231,41321],{"class":122},[62,47233,47234],{"class":72},"(Integer id, String title, String body) {\n",[62,47236,47237],{"class":64,"line":76},[62,47238,79],{"emptyLinePlaceholder":13},[62,47240,47241],{"class":64,"line":82},[62,47242,379],{"class":72},[22,47244,47245,47246,47248,47249,47251,47252,47254],{},"Next, we'll create a new class called ",[59,47247,41342],{},", which will be annotated with ",[59,47250,40266],{},". This class will handle requests related to blog posts, such as retrieving all posts, getting a single post by ID, creating a new post, updating an existing post, and deleting a post. The ",[59,47253,41342],{}," class will have an in-memory collection of posts, which we'll initialize with some dummy data.",[52,47256,47258],{"className":54,"code":47257,"language":56,"meta":57,"style":57},"@RestController\n@RequestMapping(\"/api/posts\")\npublic class PostController {\n\n private List\u003CPost> posts = new ArrayList\u003C>();\n\n @GetMapping\n List\u003CPost> findAll() {\n return posts;\n }\n\n @GetMapping(\"/{id}\")\n Optional\u003CPost> findById(@PathVariable Integer id) {\n return Optional.ofNullable(posts\n .stream()\n .filter(post -> post.id().equals(id))\n .findFirst()\n .orElseThrow(() -> new PostNotFoundException(\"Post with id: \" + id + \" not found.\")));\n }\n\n @PostMapping\n void create(@RequestBody Post post) {\n posts.add(post);\n }\n\n @PutMapping(\"/{id}\")\n void update(@RequestBody Post post, @PathVariable Integer id) {\n posts.stream()\n .filter(p -> p.id().equals(id))\n .findFirst()\n .ifPresent(value -> posts.set(posts.indexOf(value),post));\n }\n\n @DeleteMapping(\"/{id}\")\n void delete(@PathVariable Integer id) {\n posts.removeIf(post -> post.id().equals(id));\n }\n\n}\n",[59,47259,47260,47266,47278,47288,47292,47309,47313,47320,47332,47339,47343,47347,47359,47379,47390,47398,47419,47427,47461,47465,47469,47475,47491,47500,47504,47508,47520,47544,47552,47572,47580,47605,47609,47613,47625,47641,47661,47665,47669],{"__ignoreMap":57},[62,47261,47262,47264],{"class":64,"line":65},[62,47263,942],{"class":72},[62,47265,2342],{"class":68},[62,47267,47268,47270,47272,47274,47276],{"class":64,"line":76},[62,47269,942],{"class":72},[62,47271,10592],{"class":68},[62,47273,2109],{"class":72},[62,47275,41368],{"class":1675},[62,47277,2212],{"class":72},[62,47279,47280,47282,47284,47286],{"class":64,"line":82},[62,47281,116],{"class":68},[62,47283,119],{"class":68},[62,47285,41379],{"class":122},[62,47287,126],{"class":72},[62,47289,47290],{"class":64,"line":89},[62,47291,79],{"emptyLinePlaceholder":13},[62,47293,47294,47296,47298,47300,47303,47305,47307],{"class":64,"line":95},[62,47295,137],{"class":68},[62,47297,3079],{"class":72},[62,47299,38700],{"class":68},[62,47301,47302],{"class":72},"> posts ",[62,47304,146],{"class":68},[62,47306,466],{"class":68},[62,47308,3091],{"class":72},[62,47310,47311],{"class":64,"line":101},[62,47312,79],{"emptyLinePlaceholder":13},[62,47314,47315,47317],{"class":64,"line":107},[62,47316,2143],{"class":72},[62,47318,47319],{"class":68},"GetMapping\n",[62,47321,47322,47324,47326,47328,47330],{"class":64,"line":113},[62,47323,4396],{"class":72},[62,47325,38700],{"class":68},[62,47327,3135],{"class":72},[62,47329,10287],{"class":122},[62,47331,206],{"class":72},[62,47333,47334,47336],{"class":64,"line":129},[62,47335,360],{"class":68},[62,47337,47338],{"class":72}," posts;\n",[62,47340,47341],{"class":64,"line":134},[62,47342,223],{"class":72},[62,47344,47345],{"class":64,"line":156},[62,47346,79],{"emptyLinePlaceholder":13},[62,47348,47349,47351,47353,47355,47357],{"class":64,"line":161},[62,47350,2143],{"class":72},[62,47352,2548],{"class":68},[62,47354,2109],{"class":72},[62,47356,41486],{"class":1675},[62,47358,2212],{"class":72},[62,47360,47361,47363,47365,47367,47369,47371,47373,47375,47377],{"class":64,"line":167},[62,47362,39496],{"class":72},[62,47364,38700],{"class":68},[62,47366,3135],{"class":72},[62,47368,38763],{"class":122},[62,47370,2475],{"class":72},[62,47372,23740],{"class":68},[62,47374,39510],{"class":72},[62,47376,6283],{"class":889},[62,47378,768],{"class":72},[62,47380,47381,47383,47385,47387],{"class":64,"line":173},[62,47382,360],{"class":68},[62,47384,42398],{"class":72},[62,47386,42401],{"class":122},[62,47388,47389],{"class":72},"(posts\n",[62,47391,47392,47394,47396],{"class":64,"line":179},[62,47393,2418],{"class":72},[62,47395,2621],{"class":122},[62,47397,2223],{"class":72},[62,47399,47400,47402,47404,47406,47408,47410,47412,47414,47416],{"class":64,"line":185},[62,47401,2418],{"class":72},[62,47403,3216],{"class":122},[62,47405,5088],{"class":72},[62,47407,800],{"class":68},[62,47409,5105],{"class":72},[62,47411,6283],{"class":122},[62,47413,3229],{"class":72},[62,47415,3232],{"class":122},[62,47417,47418],{"class":72},"(id))\n",[62,47420,47421,47423,47425],{"class":64,"line":191},[62,47422,2418],{"class":72},[62,47424,3242],{"class":122},[62,47426,2223],{"class":72},[62,47428,47429,47431,47434,47436,47438,47440,47443,47445,47448,47450,47453,47455,47458],{"class":64,"line":209},[62,47430,2418],{"class":72},[62,47432,47433],{"class":122},"orElseThrow",[62,47435,797],{"class":72},[62,47437,800],{"class":68},[62,47439,466],{"class":68},[62,47441,47442],{"class":122}," PostNotFoundException",[62,47444,2109],{"class":72},[62,47446,47447],{"class":1675},"\"Post with id: \"",[62,47449,4507],{"class":68},[62,47451,47452],{"class":72}," id ",[62,47454,1148],{"class":68},[62,47456,47457],{"class":1675}," \" not found.\"",[62,47459,47460],{"class":72},")));\n",[62,47462,47463],{"class":64,"line":220},[62,47464,223],{"class":72},[62,47466,47467],{"class":64,"line":226},[62,47468,79],{"emptyLinePlaceholder":13},[62,47470,47471,47473],{"class":64,"line":231},[62,47472,2143],{"class":72},[62,47474,41533],{"class":68},[62,47476,47477,47479,47481,47483,47485,47487,47489],{"class":64,"line":236},[62,47478,11710],{"class":68},[62,47480,23301],{"class":122},[62,47482,2475],{"class":72},[62,47484,2478],{"class":68},[62,47486,41556],{"class":72},[62,47488,38838],{"class":889},[62,47490,768],{"class":72},[62,47492,47493,47496,47498],{"class":64,"line":242},[62,47494,47495],{"class":72}," posts.",[62,47497,5806],{"class":122},[62,47499,5777],{"class":72},[62,47501,47502],{"class":64,"line":247},[62,47503,223],{"class":72},[62,47505,47506],{"class":64,"line":252},[62,47507,79],{"emptyLinePlaceholder":13},[62,47509,47510,47512,47514,47516,47518],{"class":64,"line":257},[62,47511,2143],{"class":72},[62,47513,41584],{"class":68},[62,47515,2109],{"class":72},[62,47517,41486],{"class":1675},[62,47519,2212],{"class":72},[62,47521,47522,47524,47526,47528,47530,47532,47534,47536,47538,47540,47542],{"class":64,"line":271},[62,47523,11710],{"class":68},[62,47525,38909],{"class":122},[62,47527,2475],{"class":72},[62,47529,2478],{"class":68},[62,47531,41556],{"class":72},[62,47533,38838],{"class":889},[62,47535,39583],{"class":72},[62,47537,23740],{"class":68},[62,47539,39510],{"class":72},[62,47541,6283],{"class":889},[62,47543,768],{"class":72},[62,47545,47546,47548,47550],{"class":64,"line":281},[62,47547,47495],{"class":72},[62,47549,2621],{"class":122},[62,47551,2223],{"class":72},[62,47553,47554,47556,47558,47560,47562,47564,47566,47568,47570],{"class":64,"line":286},[62,47555,2418],{"class":72},[62,47557,3216],{"class":122},[62,47559,4419],{"class":72},[62,47561,800],{"class":68},[62,47563,4424],{"class":72},[62,47565,6283],{"class":122},[62,47567,3229],{"class":72},[62,47569,3232],{"class":122},[62,47571,47418],{"class":72},[62,47573,47574,47576,47578],{"class":64,"line":291},[62,47575,2418],{"class":72},[62,47577,3242],{"class":122},[62,47579,2223],{"class":72},[62,47581,47582,47584,47586,47589,47591,47593,47596,47599,47602],{"class":64,"line":296},[62,47583,2418],{"class":72},[62,47585,5423],{"class":122},[62,47587,47588],{"class":72},"(value ",[62,47590,800],{"class":68},[62,47592,4406],{"class":72},[62,47594,47595],{"class":122},"set",[62,47597,47598],{"class":72},"(posts.",[62,47600,47601],{"class":122},"indexOf",[62,47603,47604],{"class":72},"(value),post));\n",[62,47606,47607],{"class":64,"line":302},[62,47608,223],{"class":72},[62,47610,47611],{"class":64,"line":308},[62,47612,79],{"emptyLinePlaceholder":13},[62,47614,47615,47617,47619,47621,47623],{"class":64,"line":314},[62,47616,2143],{"class":72},[62,47618,23719],{"class":68},[62,47620,2109],{"class":72},[62,47622,41486],{"class":1675},[62,47624,2212],{"class":72},[62,47626,47627,47629,47631,47633,47635,47637,47639],{"class":64,"line":320},[62,47628,11710],{"class":68},[62,47630,38982],{"class":122},[62,47632,2475],{"class":72},[62,47634,23740],{"class":68},[62,47636,39510],{"class":72},[62,47638,6283],{"class":889},[62,47640,768],{"class":72},[62,47642,47643,47645,47647,47649,47651,47653,47655,47657,47659],{"class":64,"line":326},[62,47644,47495],{"class":72},[62,47646,23352],{"class":122},[62,47648,5088],{"class":72},[62,47650,800],{"class":68},[62,47652,5105],{"class":72},[62,47654,6283],{"class":122},[62,47656,3229],{"class":72},[62,47658,3232],{"class":122},[62,47660,23370],{"class":72},[62,47662,47663],{"class":64,"line":338},[62,47664,223],{"class":72},[62,47666,47667],{"class":64,"line":343},[62,47668,79],{"emptyLinePlaceholder":13},[62,47670,47671],{"class":64,"line":357},[62,47672,379],{"class":72},[22,47674,47675,47676,47679],{},"To populate our in-memory collection, we can use a service called JSONPlaceholder, which provides a fake data API. We'll create a new interface called ",[59,47677,47678],{},"JSONPlaceholderService",", which defines a method for loading posts from the JSONPlaceholder API. We'll implement this service to fetch 100 posts and populate our in-memory collection during application initialization.",[52,47681,47683],{"className":54,"code":47682,"language":56,"meta":57,"style":57},"public interface JsonPlaceholderService {\n\n @GetExchange(\"/posts\")\n List\u003CPost> loadPosts();\n\n}\n",[59,47684,47685,47695,47699,47711,47724,47728],{"__ignoreMap":57},[62,47686,47687,47689,47691,47693],{"class":64,"line":65},[62,47688,116],{"class":68},[62,47690,8531],{"class":68},[62,47692,44927],{"class":122},[62,47694,126],{"class":72},[62,47696,47697],{"class":64,"line":76},[62,47698,79],{"emptyLinePlaceholder":13},[62,47700,47701,47703,47705,47707,47709],{"class":64,"line":82},[62,47702,2143],{"class":72},[62,47704,39452],{"class":68},[62,47706,2109],{"class":72},[62,47708,39293],{"class":1675},[62,47710,2212],{"class":72},[62,47712,47713,47715,47717,47719,47722],{"class":64,"line":89},[62,47714,4396],{"class":72},[62,47716,38700],{"class":68},[62,47718,3135],{"class":72},[62,47720,47721],{"class":122},"loadPosts",[62,47723,822],{"class":72},[62,47725,47726],{"class":64,"line":95},[62,47727,79],{"emptyLinePlaceholder":13},[62,47729,47730],{"class":64,"line":101},[62,47731,379],{"class":72},[52,47733,47735],{"className":54,"code":47734,"language":56,"meta":57,"style":57},"@SpringBootApplication\npublic class Application {\n\n public static void main(String[] args) {\n SpringApplication.run(Application.class, args);\n }\n\n @Bean\n JsonPlaceholderService jsonPlaceholderService() {\n WebClient client = WebClient.builder()\n .baseUrl(\"https://jsonplaceholder.typicode.com\")\n .build();\n HttpServiceProxyFactory factory = HttpServiceProxyFactory.builder(WebClientAdapter.forClient(client)).build();\n return factory.createClient(JsonPlaceholderService.class);\n }\n}\n",[59,47736,47737,47743,47753,47757,47777,47785,47789,47793,47799,47807,47821,47833,47841,47865,47875,47879],{"__ignoreMap":57},[62,47738,47739,47741],{"class":64,"line":65},[62,47740,942],{"class":72},[62,47742,2079],{"class":68},[62,47744,47745,47747,47749,47751],{"class":64,"line":76},[62,47746,116],{"class":68},[62,47748,119],{"class":68},[62,47750,2088],{"class":122},[62,47752,126],{"class":72},[62,47754,47755],{"class":64,"line":82},[62,47756,79],{"emptyLinePlaceholder":13},[62,47758,47759,47761,47763,47765,47767,47769,47771,47773,47775],{"class":64,"line":89},[62,47760,194],{"class":68},[62,47762,2101],{"class":68},[62,47764,200],{"class":68},[62,47766,2106],{"class":122},[62,47768,2109],{"class":72},[62,47770,973],{"class":68},[62,47772,2114],{"class":72},[62,47774,2117],{"class":889},[62,47776,768],{"class":72},[62,47778,47779,47781,47783],{"class":64,"line":95},[62,47780,2124],{"class":72},[62,47782,2127],{"class":122},[62,47784,2130],{"class":72},[62,47786,47787],{"class":64,"line":101},[62,47788,223],{"class":72},[62,47790,47791],{"class":64,"line":107},[62,47792,79],{"emptyLinePlaceholder":13},[62,47794,47795,47797],{"class":64,"line":113},[62,47796,2143],{"class":72},[62,47798,2146],{"class":68},[62,47800,47801,47803,47805],{"class":64,"line":129},[62,47802,45161],{"class":72},[62,47804,45164],{"class":122},[62,47806,206],{"class":72},[62,47808,47809,47812,47814,47817,47819],{"class":64,"line":134},[62,47810,47811],{"class":72}," WebClient client ",[62,47813,146],{"class":68},[62,47815,47816],{"class":72}," WebClient.",[62,47818,2160],{"class":122},[62,47820,2223],{"class":72},[62,47822,47823,47825,47827,47829,47831],{"class":64,"line":156},[62,47824,2418],{"class":72},[62,47826,11188],{"class":122},[62,47828,2109],{"class":72},[62,47830,44149],{"class":1675},[62,47832,2212],{"class":72},[62,47834,47835,47837,47839],{"class":64,"line":161},[62,47836,2418],{"class":72},[62,47838,2189],{"class":122},[62,47840,822],{"class":72},[62,47842,47843,47845,47847,47850,47852,47855,47858,47861,47863],{"class":64,"line":167},[62,47844,45188],{"class":72},[62,47846,146],{"class":68},[62,47848,47849],{"class":72}," HttpServiceProxyFactory.",[62,47851,2160],{"class":122},[62,47853,47854],{"class":72},"(WebClientAdapter.",[62,47856,47857],{"class":122},"forClient",[62,47859,47860],{"class":72},"(client)).",[62,47862,2189],{"class":122},[62,47864,822],{"class":72},[62,47866,47867,47869,47871,47873],{"class":64,"line":173},[62,47868,360],{"class":68},[62,47870,45223],{"class":72},[62,47872,45226],{"class":122},[62,47874,45229],{"class":72},[62,47876,47877],{"class":64,"line":179},[62,47878,223],{"class":72},[62,47880,47881],{"class":64,"line":185},[62,47882,379],{"class":72},[52,47884,47886],{"className":54,"code":47885,"language":56,"meta":57,"style":57},"@RestController\n@RequestMapping(\"/api/posts\")\npublic class PostController {\n\n private static final Logger log = LoggerFactory.getLogger(PostController.class);\n private final JsonPlaceholderService jsonPlaceholderService;\n private List\u003CPost> posts = new ArrayList\u003C>();\n\n public PostController(JsonPlaceholderService jsonPlaceholderService) {\n this.jsonPlaceholderService = jsonPlaceholderService;\n }\n\n // CRUD methods omitted for brevity\n\n @PostConstruct\n private void init() {\n // JsonPlaceHolder Service\n if(posts.isEmpty()) {\n log.info(\"Loading posts using JsonPlaceholderService\");\n posts = jsonPlaceholderService.loadPosts();\n }\n }\n}\n",[59,47887,47888,47894,47906,47916,47920,47939,47948,47964,47968,47980,47992,47996,48000,48005,48009,48015,48025,48030,48041,48054,48068,48072,48076],{"__ignoreMap":57},[62,47889,47890,47892],{"class":64,"line":65},[62,47891,942],{"class":72},[62,47893,2342],{"class":68},[62,47895,47896,47898,47900,47902,47904],{"class":64,"line":76},[62,47897,942],{"class":72},[62,47899,10592],{"class":68},[62,47901,2109],{"class":72},[62,47903,41368],{"class":1675},[62,47905,2212],{"class":72},[62,47907,47908,47910,47912,47914],{"class":64,"line":82},[62,47909,116],{"class":68},[62,47911,119],{"class":68},[62,47913,41379],{"class":122},[62,47915,126],{"class":72},[62,47917,47918],{"class":64,"line":89},[62,47919,79],{"emptyLinePlaceholder":13},[62,47921,47922,47924,47926,47928,47930,47932,47934,47936],{"class":64,"line":95},[62,47923,137],{"class":68},[62,47925,2101],{"class":68},[62,47927,458],{"class":68},[62,47929,3061],{"class":72},[62,47931,146],{"class":68},[62,47933,3066],{"class":72},[62,47935,3069],{"class":122},[62,47937,47938],{"class":72},"(PostController.class);\n",[62,47940,47941,47943,47945],{"class":64,"line":101},[62,47942,137],{"class":68},[62,47944,458],{"class":68},[62,47946,47947],{"class":72}," JsonPlaceholderService jsonPlaceholderService;\n",[62,47949,47950,47952,47954,47956,47958,47960,47962],{"class":64,"line":107},[62,47951,137],{"class":68},[62,47953,3079],{"class":72},[62,47955,38700],{"class":68},[62,47957,47302],{"class":72},[62,47959,146],{"class":68},[62,47961,466],{"class":68},[62,47963,3091],{"class":72},[62,47965,47966],{"class":64,"line":113},[62,47967,79],{"emptyLinePlaceholder":13},[62,47969,47970,47972,47974,47976,47978],{"class":64,"line":129},[62,47971,194],{"class":68},[62,47973,41379],{"class":122},[62,47975,43806],{"class":72},[62,47977,45164],{"class":889},[62,47979,768],{"class":72},[62,47981,47982,47984,47987,47989],{"class":64,"line":134},[62,47983,2405],{"class":149},[62,47985,47986],{"class":72},".jsonPlaceholderService ",[62,47988,146],{"class":68},[62,47990,47991],{"class":72}," jsonPlaceholderService;\n",[62,47993,47994],{"class":64,"line":156},[62,47995,223],{"class":72},[62,47997,47998],{"class":64,"line":161},[62,47999,79],{"emptyLinePlaceholder":13},[62,48001,48002],{"class":64,"line":167},[62,48003,48004],{"class":85}," // CRUD methods omitted for brevity\n",[62,48006,48007],{"class":64,"line":173},[62,48008,79],{"emptyLinePlaceholder":13},[62,48010,48011,48013],{"class":64,"line":179},[62,48012,2143],{"class":72},[62,48014,3273],{"class":68},[62,48016,48017,48019,48021,48023],{"class":64,"line":185},[62,48018,137],{"class":68},[62,48020,200],{"class":68},[62,48022,3282],{"class":122},[62,48024,206],{"class":72},[62,48026,48027],{"class":64,"line":191},[62,48028,48029],{"class":85}," // JsonPlaceHolder Service\n",[62,48031,48032,48034,48036,48039],{"class":64,"line":209},[62,48033,12741],{"class":68},[62,48035,47598],{"class":72},[62,48037,48038],{"class":122},"isEmpty",[62,48040,6671],{"class":72},[62,48042,48043,48045,48047,48049,48052],{"class":64,"line":220},[62,48044,13341],{"class":72},[62,48046,12688],{"class":122},[62,48048,2109],{"class":72},[62,48050,48051],{"class":1675},"\"Loading posts using JsonPlaceholderService\"",[62,48053,1133],{"class":72},[62,48055,48056,48059,48061,48064,48066],{"class":64,"line":226},[62,48057,48058],{"class":72}," posts ",[62,48060,146],{"class":68},[62,48062,48063],{"class":72}," jsonPlaceholderService.",[62,48065,47721],{"class":122},[62,48067,822],{"class":72},[62,48069,48070],{"class":64,"line":231},[62,48071,533],{"class":72},[62,48073,48074],{"class":64,"line":236},[62,48075,223],{"class":72},[62,48077,48078],{"class":64,"line":242},[62,48079,379],{"class":72},[22,48081,48082],{},"Once we have our Spring Boot application and REST API set up, we can proceed to package and deploy our application to AWS Lambda.",[26,48084,48086],{"id":48085},"packaging-and-deploying-to-aws-lambda","Packaging and Deploying to AWS Lambda",[22,48088,48089],{},"To package our application, we can use Maven. From the terminal, navigate to the root directory of your project and run the following command:",[52,48091,48092],{"className":1663,"code":3632,"language":1665,"meta":57,"style":57},[59,48093,48094],{"__ignoreMap":57},[62,48095,48096,48098,48100],{"class":64,"line":65},[62,48097,3639],{"class":122},[62,48099,3642],{"class":1675},[62,48101,3645],{"class":1675},[22,48103,48104,48105,48107],{},"This command will create a ZIP file in the ",[59,48106,3651],{}," directory, which contains our packaged application.",[22,48109,48110],{},"With our packaged application in hand, we can now proceed to deploy it to AWS Lambda. To create a new Lambda function, navigate to the AWS Management Console and select AWS Lambda. Click on \"Create function\" and choose the \"Author from scratch\" option.",[22,48112,48113],{},[653,48114],{"alt":48115,"src":48116},"Create new Lambda Function","/images/blog/2023/06/30/7uleLq2l1fkpOH0HzKEz-1297.12.png",[22,48118,48119],{},"In the function creation wizard, provide a name for your function and select \"Java 17\" as the runtime. Note that we'll be using the x86 architecture, as there are some limitations with Snapstart and Java 17 on other architectures.",[22,48121,48122],{},"After creating the function, you'll need to upload the ZIP file containing your packaged application. Click on \"Upload\" and select the ZIP file from your local machine.",[22,48124,48125,48126,48129,48130,48133],{},"Next, we need to configure the runtime settings and set the handler for our Lambda function. The handler is the entry point for our application and determines how requests are processed. In our case, the handler class is named ",[59,48127,48128],{},"dev.danvega.StreamLambdaHandler"," and the method to handle requests is ",[59,48131,48132],{},"handleRequest",". Once you've set the handler, click \"Save\" to apply the configuration.",[22,48135,48136],{},"To test the Lambda function, you can use the AWS Management Console. Select the function, click on \"Test\", and create a new test event using the \"API Gateway AWS Proxy\" template. Fill in the necessary details, such as the path to the resource you want to test, and click \"Create\".",[22,48138,48139],{},"If everything is set up correctly, you should see a successful response from your Lambda function. You can also use the test event to verify that the different endpoints of your REST API are working as expected.",[22,48141,48142],{},"Now that we have our Lambda function up and running, we can create an API Gateway to serve as the front-end for our application. The API Gateway will act as a proxy, forwarding incoming requests to our Lambda function.",[26,48144,48146],{"id":48145},"creating-an-api-gateway","Creating an API Gateway",[22,48148,48149],{},"To create an API Gateway, navigate to the AWS Management Console and select API Gateway. Click on \"Create API\" and choose the \"REST API\" option.",[22,48151,48152],{},"Provide a name for your API and choose \"Create API\". This will create a new REST API in API Gateway.",[22,48154,48155,48156,48159],{},"To define the resources and methods for our API, we'll create a new resource for each endpoint of our REST API. For example, if we have an endpoint ",[59,48157,48158],{},"/posts",", we'll create a resource named \"Posts\". Under each resource, we can define the HTTP methods that are allowed, such as GET, POST, PUT, and DELETE.",[22,48161,48162],{},[653,48163],{"alt":48164,"src":48165},"API Gateway","/images/blog/2023/06/30/alA7JYq3UOnAEEbsdORU-1607.83.png",[22,48167,48168],{},"To configure the integration between the API Gateway and our Lambda function, select a resource and method, and click on \"Integration Request\". Choose the Lambda function as the integration type and select the appropriate Lambda function from the list. Click \"Save\" to apply the changes.",[22,48170,48171],{},"Once the integration is set up, you can deploy your API by creating a new stage. This will provide you with a unique Invoke URL that you can use to access your API.",[22,48173,48174],{},"Congratulations! You have successfully deployed your Spring Boot application to AWS Lambda using API Gateway as a front-end proxy. You can now access your REST API using the provided Invoke URL.",[26,48176,1499],{"id":1498},[22,48178,48179],{},"In this comprehensive blog post, we explored the process of deploying Spring Boot applications to AWS Lambda. We discussed the benefits of serverless computing, such as automatic scaling, reduced costs, and high availability. By leveraging AWS Lambda, you can offload the burden of server management and focus on developing and delivering your applications.",[22,48181,48182],{},"We walked through the process step-by-step, from creating a new Spring Boot project using a Maven archetype specifically designed for AWS Lambda, to packaging and deploying the application to AWS Lambda using the AWS Management Console.",[22,48184,48185],{},"Now that you have the knowledge and resources to deploy your Spring Boot applications to AWS Lambda, you can unlock the power of serverless computing and take your projects to the next level.",[22,48187,48188],{},"If you have any questions or need further assistance, please don't hesitate to leave a comment and I'll be happy to help. I hope you found this blog post helpful and informative. As always, happy coding, friends!",[1527,48190,48191],{},"html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .spoOn, html code.shiki .spoOn{--shiki-default:#E36209;--shiki-dark:#FFAB70;--shiki-sepia:#E36209}html pre.shiki code .s4-Ip, html code.shiki .s4-Ip{--shiki-default:#6A737D;--shiki-dark:#6A737D;--shiki-sepia:#6A737D}",{"title":57,"searchDepth":76,"depth":76,"links":48193},[48194,48195,48196,48197,48198,48199],{"id":47086,"depth":76,"text":47087},{"id":47096,"depth":76,"text":47097},{"id":47109,"depth":76,"text":47110},{"id":48085,"depth":76,"text":48086},{"id":48145,"depth":76,"text":48146},{"id":1498,"depth":76,"text":1499},{"_id":48201,"path":48202,"title":48203,"description":48204,"meta":48205,"body":48211},"content/blog/2023/06/14/spring-http-interfaces.md","/blog/2023/06/14/spring-http-interfaces","Spring HTTP Interface Clients: Consuming HTTP services in Spring Boot","Learn all about the new HTTP Interfaces in Spring Framework 6 and Spring Boot 3 by building out a real world example.",{"slug":48206,"date":48207,"published":13,"tags":48208,"author":17,"cover":48210,"excerpt":-1},"spring-http-interfaces","2023-06-14T08:00:00.000Z",[48209,2925],"Spring Framework","./http-interfaces-crud.png",{"type":19,"value":48212,"toc":49190},[48213,48216,48220,48234,48236,48239,48245,48248,48254,48257,48260,48277,48284,48311,48317,48347,48839,48842,48845,48848,48901,48920,48926,49112,49115,49118,49124,49127,49138,49141,49172,49179,49182,49184,49187],[22,48214,48215],{},"In this blog post, we will explore the concept of HTTP interface clients in the Spring Framework 6 and Spring Boot 3. We will go through the process of creating a full REST API in Spring MVC and then create a new service to consume that API using Spring's HTTP interface clients. This blog post will provide a step-by-step guide and example code snippets to help you understand the implementation details. So, let's get started!",[26,48217,48219],{"id":48218},"table-of-contents","Table of Contents",[915,48221,48222,48225,48228,48231],{},[37,48223,48224],{},"Setting up the Project",[37,48226,48227],{},"Creating the REST API in the Article Service",[37,48229,48230],{},"Creating the Article Client in the Content Service",[37,48232,48233],{},"Testing the Implemented API",[26,48235,48224],{"id":15195},[22,48237,48238],{},"Before we dive into the implementation details, we need to set up the project structure. The project will be a Maven multi-module project consisting of two modules: the Article Service and the Content Service. We will create an empty Maven project and add the necessary submodules to it. Let's start by setting up the project in IntelliJ IDEA, although you can use any IDE or text editor of your choice.",[22,48240,48241],{},[653,48242],{"alt":48243,"src":48244},"Creating an empty Maven project","/images/blog/2023/06/14/empty-project.png",[22,48246,48247],{},"Once you have created an empty Maven project, go ahead and create the Article Service and the Content Service as submodules. You can choose the appropriate settings, such as Java version and group ID, according to your preferences. Once the project is set up, you can move on to creating the REST API in the Article Service.",[22,48249,48250],{},[653,48251],{"alt":48252,"src":48253},"Maven multi module project","/images/blog/2023/06/14/multi-module-project.png",[26,48255,48227],{"id":48256},"creating-the-rest-api-in-the-article-service",[22,48258,48259],{},"In the Article Service, we will create a REST API using Spring MVC. We will define the necessary CRUD (Create, Read, Update, Delete) operations for managing articles. Let's start by creating the Article class, which will represent the structure of an article in our system. We will define the ID, title, and body fields for the article.",[52,48261,48263],{"className":54,"code":48262,"language":56,"meta":57,"style":57},"public record Article(Integer id, String title, String body) {}\n",[59,48264,48265],{"__ignoreMap":57},[62,48266,48267,48269,48271,48274],{"class":64,"line":65},[62,48268,116],{"class":68},[62,48270,2996],{"class":68},[62,48272,48273],{"class":122}," Article",[62,48275,48276],{"class":72},"(Integer id, String title, String body) {}\n",[22,48278,48279,48280,48283],{},"Next, we will create the ",[59,48281,48282],{},"ArticleController"," class, which will be responsible for handling the HTTP requests related to articles. We will define the necessary methods to handle the CRUD operations. Here's an overview of the methods we will create:",[915,48285,48286,48291,48296,48301,48306],{},[37,48287,48288,48290],{},[59,48289,10287],{},": This method will retrieve all the articles from the service.",[37,48292,48293,48295],{},[59,48294,38763],{},": This method will retrieve a specific article based on the provided ID.",[37,48297,48298,48300],{},[59,48299,14348],{},": This method will create a new article.",[37,48302,48303,48305],{},[59,48304,1338],{},": This method will update an existing article.",[37,48307,48308,48310],{},[59,48309,41666],{},": This method will delete an article.",[22,48312,48313,48314,48316],{},"To create the ",[59,48315,48282],{}," class, follow these steps:",[34,48318,48319,48329,48338,48344],{},[37,48320,48321,48322,48324,48325,48328],{},"Create a new Java class called ",[59,48323,48282],{}," in the ",[59,48326,48327],{},"controller"," package.",[37,48330,48331,48332,48334,48335,2755],{},"Annotate the class with ",[59,48333,40266],{}," and specify the request mapping for the API, e.g., ",[59,48336,48337],{},"@RequestMapping(\"/api/articles\")",[37,48339,48340,48341,2755],{},"Define a constructor for the class and inject the necessary dependencies, such as the ",[59,48342,48343],{},"ArticleRepository",[37,48345,48346],{},"Implement the CRUD methods mentioned above.",[52,48348,48350],{"className":54,"code":48349,"language":56,"meta":57,"style":57},"@RestController\n@RequestMapping(\"/api/articles\")\npublic class ArticleController {\n\n private final List\u003CArticle> articles = new ArrayList\u003C>();\n\n @GetMapping\n public List\u003CArticle> findAll() throws InterruptedException {\n Thread.sleep(6000);\n return articles;\n }\n\n @GetMapping(\"/{id}\")\n public Optional\u003CArticle> findById(@PathVariable Integer id) {\n return articles.stream().filter(article -> article.id().equals(id)).findFirst();\n }\n\n @PostMapping\n @ResponseStatus(HttpStatus.CREATED)\n public void create(@RequestBody Article article) {\n this.articles.add(article);\n }\n\n @PutMapping(\"/{id}\")\n @ResponseStatus(HttpStatus.NO_CONTENT)\n public void update(@RequestBody Article article, @PathVariable Integer id) {\n var currentArticle = articles.stream().filter(a -> a.id().equals(id)).findFirst();\n currentArticle.ifPresent(value -> this.articles.set(articles.indexOf(value), article));\n }\n\n @DeleteMapping(\"/{id}\")\n @ResponseStatus(HttpStatus.NO_CONTENT)\n public void delete(@PathVariable Integer id) {\n this.articles.removeIf(a -> a.id().equals(id));\n }\n\n @PostConstruct\n private void init() {\n Article article = new Article(1,\"Hello, World!\",\"This is my first post\");\n this.articles.add(article);\n }\n\n}\n",[59,48351,48352,48358,48371,48382,48386,48405,48409,48415,48434,48448,48455,48459,48463,48475,48497,48531,48535,48539,48545,48553,48571,48583,48587,48591,48603,48611,48637,48674,48699,48703,48707,48719,48727,48745,48767,48771,48775,48781,48791,48817,48827,48831,48835],{"__ignoreMap":57},[62,48353,48354,48356],{"class":64,"line":65},[62,48355,942],{"class":72},[62,48357,2342],{"class":68},[62,48359,48360,48362,48364,48366,48369],{"class":64,"line":76},[62,48361,942],{"class":72},[62,48363,10592],{"class":68},[62,48365,2109],{"class":72},[62,48367,48368],{"class":1675},"\"/api/articles\"",[62,48370,2212],{"class":72},[62,48372,48373,48375,48377,48380],{"class":64,"line":82},[62,48374,116],{"class":68},[62,48376,119],{"class":68},[62,48378,48379],{"class":122}," ArticleController",[62,48381,126],{"class":72},[62,48383,48384],{"class":64,"line":89},[62,48385,79],{"emptyLinePlaceholder":13},[62,48387,48388,48390,48392,48394,48396,48399,48401,48403],{"class":64,"line":95},[62,48389,137],{"class":68},[62,48391,458],{"class":68},[62,48393,3079],{"class":72},[62,48395,39467],{"class":68},[62,48397,48398],{"class":72},"> articles ",[62,48400,146],{"class":68},[62,48402,466],{"class":68},[62,48404,3091],{"class":72},[62,48406,48407],{"class":64,"line":101},[62,48408,79],{"emptyLinePlaceholder":13},[62,48410,48411,48413],{"class":64,"line":107},[62,48412,2143],{"class":72},[62,48414,47319],{"class":68},[62,48416,48417,48419,48421,48423,48425,48427,48429,48431],{"class":64,"line":113},[62,48418,194],{"class":68},[62,48420,3079],{"class":72},[62,48422,39467],{"class":68},[62,48424,3135],{"class":72},[62,48426,10287],{"class":122},[62,48428,5398],{"class":72},[62,48430,11501],{"class":68},[62,48432,48433],{"class":72}," InterruptedException {\n",[62,48435,48436,48439,48441,48443,48446],{"class":64,"line":129},[62,48437,48438],{"class":72}," Thread.",[62,48440,847],{"class":122},[62,48442,2109],{"class":72},[62,48444,48445],{"class":149},"6000",[62,48447,1133],{"class":72},[62,48449,48450,48452],{"class":64,"line":134},[62,48451,360],{"class":68},[62,48453,48454],{"class":72}," articles;\n",[62,48456,48457],{"class":64,"line":156},[62,48458,223],{"class":72},[62,48460,48461],{"class":64,"line":161},[62,48462,79],{"emptyLinePlaceholder":13},[62,48464,48465,48467,48469,48471,48473],{"class":64,"line":167},[62,48466,2143],{"class":72},[62,48468,2548],{"class":68},[62,48470,2109],{"class":72},[62,48472,41486],{"class":1675},[62,48474,2212],{"class":72},[62,48476,48477,48479,48481,48483,48485,48487,48489,48491,48493,48495],{"class":64,"line":173},[62,48478,194],{"class":68},[62,48480,38756],{"class":72},[62,48482,39467],{"class":68},[62,48484,3135],{"class":72},[62,48486,38763],{"class":122},[62,48488,2475],{"class":72},[62,48490,23740],{"class":68},[62,48492,39510],{"class":72},[62,48494,6283],{"class":889},[62,48496,768],{"class":72},[62,48498,48499,48501,48504,48506,48508,48510,48513,48515,48518,48520,48522,48524,48527,48529],{"class":64,"line":179},[62,48500,360],{"class":68},[62,48502,48503],{"class":72}," articles.",[62,48505,2621],{"class":122},[62,48507,3229],{"class":72},[62,48509,3216],{"class":122},[62,48511,48512],{"class":72},"(article ",[62,48514,800],{"class":68},[62,48516,48517],{"class":72}," article.",[62,48519,6283],{"class":122},[62,48521,3229],{"class":72},[62,48523,3232],{"class":122},[62,48525,48526],{"class":72},"(id)).",[62,48528,3242],{"class":122},[62,48530,822],{"class":72},[62,48532,48533],{"class":64,"line":185},[62,48534,223],{"class":72},[62,48536,48537],{"class":64,"line":191},[62,48538,79],{"emptyLinePlaceholder":13},[62,48540,48541,48543],{"class":64,"line":209},[62,48542,2143],{"class":72},[62,48544,41533],{"class":68},[62,48546,48547,48549,48551],{"class":64,"line":220},[62,48548,2143],{"class":72},[62,48550,41540],{"class":68},[62,48552,41543],{"class":72},[62,48554,48555,48557,48559,48561,48563,48565,48567,48569],{"class":64,"line":226},[62,48556,194],{"class":68},[62,48558,200],{"class":68},[62,48560,23301],{"class":122},[62,48562,2475],{"class":72},[62,48564,2478],{"class":68},[62,48566,39544],{"class":72},[62,48568,39547],{"class":889},[62,48570,768],{"class":72},[62,48572,48573,48575,48578,48580],{"class":64,"line":231},[62,48574,2405],{"class":149},[62,48576,48577],{"class":72},".articles.",[62,48579,5806],{"class":122},[62,48581,48582],{"class":72},"(article);\n",[62,48584,48585],{"class":64,"line":236},[62,48586,223],{"class":72},[62,48588,48589],{"class":64,"line":242},[62,48590,79],{"emptyLinePlaceholder":13},[62,48592,48593,48595,48597,48599,48601],{"class":64,"line":247},[62,48594,2143],{"class":72},[62,48596,41584],{"class":68},[62,48598,2109],{"class":72},[62,48600,41486],{"class":1675},[62,48602,2212],{"class":72},[62,48604,48605,48607,48609],{"class":64,"line":252},[62,48606,2143],{"class":72},[62,48608,41540],{"class":68},[62,48610,44041],{"class":72},[62,48612,48613,48615,48617,48619,48621,48623,48625,48627,48629,48631,48633,48635],{"class":64,"line":257},[62,48614,194],{"class":68},[62,48616,200],{"class":68},[62,48618,38909],{"class":122},[62,48620,2475],{"class":72},[62,48622,2478],{"class":68},[62,48624,39544],{"class":72},[62,48626,39547],{"class":889},[62,48628,39583],{"class":72},[62,48630,23740],{"class":68},[62,48632,39510],{"class":72},[62,48634,6283],{"class":889},[62,48636,768],{"class":72},[62,48638,48639,48641,48644,48646,48648,48650,48652,48654,48657,48659,48662,48664,48666,48668,48670,48672],{"class":64,"line":271},[62,48640,13605],{"class":68},[62,48642,48643],{"class":72}," currentArticle ",[62,48645,146],{"class":68},[62,48647,48503],{"class":72},[62,48649,2621],{"class":122},[62,48651,3229],{"class":72},[62,48653,3216],{"class":122},[62,48655,48656],{"class":72},"(a ",[62,48658,800],{"class":68},[62,48660,48661],{"class":72}," a.",[62,48663,6283],{"class":122},[62,48665,3229],{"class":72},[62,48667,3232],{"class":122},[62,48669,48526],{"class":72},[62,48671,3242],{"class":122},[62,48673,822],{"class":72},[62,48675,48676,48679,48681,48683,48685,48687,48689,48691,48694,48696],{"class":64,"line":281},[62,48677,48678],{"class":72}," currentArticle.",[62,48680,5423],{"class":122},[62,48682,47588],{"class":72},[62,48684,800],{"class":68},[62,48686,9961],{"class":149},[62,48688,48577],{"class":72},[62,48690,47595],{"class":122},[62,48692,48693],{"class":72},"(articles.",[62,48695,47601],{"class":122},[62,48697,48698],{"class":72},"(value), article));\n",[62,48700,48701],{"class":64,"line":286},[62,48702,223],{"class":72},[62,48704,48705],{"class":64,"line":291},[62,48706,79],{"emptyLinePlaceholder":13},[62,48708,48709,48711,48713,48715,48717],{"class":64,"line":296},[62,48710,2143],{"class":72},[62,48712,23719],{"class":68},[62,48714,2109],{"class":72},[62,48716,41486],{"class":1675},[62,48718,2212],{"class":72},[62,48720,48721,48723,48725],{"class":64,"line":302},[62,48722,2143],{"class":72},[62,48724,41540],{"class":68},[62,48726,44041],{"class":72},[62,48728,48729,48731,48733,48735,48737,48739,48741,48743],{"class":64,"line":308},[62,48730,194],{"class":68},[62,48732,200],{"class":68},[62,48734,38982],{"class":122},[62,48736,2475],{"class":72},[62,48738,23740],{"class":68},[62,48740,39510],{"class":72},[62,48742,6283],{"class":889},[62,48744,768],{"class":72},[62,48746,48747,48749,48751,48753,48755,48757,48759,48761,48763,48765],{"class":64,"line":314},[62,48748,2405],{"class":149},[62,48750,48577],{"class":72},[62,48752,23352],{"class":122},[62,48754,48656],{"class":72},[62,48756,800],{"class":68},[62,48758,48661],{"class":72},[62,48760,6283],{"class":122},[62,48762,3229],{"class":72},[62,48764,3232],{"class":122},[62,48766,23370],{"class":72},[62,48768,48769],{"class":64,"line":320},[62,48770,223],{"class":72},[62,48772,48773],{"class":64,"line":326},[62,48774,79],{"emptyLinePlaceholder":13},[62,48776,48777,48779],{"class":64,"line":338},[62,48778,2143],{"class":72},[62,48780,3273],{"class":68},[62,48782,48783,48785,48787,48789],{"class":64,"line":343},[62,48784,137],{"class":68},[62,48786,200],{"class":68},[62,48788,3282],{"class":122},[62,48790,206],{"class":72},[62,48792,48793,48796,48798,48800,48802,48804,48806,48808,48810,48812,48815],{"class":64,"line":357},[62,48794,48795],{"class":72}," Article article ",[62,48797,146],{"class":68},[62,48799,466],{"class":68},[62,48801,48273],{"class":122},[62,48803,2109],{"class":72},[62,48805,6689],{"class":149},[62,48807,32225],{"class":72},[62,48809,27469],{"class":1675},[62,48811,32225],{"class":72},[62,48813,48814],{"class":1675},"\"This is my first post\"",[62,48816,1133],{"class":72},[62,48818,48819,48821,48823,48825],{"class":64,"line":366},[62,48820,2405],{"class":149},[62,48822,48577],{"class":72},[62,48824,5806],{"class":122},[62,48826,48582],{"class":72},[62,48828,48829],{"class":64,"line":371},[62,48830,223],{"class":72},[62,48832,48833],{"class":64,"line":376},[62,48834,79],{"emptyLinePlaceholder":13},[62,48836,48837],{"class":64,"line":16333},[62,48838,379],{"class":72},[22,48840,48841],{},"That's it! You have successfully created the REST API in the Article Service. You can test the API endpoints using tools like curl or Postman. Now, let's move on to creating the Article Client in the Content Service.",[26,48843,48230],{"id":48844},"creating-the-article-client-in-the-content-service",[22,48846,48847],{},"In the Content Service, we will create an Article Client using Spring's HTTP interface clients to consume the REST API provided by the Article Service. The Article Client will be responsible for making HTTP requests to the API endpoints of the Article Service and retrieving the necessary data. Let's start by creating the necessary configuration and interface definition.",[34,48849,48850,48857,48870,48880,48888],{},[37,48851,48852,48853,48856],{},"Create a new package called ",[59,48854,48855],{},"service"," in the Content Service.",[37,48858,48859,48860,48324,48863,48866,48867,2755],{},"Create a new class called ",[59,48861,48862],{},"ArticleClientConfig",[59,48864,48865],{},"config"," package. Annotate it with ",[59,48868,48869],{},"@Configuration",[37,48871,48872,48873,48876,48877,48879],{},"Define a bean for the ",[59,48874,48875],{},"ArticleClient"," using the ",[59,48878,34866],{}," builder and specify the base URL of the Article Service API.",[37,48881,48882,48883,48324,48885,48887],{},"Create an interface called ",[59,48884,48875],{},[59,48886,48855],{}," package. This interface will define the methods for interacting with the Article Service API.",[37,48889,48890,48891,48893,48894,48897,48898,2755],{},"Annotate the ",[59,48892,48875],{}," interface with ",[59,48895,48896],{},"@FeignClient"," and specify the name of the client, e.g., ",[59,48899,48900],{},"@FeignClient(\"article-service\")",[22,48902,3521,48903,48905,48906,976,48909,976,48912,48915,48916,48919],{},[59,48904,48875],{}," interface will contain the necessary method declarations for interacting with the Article Service API. The methods will have ",[59,48907,48908],{},"@GetMapping",[59,48910,48911],{},"@PostMapping",[59,48913,48914],{},"@PutMapping",", or ",[59,48917,48918],{},"@DeleteMapping"," annotations to specify the corresponding HTTP method and URL. Each method will have a returning type according to the expected response from the Article Service API.",[22,48921,48922,48923,48925],{},"Here's an example of the ",[59,48924,48875],{}," interface:",[52,48927,48928],{"className":54,"code":39428,"language":56,"meta":57,"style":57},[59,48929,48930,48940,48944,48956,48968,48972,48984,49004,49008,49020,49036,49040,49052,49076,49080,49092,49108],{"__ignoreMap":57},[62,48931,48932,48934,48936,48938],{"class":64,"line":65},[62,48933,116],{"class":68},[62,48935,8531],{"class":68},[62,48937,39439],{"class":122},[62,48939,126],{"class":72},[62,48941,48942],{"class":64,"line":76},[62,48943,79],{"emptyLinePlaceholder":13},[62,48945,48946,48948,48950,48952,48954],{"class":64,"line":82},[62,48947,2143],{"class":72},[62,48949,39452],{"class":68},[62,48951,2109],{"class":72},[62,48953,39457],{"class":1675},[62,48955,2212],{"class":72},[62,48957,48958,48960,48962,48964,48966],{"class":64,"line":89},[62,48959,39464],{"class":72},[62,48961,39467],{"class":68},[62,48963,39470],{"class":72},[62,48965,10287],{"class":122},[62,48967,822],{"class":72},[62,48969,48970],{"class":64,"line":95},[62,48971,79],{"emptyLinePlaceholder":13},[62,48973,48974,48976,48978,48980,48982],{"class":64,"line":101},[62,48975,2143],{"class":72},[62,48977,39452],{"class":68},[62,48979,2109],{"class":72},[62,48981,39489],{"class":1675},[62,48983,2212],{"class":72},[62,48985,48986,48988,48990,48992,48994,48996,48998,49000,49002],{"class":64,"line":107},[62,48987,39496],{"class":72},[62,48989,39467],{"class":68},[62,48991,3135],{"class":72},[62,48993,39503],{"class":122},[62,48995,2475],{"class":72},[62,48997,23740],{"class":68},[62,48999,39510],{"class":72},[62,49001,6283],{"class":889},[62,49003,1133],{"class":72},[62,49005,49006],{"class":64,"line":113},[62,49007,79],{"emptyLinePlaceholder":13},[62,49009,49010,49012,49014,49016,49018],{"class":64,"line":129},[62,49011,2143],{"class":72},[62,49013,39525],{"class":68},[62,49015,2109],{"class":72},[62,49017,39457],{"class":1675},[62,49019,2212],{"class":72},[62,49021,49022,49024,49026,49028,49030,49032,49034],{"class":64,"line":134},[62,49023,11710],{"class":68},[62,49025,23301],{"class":122},[62,49027,2475],{"class":72},[62,49029,2478],{"class":68},[62,49031,39544],{"class":72},[62,49033,39547],{"class":889},[62,49035,1133],{"class":72},[62,49037,49038],{"class":64,"line":156},[62,49039,79],{"emptyLinePlaceholder":13},[62,49041,49042,49044,49046,49048,49050],{"class":64,"line":161},[62,49043,2143],{"class":72},[62,49045,39560],{"class":68},[62,49047,2109],{"class":72},[62,49049,39489],{"class":1675},[62,49051,2212],{"class":72},[62,49053,49054,49056,49058,49060,49062,49064,49066,49068,49070,49072,49074],{"class":64,"line":167},[62,49055,11710],{"class":68},[62,49057,38909],{"class":122},[62,49059,2475],{"class":72},[62,49061,2478],{"class":68},[62,49063,39544],{"class":72},[62,49065,39547],{"class":889},[62,49067,39583],{"class":72},[62,49069,23740],{"class":68},[62,49071,39510],{"class":72},[62,49073,6283],{"class":889},[62,49075,1133],{"class":72},[62,49077,49078],{"class":64,"line":173},[62,49079,79],{"emptyLinePlaceholder":13},[62,49081,49082,49084,49086,49088,49090],{"class":64,"line":179},[62,49083,2143],{"class":72},[62,49085,39602],{"class":68},[62,49087,2109],{"class":72},[62,49089,39489],{"class":1675},[62,49091,2212],{"class":72},[62,49093,49094,49096,49098,49100,49102,49104,49106],{"class":64,"line":185},[62,49095,11710],{"class":68},[62,49097,38982],{"class":122},[62,49099,2475],{"class":72},[62,49101,23740],{"class":68},[62,49103,39510],{"class":72},[62,49105,6283],{"class":889},[62,49107,1133],{"class":72},[62,49109,49110],{"class":64,"line":191},[62,49111,379],{"class":72},[22,49113,49114],{},"That's it! You have created the Article Client in the Content Service. The Article Client will utilize the Feign client under the hood to handle the HTTP requests and responses.",[26,49116,48233],{"id":49117},"testing-the-implemented-api",[22,49119,49120],{},[653,49121],{"alt":49122,"src":49123},"IntelliJ HTTP Client","/images/blog/2023/06/14/intellij-http-client.png",[22,49125,49126],{},"Now that we have implemented the REST API in the Article Service and created the Article Client in the Content Service, we can test the API endpoints to ensure they work as expected. To test the API, follow these steps:",[34,49128,49129,49132,49135],{},[37,49130,49131],{},"Start both the Article Service and the Content Service applications.",[37,49133,49134],{},"Use tools like curl or Postman to make HTTP requests to the API endpoints of the Content Service.",[37,49136,49137],{},"Use the Article Client methods in the Content Service to retrieve, create, update, or delete articles in the Article Service.",[22,49139,49140],{},"For example, you can use the following HTTP requests to test the API endpoints:",[915,49142,49143,49149,49155,49161,49167],{},[37,49144,49145,49146],{},"To retrieve all articles: GET ",[59,49147,49148],{},"http://localhost:8081/api/content/articles",[37,49150,49151,49152],{},"To retrieve a specific article: GET ",[59,49153,49154],{},"http://localhost:8081/api/content/articles/{id}",[37,49156,49157,49158,49160],{},"To create a new article: POST ",[59,49159,49148],{}," with the article data in the request body",[37,49162,49163,49164,49166],{},"To update an existing article: PUT ",[59,49165,49154],{}," with the updated article data in the request body",[37,49168,49169,49170],{},"To delete an article: DELETE ",[59,49171,49154],{},[22,49173,49174,49175,49178],{},"Make sure to replace ",[59,49176,49177],{},"{id}"," with the actual ID of the article you want to retrieve, update, or delete.",[22,49180,49181],{},"That's it! You have successfully implemented the REST API using HTTP interface clients in the Spring Framework 6 and Spring Boot 3. You now have a fully functional API that can be consumed by other services through the Article Client. This approach helps reduce boilerplate code and simplifies the process of calling external services.",[26,49183,1499],{"id":1498},[22,49185,49186],{},"In this blog post, we covered the concept of HTTP interface clients in the Spring Framework 6 and Spring Boot 3. We explored the process of creating a REST API in the Article Service and consuming it through an Article Client in the Content Service. The use of HTTP interface clients simplifies the process of making HTTP requests to external services and reduces the amount of low-level code that needs to be written. By following the steps and code snippets provided in this blog post, you should now have a good understanding of how to create and utilize HTTP interface clients in your Spring applications.",[1527,49188,49189],{},"html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}html pre.shiki code .spoOn, html code.shiki .spoOn{--shiki-default:#E36209;--shiki-dark:#FFAB70;--shiki-sepia:#E36209}",{"title":57,"searchDepth":76,"depth":76,"links":49191},[49192,49193,49194,49195,49196,49197],{"id":48218,"depth":76,"text":48219},{"id":15195,"depth":76,"text":48224},{"id":48256,"depth":76,"text":48227},{"id":48844,"depth":76,"text":48230},{"id":49117,"depth":76,"text":48233},{"id":1498,"depth":76,"text":1499},{"_id":49199,"path":49200,"title":49201,"description":49201,"meta":49202,"body":49207},"content/blog/2017/06/14/spring-boot-defining-requestmapping-handler-methods.md","/blog/2017/06/14/spring-boot-defining-requestmapping-handler-methods","Spring Boot Defining @RequestMapping handler methods",{"slug":49203,"date":49204,"published":13,"tags":49205,"author":-1,"cover":49206,"excerpt":-1},"spring-boot-defining-requestmapping-handler-methods","2017-06-14T08:36:35-04:00",[11002],"./pexels-photo-374074-760x506.jpeg",{"type":19,"value":49208,"toc":49738},[49209,49212,49254,49258,49261,49467,49470,49474,49481,49664,49673,49678,49681,49715,49722,49724,49727,49735],[22,49210,49211],{},"In this article, we are going to take a look at what happens when you define a method that is annotated with @RequestMapping. @RequestMapping is one of the most widely used Spring MVC annotation. RequestMapping annotation is used to map web requests onto specific handler classes and/or handler methods. If you have done any work with Spring MVC you have undoubtedly come across this annotation before. The reason we are talking about this is that I had a student ask me the following question.",[29685,49213,49214],{},[22,49215,49216,49217,49220,49221,49224,49225,49228,49229,49232,49233,49236,49237,49240,49241,49243,49244,49246,49247,49249,49250,49253],{},"Hi Dan, I'm really enjoying the course so far but there are a few things that don't quite fit to my head. In the lesson 43 (Error Handling) we have ",[59,49218,49219],{},"CustomErrorController"," class where we are injecting ",[59,49222,49223],{},"ErrorAttributes"," inside the constructor via ",[59,49226,49227],{},"@AutoWired"," so I assume that Spring boot will initialize that object automatically for us. Now what about the ",[59,49230,49231],{},"error()"," method? How did the parameters ",[59,49234,49235],{},"Model model"," and ",[59,49238,49239],{},"HttpServletRequest request"," came up? Is spring boot going to initialize those objects for us too? (apparently yes cause they just work) But then why ",[59,49242,49227],{}," is not needed to automatically inject those objects just like what happens in constructor? Furthermore how could I know that I need the above 2 parameters? Could there be any further parameters in the ",[59,49245,21875],{}," method that I should be aware of? If yes would they be initialized automagically like ",[59,49248,11933],{}," and ",[59,49251,49252],{},"HttpServletRequest"," ? Thanks",[26,49255,49257],{"id":49256},"requestmapping-examples","@RequestMapping Examples",[22,49259,49260],{},"Before we dive into the solution and explain why this happening I want to catch the rest of you up with what is going on. I had a custom error controller that I was using that looked something like this. ",[52,49262,49264],{"className":54,"code":49263,"language":56,"meta":57,"style":57},"@Controller\npublic class CustomErrorController implements ErrorController {\n\n private static final String ERROR_PATH = \"/error\";\n private static final String ERROR_TEMPLATE = \"customError\";\n\n private final ErrorAttributes errorAttributes;\n\n @Autowired\n public CustomErrorController(ErrorAttributes errorAttributes) {\n this.errorAttributes = errorAttributes;\n }\n\n @RequestMapping(ERROR_PATH)\n public String error(Model model, HttpServletRequest request) {\n return \"SOME_ERROR_STRING\";\n }\n\n @Override\n public String getErrorPath() {\n return null;\n }\n\n}\n",[59,49265,49266,49272,49288,49292,49310,49328,49332,49341,49345,49351,49365,49377,49381,49385,49394,49413,49422,49426,49430,49436,49447,49455,49459,49463],{"__ignoreMap":57},[62,49267,49268,49270],{"class":64,"line":65},[62,49269,942],{"class":72},[62,49271,16624],{"class":68},[62,49273,49274,49276,49278,49281,49283,49286],{"class":64,"line":76},[62,49275,116],{"class":68},[62,49277,119],{"class":68},[62,49279,49280],{"class":122}," CustomErrorController",[62,49282,13520],{"class":68},[62,49284,49285],{"class":122}," ErrorController",[62,49287,126],{"class":72},[62,49289,49290],{"class":64,"line":82},[62,49291,79],{"emptyLinePlaceholder":13},[62,49293,49294,49296,49298,49300,49303,49305,49308],{"class":64,"line":89},[62,49295,137],{"class":68},[62,49297,2101],{"class":68},[62,49299,458],{"class":68},[62,49301,49302],{"class":72}," String ERROR_PATH ",[62,49304,146],{"class":68},[62,49306,49307],{"class":1675}," \"/error\"",[62,49309,153],{"class":72},[62,49311,49312,49314,49316,49318,49321,49323,49326],{"class":64,"line":95},[62,49313,137],{"class":68},[62,49315,2101],{"class":68},[62,49317,458],{"class":68},[62,49319,49320],{"class":72}," String ERROR_TEMPLATE ",[62,49322,146],{"class":68},[62,49324,49325],{"class":1675}," \"customError\"",[62,49327,153],{"class":72},[62,49329,49330],{"class":64,"line":101},[62,49331,79],{"emptyLinePlaceholder":13},[62,49333,49334,49336,49338],{"class":64,"line":107},[62,49335,137],{"class":68},[62,49337,458],{"class":68},[62,49339,49340],{"class":72}," ErrorAttributes errorAttributes;\n",[62,49342,49343],{"class":64,"line":113},[62,49344,79],{"emptyLinePlaceholder":13},[62,49346,49347,49349],{"class":64,"line":129},[62,49348,2143],{"class":72},[62,49350,11687],{"class":68},[62,49352,49353,49355,49357,49360,49363],{"class":64,"line":134},[62,49354,194],{"class":68},[62,49356,49280],{"class":122},[62,49358,49359],{"class":72},"(ErrorAttributes ",[62,49361,49362],{"class":889},"errorAttributes",[62,49364,768],{"class":72},[62,49366,49367,49369,49372,49374],{"class":64,"line":156},[62,49368,2405],{"class":149},[62,49370,49371],{"class":72},".errorAttributes ",[62,49373,146],{"class":68},[62,49375,49376],{"class":72}," errorAttributes;\n",[62,49378,49379],{"class":64,"line":161},[62,49380,223],{"class":72},[62,49382,49383],{"class":64,"line":167},[62,49384,79],{"emptyLinePlaceholder":13},[62,49386,49387,49389,49391],{"class":64,"line":173},[62,49388,2143],{"class":72},[62,49390,10592],{"class":68},[62,49392,49393],{"class":72},"(ERROR_PATH)\n",[62,49395,49396,49398,49400,49402,49404,49406,49409,49411],{"class":64,"line":179},[62,49397,194],{"class":68},[62,49399,2469],{"class":72},[62,49401,21875],{"class":122},[62,49403,22475],{"class":72},[62,49405,16671],{"class":889},[62,49407,49408],{"class":72},", HttpServletRequest ",[62,49410,13262],{"class":889},[62,49412,768],{"class":72},[62,49414,49415,49417,49420],{"class":64,"line":185},[62,49416,360],{"class":68},[62,49418,49419],{"class":1675}," \"SOME_ERROR_STRING\"",[62,49421,153],{"class":72},[62,49423,49424],{"class":64,"line":191},[62,49425,223],{"class":72},[62,49427,49428],{"class":64,"line":209},[62,49429,79],{"emptyLinePlaceholder":13},[62,49431,49432,49434],{"class":64,"line":220},[62,49433,2143],{"class":72},[62,49435,13555],{"class":68},[62,49437,49438,49440,49442,49445],{"class":64,"line":226},[62,49439,194],{"class":68},[62,49441,2469],{"class":72},[62,49443,49444],{"class":122},"getErrorPath",[62,49446,206],{"class":72},[62,49448,49449,49451,49453],{"class":64,"line":231},[62,49450,360],{"class":68},[62,49452,13324],{"class":149},[62,49454,153],{"class":72},[62,49456,49457],{"class":64,"line":236},[62,49458,223],{"class":72},[62,49460,49461],{"class":64,"line":242},[62,49462,79],{"emptyLinePlaceholder":13},[62,49464,49465],{"class":64,"line":247},[62,49466,379],{"class":72},[22,49468,49469],{},"As the student pointed out when we create the constructor and annotated it with @Autowired (No longer needed in Spring 4.3+) Spring managed the constructor injection of the managed bean ErrorAttributes. This is one of those beans that the Spring Framework manages for us. So now the question is I have this method called error and in this method, I have some method arguments, so how are those being supplied for me? ",[636,49471,49473],{"id":49472},"defining-requestmapping-handler-methods","Defining @RequestMapping handler methods",[22,49475,49476,49477,49480],{},"To understand the problem we must first understand that the ",[4534,49478,49479],{},"Custom Error Controller"," I created is nothing special. In fact, we can create a regular controller and it would act the same. This is an example of a Home Controller I used to respond to the \"/home\" request. If you look closely now though, I have 2 different method parameters being passed to my controller. What the heck is going on here? ",[52,49482,49484],{"className":54,"code":49483,"language":56,"meta":57,"style":57},"package com.therealdanvega.controller;\n\nimport com.therealdanvega.service.HomeService;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.web.bind.annotation.RequestMapping;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\n@Controller\npublic class HomeController {\n\n private HomeService homeService;\n\n public HomeController(HomeService homeService) {\n this.homeService = homeService;\n }\n\n @RequestMapping(\"/home\")\n public String home(HttpServletRequest request, HttpServletResponse response) {\n return this.homeService.getMsg(request);\n }\n\n}\n",[59,49485,49486,49493,49497,49504,49511,49518,49522,49529,49536,49540,49546,49556,49560,49567,49571,49585,49597,49601,49605,49618,49637,49652,49656,49660],{"__ignoreMap":57},[62,49487,49488,49490],{"class":64,"line":65},[62,49489,69],{"class":68},[62,49491,49492],{"class":72}," com.therealdanvega.controller;\n",[62,49494,49495],{"class":64,"line":76},[62,49496,79],{"emptyLinePlaceholder":13},[62,49498,49499,49501],{"class":64,"line":82},[62,49500,27875],{"class":68},[62,49502,49503],{"class":72}," com.therealdanvega.service.HomeService;\n",[62,49505,49506,49508],{"class":64,"line":89},[62,49507,27875],{"class":68},[62,49509,49510],{"class":72}," org.springframework.stereotype.Controller;\n",[62,49512,49513,49515],{"class":64,"line":95},[62,49514,27875],{"class":68},[62,49516,49517],{"class":72}," org.springframework.web.bind.annotation.RequestMapping;\n",[62,49519,49520],{"class":64,"line":101},[62,49521,79],{"emptyLinePlaceholder":13},[62,49523,49524,49526],{"class":64,"line":107},[62,49525,27875],{"class":68},[62,49527,49528],{"class":72}," javax.servlet.http.HttpServletRequest;\n",[62,49530,49531,49533],{"class":64,"line":113},[62,49532,27875],{"class":68},[62,49534,49535],{"class":72}," javax.servlet.http.HttpServletResponse;\n",[62,49537,49538],{"class":64,"line":129},[62,49539,79],{"emptyLinePlaceholder":13},[62,49541,49542,49544],{"class":64,"line":134},[62,49543,942],{"class":72},[62,49545,16624],{"class":68},[62,49547,49548,49550,49552,49554],{"class":64,"line":156},[62,49549,116],{"class":68},[62,49551,119],{"class":68},[62,49553,25636],{"class":122},[62,49555,126],{"class":72},[62,49557,49558],{"class":64,"line":161},[62,49559,79],{"emptyLinePlaceholder":13},[62,49561,49562,49564],{"class":64,"line":167},[62,49563,137],{"class":68},[62,49565,49566],{"class":72}," HomeService homeService;\n",[62,49568,49569],{"class":64,"line":173},[62,49570,79],{"emptyLinePlaceholder":13},[62,49572,49573,49575,49577,49580,49583],{"class":64,"line":179},[62,49574,194],{"class":68},[62,49576,25636],{"class":122},[62,49578,49579],{"class":72},"(HomeService ",[62,49581,49582],{"class":889},"homeService",[62,49584,768],{"class":72},[62,49586,49587,49589,49592,49594],{"class":64,"line":185},[62,49588,2405],{"class":149},[62,49590,49591],{"class":72},".homeService ",[62,49593,146],{"class":68},[62,49595,49596],{"class":72}," homeService;\n",[62,49598,49599],{"class":64,"line":191},[62,49600,223],{"class":72},[62,49602,49603],{"class":64,"line":209},[62,49604,79],{"emptyLinePlaceholder":13},[62,49606,49607,49609,49611,49613,49616],{"class":64,"line":220},[62,49608,2143],{"class":72},[62,49610,10592],{"class":68},[62,49612,2109],{"class":72},[62,49614,49615],{"class":1675},"\"/home\"",[62,49617,2212],{"class":72},[62,49619,49620,49622,49624,49626,49628,49630,49633,49635],{"class":64,"line":226},[62,49621,194],{"class":68},[62,49623,2469],{"class":72},[62,49625,18647],{"class":122},[62,49627,16663],{"class":72},[62,49629,13262],{"class":889},[62,49631,49632],{"class":72},", HttpServletResponse ",[62,49634,13389],{"class":889},[62,49636,768],{"class":72},[62,49638,49639,49641,49643,49646,49649],{"class":64,"line":231},[62,49640,360],{"class":68},[62,49642,9961],{"class":149},[62,49644,49645],{"class":72},".homeService.",[62,49647,49648],{"class":122},"getMsg",[62,49650,49651],{"class":72},"(request);\n",[62,49653,49654],{"class":64,"line":236},[62,49655,223],{"class":72},[62,49657,49658],{"class":64,"line":242},[62,49659,79],{"emptyLinePlaceholder":13},[62,49661,49662],{"class":64,"line":247},[62,49663,379],{"class":72},[22,49665,49666,49667,49672],{},"The real secret to what is happening here is the @RequestMapping annotation on the method. If you look back to our CustomErrrorController you will see the same annotation on the method in question. If you take a look at the Spring MVC documentation on ",[677,49668,49671],{"href":49669,"rel":49670},"http://docs.spring.io/spring/docs/current/spring-framework-reference/html/mvc.html#mvc-ann-methods",[681],"@RequestMapping handler Methods",": ",[29685,49674,49675],{},[22,49676,49677],{},"@RequestMapping handler methods can have very flexible signatures. Most arguments can be used in arbitrary order with the only exception being BindingResult arguments.",[22,49679,49680],{},"What this means is that when we define a method and use the annotation @RequestMapping we have access to a number of method arguments that help us get information like;",[915,49682,49683,49686,49689,49692,49695,49698,49701,49704,49706,49709,49712],{},[37,49684,49685],{},"Request & Response Objects",[37,49687,49688],{},"Web Request",[37,49690,49691],{},"Session Information",[37,49693,49694],{},"Http Method",[37,49696,49697],{},"Path Variables",[37,49699,49700],{},"Request Parameters",[37,49702,49703],{},"Request Body",[37,49705,11933],{},[37,49707,49708],{},"Errors",[37,49710,49711],{},"Binding Results",[37,49713,49714],{},"Much More...",[22,49716,49717,49718,2755],{},"If you want to see all of the available ",[677,49719,49721],{"href":49669,"rel":49720},[681],"@RequestMapping handler methods check out the documentation here",[26,49723,1499],{"id":1498},[22,49725,49726],{},"Spring does a lot of things behind the scenes for us and I completely understand how this can be confusing at times. You can usually find the answers in the documentation but at the same time, we don't always know what to look for. I can assure you I don't remember all of the method arguments and that is why I have that link above bookmarked. ",[22,49728,49729],{},[4534,49730,49731,49734],{},[646,49732,49733],{},"Question:"," Is there anything that happens behind the scenes in Spring you don't quite understand?",[1527,49736,49737],{},"html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}html pre.shiki code .spoOn, html code.shiki .spoOn{--shiki-default:#E36209;--shiki-dark:#FFAB70;--shiki-sepia:#E36209}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}",{"title":57,"searchDepth":76,"depth":76,"links":49739},[49740,49743],{"id":49256,"depth":76,"text":49257,"children":49741},[49742],{"id":49472,"depth":82,"text":49473},{"id":1498,"depth":76,"text":1499},{"_id":49745,"path":49746,"title":49747,"description":49747,"meta":49748,"body":49754},"content/blog/2017/06/12/bootstrapping-angular-application.md","/blog/2017/06/12/bootstrapping-angular-application","Bootstrapping your Angular Application",{"slug":49749,"date":49750,"published":13,"tags":49751,"author":-1,"cover":49753,"excerpt":-1},"bootstrapping-angular-application","2017-06-12T11:40:51-04:00",[49752],"Angular","./pexels-photo-177598-760x506.jpeg",{"type":19,"value":49755,"toc":50761},[49756,49759,49763,49766,49772,49775,49788,49791,49795,49804,50229,50232,50619,50622,50635,50638,50731,50740,50745,50749,50752,50758],[22,49757,49758],{},"I recently wrote an article about how to get started with the Angular CLI. This is an awesome productivity tool when it comes to getting started with Angular. With it we can quickly create, build and run our applications. This is really helpful but it also adds a little layer of magic to your application. If you always use the CLI but don't take the time to appreciate what is happening behind the scenes you won't understand how to fix problems when they come up. In this article, we are going to look at how Angular boots up your application. ",[26,49760,49762],{"id":49761},"creating-a-new-angular-application","Creating a new Angular Application",[22,49764,49765],{},"When you use the Angular CLI to create a new application you get a bunch of files and directories created for you. ",[22,49767,49768],{},[653,49769],{"alt":49770,"src":49771},"Creating Angular App","./2017-06-05_10-10-56.png",[22,49773,49774],{},"We also know that we can use the CLI to run the application using the following command. ",[52,49776,49778],{"className":1663,"code":49777,"language":1665,"meta":57,"style":57},"ng serve\n",[59,49779,49780],{"__ignoreMap":57},[62,49781,49782,49785],{"class":64,"line":65},[62,49783,49784],{"class":122},"ng",[62,49786,49787],{"class":1675}," serve\n",[22,49789,49790],{},"This is great and makes our lives easier as we don't have to worry about the nuts and bolts of the application. When we start our application though we don't know what Angular is doing under the hood so let's actually walk through that now. ",[26,49792,49794],{"id":49793},"bootstrapping-angular","Bootstrapping Angular",[22,49796,49797,49798,49803],{},"Every application we build has an entry point. When we run the hello-angular application above using ",[646,49799,49800],{},[4534,49801,49802],{},"ng serve"," it will first look at the file angular-cli.json. This file contains some information about the application but the main setting we want to look at here is line 15 which tells us \"main.ts\" is our main file.",[52,49805,49807],{"className":3671,"code":49806,"language":3673,"meta":57,"style":57},"{\n \"$schema\": \"./node_modules/@angular/cli/lib/config/schema.json\",\n \"project\": {\n \"name\": \"hello-angular\"\n },\n \"apps\": [\n {\n \"root\": \"src\",\n \"outDir\": \"dist\",\n \"assets\": [\n \"assets\",\n \"favicon.ico\"\n ],\n \"index\": \"index.html\",\n \"main\": \"main.ts\",\n \"polyfills\": \"polyfills.ts\",\n \"test\": \"test.ts\",\n \"tsconfig\": \"tsconfig.app.json\",\n \"testTsconfig\": \"tsconfig.spec.json\",\n \"prefix\": \"app\",\n \"styles\": [\n \"styles.css\"\n ],\n \"scripts\": [],\n \"environmentSource\": \"environments/environment.ts\",\n \"environments\": {\n \"dev\": \"environments/environment.ts\",\n \"prod\": \"environments/environment.prod.ts\"\n }\n }\n ],\n \"e2e\": {\n \"protractor\": {\n \"config\": \"./protractor.conf.js\"\n }\n },\n \"lint\": [\n {\n \"project\": \"src/tsconfig.app.json\"\n },\n {\n \"project\": \"src/tsconfig.spec.json\"\n },\n {\n \"project\": \"e2e/tsconfig.e2e.json\"\n }\n ],\n \"test\": {\n \"karma\": {\n \"config\": \"./karma.conf.js\"\n }\n },\n \"defaults\": {\n \"styleExt\": \"css\",\n \"component\": {}\n }\n}\n",[59,49808,49809,49813,49825,49832,49842,49846,49853,49858,49870,49882,49889,49896,49901,49906,49918,49930,49942,49954,49966,49978,49990,49997,50002,50006,50014,50026,50033,50044,50054,50058,50062,50067,50074,50081,50091,50095,50099,50106,50110,50120,50125,50129,50138,50142,50146,50155,50159,50163,50170,50177,50186,50190,50194,50201,50213,50221,50225],{"__ignoreMap":57},[62,49810,49811],{"class":64,"line":65},[62,49812,3680],{"class":72},[62,49814,49815,49818,49820,49823],{"class":64,"line":76},[62,49816,49817],{"class":149}," \"$schema\"",[62,49819,3696],{"class":72},[62,49821,49822],{"class":1675},"\"./node_modules/@angular/cli/lib/config/schema.json\"",[62,49824,3338],{"class":72},[62,49826,49827,49830],{"class":64,"line":82},[62,49828,49829],{"class":149}," \"project\"",[62,49831,3688],{"class":72},[62,49833,49834,49837,49839],{"class":64,"line":89},[62,49835,49836],{"class":149}," \"name\"",[62,49838,3696],{"class":72},[62,49840,49841],{"class":1675},"\"hello-angular\"\n",[62,49843,49844],{"class":64,"line":95},[62,49845,32848],{"class":72},[62,49847,49848,49851],{"class":64,"line":101},[62,49849,49850],{"class":149}," \"apps\"",[62,49852,3709],{"class":72},[62,49854,49855],{"class":64,"line":107},[62,49856,49857],{"class":72}," {\n",[62,49859,49860,49863,49865,49868],{"class":64,"line":113},[62,49861,49862],{"class":149}," \"root\"",[62,49864,3696],{"class":72},[62,49866,49867],{"class":1675},"\"src\"",[62,49869,3338],{"class":72},[62,49871,49872,49875,49877,49880],{"class":64,"line":129},[62,49873,49874],{"class":149}," \"outDir\"",[62,49876,3696],{"class":72},[62,49878,49879],{"class":1675},"\"dist\"",[62,49881,3338],{"class":72},[62,49883,49884,49887],{"class":64,"line":134},[62,49885,49886],{"class":149}," \"assets\"",[62,49888,3709],{"class":72},[62,49890,49891,49894],{"class":64,"line":156},[62,49892,49893],{"class":1675}," \"assets\"",[62,49895,3338],{"class":72},[62,49897,49898],{"class":64,"line":161},[62,49899,49900],{"class":1675}," \"favicon.ico\"\n",[62,49902,49903],{"class":64,"line":167},[62,49904,49905],{"class":72}," ],\n",[62,49907,49908,49911,49913,49916],{"class":64,"line":173},[62,49909,49910],{"class":149}," \"index\"",[62,49912,3696],{"class":72},[62,49914,49915],{"class":1675},"\"index.html\"",[62,49917,3338],{"class":72},[62,49919,49920,49923,49925,49928],{"class":64,"line":179},[62,49921,49922],{"class":149}," \"main\"",[62,49924,3696],{"class":72},[62,49926,49927],{"class":1675},"\"main.ts\"",[62,49929,3338],{"class":72},[62,49931,49932,49935,49937,49940],{"class":64,"line":185},[62,49933,49934],{"class":149}," \"polyfills\"",[62,49936,3696],{"class":72},[62,49938,49939],{"class":1675},"\"polyfills.ts\"",[62,49941,3338],{"class":72},[62,49943,49944,49947,49949,49952],{"class":64,"line":191},[62,49945,49946],{"class":149}," \"test\"",[62,49948,3696],{"class":72},[62,49950,49951],{"class":1675},"\"test.ts\"",[62,49953,3338],{"class":72},[62,49955,49956,49959,49961,49964],{"class":64,"line":209},[62,49957,49958],{"class":149}," \"tsconfig\"",[62,49960,3696],{"class":72},[62,49962,49963],{"class":1675},"\"tsconfig.app.json\"",[62,49965,3338],{"class":72},[62,49967,49968,49971,49973,49976],{"class":64,"line":220},[62,49969,49970],{"class":149}," \"testTsconfig\"",[62,49972,3696],{"class":72},[62,49974,49975],{"class":1675},"\"tsconfig.spec.json\"",[62,49977,3338],{"class":72},[62,49979,49980,49983,49985,49988],{"class":64,"line":226},[62,49981,49982],{"class":149}," \"prefix\"",[62,49984,3696],{"class":72},[62,49986,49987],{"class":1675},"\"app\"",[62,49989,3338],{"class":72},[62,49991,49992,49995],{"class":64,"line":231},[62,49993,49994],{"class":149}," \"styles\"",[62,49996,3709],{"class":72},[62,49998,49999],{"class":64,"line":236},[62,50000,50001],{"class":1675}," \"styles.css\"\n",[62,50003,50004],{"class":64,"line":242},[62,50005,49905],{"class":72},[62,50007,50008,50011],{"class":64,"line":247},[62,50009,50010],{"class":149}," \"scripts\"",[62,50012,50013],{"class":72},": [],\n",[62,50015,50016,50019,50021,50024],{"class":64,"line":252},[62,50017,50018],{"class":149}," \"environmentSource\"",[62,50020,3696],{"class":72},[62,50022,50023],{"class":1675},"\"environments/environment.ts\"",[62,50025,3338],{"class":72},[62,50027,50028,50031],{"class":64,"line":257},[62,50029,50030],{"class":149}," \"environments\"",[62,50032,3688],{"class":72},[62,50034,50035,50038,50040,50042],{"class":64,"line":271},[62,50036,50037],{"class":149}," \"dev\"",[62,50039,3696],{"class":72},[62,50041,50023],{"class":1675},[62,50043,3338],{"class":72},[62,50045,50046,50049,50051],{"class":64,"line":281},[62,50047,50048],{"class":149}," \"prod\"",[62,50050,3696],{"class":72},[62,50052,50053],{"class":1675},"\"environments/environment.prod.ts\"\n",[62,50055,50056],{"class":64,"line":286},[62,50057,29042],{"class":72},[62,50059,50060],{"class":64,"line":291},[62,50061,223],{"class":72},[62,50063,50064],{"class":64,"line":296},[62,50065,50066],{"class":72}," ],\n",[62,50068,50069,50072],{"class":64,"line":302},[62,50070,50071],{"class":149}," \"e2e\"",[62,50073,3688],{"class":72},[62,50075,50076,50079],{"class":64,"line":308},[62,50077,50078],{"class":149}," \"protractor\"",[62,50080,3688],{"class":72},[62,50082,50083,50086,50088],{"class":64,"line":314},[62,50084,50085],{"class":149}," \"config\"",[62,50087,3696],{"class":72},[62,50089,50090],{"class":1675},"\"./protractor.conf.js\"\n",[62,50092,50093],{"class":64,"line":320},[62,50094,223],{"class":72},[62,50096,50097],{"class":64,"line":326},[62,50098,32848],{"class":72},[62,50100,50101,50104],{"class":64,"line":338},[62,50102,50103],{"class":149}," \"lint\"",[62,50105,3709],{"class":72},[62,50107,50108],{"class":64,"line":343},[62,50109,49857],{"class":72},[62,50111,50112,50115,50117],{"class":64,"line":357},[62,50113,50114],{"class":149}," \"project\"",[62,50116,3696],{"class":72},[62,50118,50119],{"class":1675},"\"src/tsconfig.app.json\"\n",[62,50121,50122],{"class":64,"line":366},[62,50123,50124],{"class":72}," },\n",[62,50126,50127],{"class":64,"line":371},[62,50128,49857],{"class":72},[62,50130,50131,50133,50135],{"class":64,"line":376},[62,50132,50114],{"class":149},[62,50134,3696],{"class":72},[62,50136,50137],{"class":1675},"\"src/tsconfig.spec.json\"\n",[62,50139,50140],{"class":64,"line":16333},[62,50141,50124],{"class":72},[62,50143,50144],{"class":64,"line":16349},[62,50145,49857],{"class":72},[62,50147,50148,50150,50152],{"class":64,"line":16365},[62,50149,50114],{"class":149},[62,50151,3696],{"class":72},[62,50153,50154],{"class":1675},"\"e2e/tsconfig.e2e.json\"\n",[62,50156,50157],{"class":64,"line":16381},[62,50158,223],{"class":72},[62,50160,50161],{"class":64,"line":16402},[62,50162,50066],{"class":72},[62,50164,50165,50168],{"class":64,"line":16411},[62,50166,50167],{"class":149}," \"test\"",[62,50169,3688],{"class":72},[62,50171,50172,50175],{"class":64,"line":16427},[62,50173,50174],{"class":149}," \"karma\"",[62,50176,3688],{"class":72},[62,50178,50179,50181,50183],{"class":64,"line":16448},[62,50180,50085],{"class":149},[62,50182,3696],{"class":72},[62,50184,50185],{"class":1675},"\"./karma.conf.js\"\n",[62,50187,50188],{"class":64,"line":16457},[62,50189,223],{"class":72},[62,50191,50192],{"class":64,"line":16466},[62,50193,32848],{"class":72},[62,50195,50196,50199],{"class":64,"line":16471},[62,50197,50198],{"class":149}," \"defaults\"",[62,50200,3688],{"class":72},[62,50202,50203,50206,50208,50211],{"class":64,"line":16487},[62,50204,50205],{"class":149}," \"styleExt\"",[62,50207,3696],{"class":72},[62,50209,50210],{"class":1675},"\"css\"",[62,50212,3338],{"class":72},[62,50214,50215,50218],{"class":64,"line":16504},[62,50216,50217],{"class":149}," \"component\"",[62,50219,50220],{"class":72},": {}\n",[62,50222,50223],{"class":64,"line":16517},[62,50224,3731],{"class":72},[62,50226,50227],{"class":64,"line":16523},[62,50228,379],{"class":72},[22,50230,50231],{},"If we look at the file src/main.ts we will see the following code. ",[52,50233,50237],{"className":50234,"code":50235,"language":50236,"meta":57,"style":57},"language-typescript shiki shiki-themes github-light github-dark github-light","import { enableProdMode } from '@angular/core';\nimport { platformBrowserDynamic } from '@angular/platform-browser-dynamic';\n\nimport { AppModule } from './app/app.module';\nimport { environment } from './environments/environment';\n\nif (environment.production) {\n enableProdMode();\n}\n\nplatformBrowserDynamic().bootstrapModule(AppModule);\n\nThe last line is where the magic happens and the bootstrap process boots an Angular module called AppModule. If you look under src/app you will see a file, app.module.ts and this is our AppModule that gets called. \n\nimport { BrowserModule } from '@angular/platform-browser';\nimport { NgModule } from '@angular/core';\nimport { FormsModule } from '@angular/forms';\nimport { HttpModule } from '@angular/http';\n\nimport { AppComponent } from './app.component';\nimport { UsersComponent } from './users/users.component';\nimport { UsersListComponent } from './users/users-list/users-list.component';\n\n@NgModule({\n declarations: [\n AppComponent,\n UsersComponent,\n UsersListComponent\n ],\n imports: [\n BrowserModule,\n FormsModule,\n HttpModule\n ],\n providers: [],\n bootstrap: [AppComponent]\n})\nexport class AppModule { }\n","typescript",[59,50238,50239,50253,50267,50271,50285,50299,50303,50310,50317,50321,50325,50338,50342,50426,50430,50444,50457,50471,50485,50489,50503,50517,50531,50535,50545,50550,50555,50560,50565,50569,50574,50579,50584,50589,50593,50598,50603,50608],{"__ignoreMap":57},[62,50240,50241,50243,50246,50248,50251],{"class":64,"line":65},[62,50242,27875],{"class":68},[62,50244,50245],{"class":72}," { enableProdMode } ",[62,50247,3507],{"class":68},[62,50249,50250],{"class":1675}," '@angular/core'",[62,50252,153],{"class":72},[62,50254,50255,50257,50260,50262,50265],{"class":64,"line":76},[62,50256,27875],{"class":68},[62,50258,50259],{"class":72}," { platformBrowserDynamic } ",[62,50261,3507],{"class":68},[62,50263,50264],{"class":1675}," '@angular/platform-browser-dynamic'",[62,50266,153],{"class":72},[62,50268,50269],{"class":64,"line":82},[62,50270,79],{"emptyLinePlaceholder":13},[62,50272,50273,50275,50278,50280,50283],{"class":64,"line":89},[62,50274,27875],{"class":68},[62,50276,50277],{"class":72}," { AppModule } ",[62,50279,3507],{"class":68},[62,50281,50282],{"class":1675}," './app/app.module'",[62,50284,153],{"class":72},[62,50286,50287,50289,50292,50294,50297],{"class":64,"line":95},[62,50288,27875],{"class":68},[62,50290,50291],{"class":72}," { environment } ",[62,50293,3507],{"class":68},[62,50295,50296],{"class":1675}," './environments/environment'",[62,50298,153],{"class":72},[62,50300,50301],{"class":64,"line":101},[62,50302,79],{"emptyLinePlaceholder":13},[62,50304,50305,50307],{"class":64,"line":107},[62,50306,34116],{"class":68},[62,50308,50309],{"class":72}," (environment.production) {\n",[62,50311,50312,50315],{"class":64,"line":113},[62,50313,50314],{"class":122}," enableProdMode",[62,50316,822],{"class":72},[62,50318,50319],{"class":64,"line":129},[62,50320,379],{"class":72},[62,50322,50323],{"class":64,"line":134},[62,50324,79],{"emptyLinePlaceholder":13},[62,50326,50327,50330,50332,50335],{"class":64,"line":156},[62,50328,50329],{"class":122},"platformBrowserDynamic",[62,50331,3229],{"class":72},[62,50333,50334],{"class":122},"bootstrapModule",[62,50336,50337],{"class":72},"(AppModule);\n",[62,50339,50340],{"class":64,"line":161},[62,50341,79],{"emptyLinePlaceholder":13},[62,50343,50344,50347,50349,50352,50355,50357,50360,50363,50366,50369,50371,50373,50376,50378,50381,50384,50386,50389,50391,50393,50395,50397,50399,50402,50405,50407,50410,50413,50415,50418,50421,50423],{"class":64,"line":167},[62,50345,50346],{"class":72},"The last line is where the magic happens and the bootstrap process boots an Angular ",[62,50348,32813],{"class":68},[62,50350,50351],{"class":122}," called",[62,50353,50354],{"class":122}," AppModule",[62,50356,43654],{"class":72},[62,50358,50359],{"class":122},"If",[62,50361,50362],{"class":122}," you",[62,50364,50365],{"class":122}," look",[62,50367,50368],{"class":122}," under",[62,50370,15849],{"class":122},[62,50372,6936],{"class":72},[62,50374,50375],{"class":122},"app",[62,50377,50362],{"class":122},[62,50379,50380],{"class":122}," will",[62,50382,50383],{"class":122}," see",[62,50385,28870],{"class":122},[62,50387,50388],{"class":122}," file",[62,50390,976],{"class":72},[62,50392,50375],{"class":122},[62,50394,2755],{"class":72},[62,50396,32813],{"class":122},[62,50398,2755],{"class":72},[62,50400,50401],{"class":122},"ts",[62,50403,50404],{"class":122}," and",[62,50406,9961],{"class":122},[62,50408,50409],{"class":122}," is",[62,50411,50412],{"class":122}," our",[62,50414,50354],{"class":122},[62,50416,50417],{"class":122}," that",[62,50419,50420],{"class":122}," gets",[62,50422,50351],{"class":122},[62,50424,50425],{"class":72},". \n",[62,50427,50428],{"class":64,"line":173},[62,50429,79],{"emptyLinePlaceholder":13},[62,50431,50432,50434,50437,50439,50442],{"class":64,"line":179},[62,50433,27875],{"class":68},[62,50435,50436],{"class":72}," { BrowserModule } ",[62,50438,3507],{"class":68},[62,50440,50441],{"class":1675}," '@angular/platform-browser'",[62,50443,153],{"class":72},[62,50445,50446,50448,50451,50453,50455],{"class":64,"line":185},[62,50447,27875],{"class":68},[62,50449,50450],{"class":72}," { NgModule } ",[62,50452,3507],{"class":68},[62,50454,50250],{"class":1675},[62,50456,153],{"class":72},[62,50458,50459,50461,50464,50466,50469],{"class":64,"line":191},[62,50460,27875],{"class":68},[62,50462,50463],{"class":72}," { FormsModule } ",[62,50465,3507],{"class":68},[62,50467,50468],{"class":1675}," '@angular/forms'",[62,50470,153],{"class":72},[62,50472,50473,50475,50478,50480,50483],{"class":64,"line":209},[62,50474,27875],{"class":68},[62,50476,50477],{"class":72}," { HttpModule } ",[62,50479,3507],{"class":68},[62,50481,50482],{"class":1675}," '@angular/http'",[62,50484,153],{"class":72},[62,50486,50487],{"class":64,"line":220},[62,50488,79],{"emptyLinePlaceholder":13},[62,50490,50491,50493,50496,50498,50501],{"class":64,"line":226},[62,50492,27875],{"class":68},[62,50494,50495],{"class":72}," { AppComponent } ",[62,50497,3507],{"class":68},[62,50499,50500],{"class":1675}," './app.component'",[62,50502,153],{"class":72},[62,50504,50505,50507,50510,50512,50515],{"class":64,"line":231},[62,50506,27875],{"class":68},[62,50508,50509],{"class":72}," { UsersComponent } ",[62,50511,3507],{"class":68},[62,50513,50514],{"class":1675}," './users/users.component'",[62,50516,153],{"class":72},[62,50518,50519,50521,50524,50526,50529],{"class":64,"line":236},[62,50520,27875],{"class":68},[62,50522,50523],{"class":72}," { UsersListComponent } ",[62,50525,3507],{"class":68},[62,50527,50528],{"class":1675}," './users/users-list/users-list.component'",[62,50530,153],{"class":72},[62,50532,50533],{"class":64,"line":242},[62,50534,79],{"emptyLinePlaceholder":13},[62,50536,50537,50539,50542],{"class":64,"line":247},[62,50538,942],{"class":72},[62,50540,50541],{"class":122},"NgModule",[62,50543,50544],{"class":72},"({\n",[62,50546,50547],{"class":64,"line":252},[62,50548,50549],{"class":72}," declarations: [\n",[62,50551,50552],{"class":64,"line":257},[62,50553,50554],{"class":72}," AppComponent,\n",[62,50556,50557],{"class":64,"line":271},[62,50558,50559],{"class":72}," UsersComponent,\n",[62,50561,50562],{"class":64,"line":281},[62,50563,50564],{"class":72}," UsersListComponent\n",[62,50566,50567],{"class":64,"line":286},[62,50568,50066],{"class":72},[62,50570,50571],{"class":64,"line":291},[62,50572,50573],{"class":72}," imports: [\n",[62,50575,50576],{"class":64,"line":296},[62,50577,50578],{"class":72}," BrowserModule,\n",[62,50580,50581],{"class":64,"line":302},[62,50582,50583],{"class":72}," FormsModule,\n",[62,50585,50586],{"class":64,"line":308},[62,50587,50588],{"class":72}," HttpModule\n",[62,50590,50591],{"class":64,"line":314},[62,50592,50066],{"class":72},[62,50594,50595],{"class":64,"line":320},[62,50596,50597],{"class":72}," providers: [],\n",[62,50599,50600],{"class":64,"line":326},[62,50601,50602],{"class":72}," bootstrap: [AppComponent]\n",[62,50604,50605],{"class":64,"line":338},[62,50606,50607],{"class":72},"})\n",[62,50609,50610,50612,50614,50616],{"class":64,"line":343},[62,50611,14767],{"class":68},[62,50613,119],{"class":68},[62,50615,50354],{"class":122},[62,50617,50618],{"class":72}," { }\n",[22,50620,50621],{},"The AppModule contains some declarations and imports but it also contains one very important line. ",[52,50623,50625],{"className":50234,"code":50624,"language":50236,"meta":57,"style":57},"bootstrap: [AppComponent]\n",[59,50626,50627],{"__ignoreMap":57},[62,50628,50629,50632],{"class":64,"line":65},[62,50630,50631],{"class":122},"bootstrap",[62,50633,50634],{"class":72},": [AppComponent]\n",[22,50636,50637],{},"This is telling Angular what component it should load as the application's top level component. So now the app component is loaded that looks like this. ",[52,50639,50641],{"className":50234,"code":50640,"language":50236,"meta":57,"style":57},"import { Component } from '@angular/core';\n\n@Component({\n selector: 'app-root',\n templateUrl: './app.component.html',\n styleUrls: \\['./app.component.css'\\]\n})\nexport class AppComponent {\n title = 'app works!';\n}\n",[59,50642,50643,50656,50660,50669,50679,50689,50700,50704,50715,50727],{"__ignoreMap":57},[62,50644,50645,50647,50650,50652,50654],{"class":64,"line":65},[62,50646,27875],{"class":68},[62,50648,50649],{"class":72}," { Component } ",[62,50651,3507],{"class":68},[62,50653,50250],{"class":1675},[62,50655,153],{"class":72},[62,50657,50658],{"class":64,"line":76},[62,50659,79],{"emptyLinePlaceholder":13},[62,50661,50662,50664,50667],{"class":64,"line":82},[62,50663,942],{"class":72},[62,50665,50666],{"class":122},"Component",[62,50668,50544],{"class":72},[62,50670,50671,50674,50677],{"class":64,"line":89},[62,50672,50673],{"class":72}," selector: ",[62,50675,50676],{"class":1675},"'app-root'",[62,50678,3338],{"class":72},[62,50680,50681,50684,50687],{"class":64,"line":95},[62,50682,50683],{"class":72}," templateUrl: ",[62,50685,50686],{"class":1675},"'./app.component.html'",[62,50688,3338],{"class":72},[62,50690,50691,50694,50697],{"class":64,"line":101},[62,50692,50693],{"class":72}," styleUrls: \\[",[62,50695,50696],{"class":1675},"'./app.component.css'",[62,50698,50699],{"class":72},"\\]\n",[62,50701,50702],{"class":64,"line":107},[62,50703,50607],{"class":72},[62,50705,50706,50708,50710,50713],{"class":64,"line":113},[62,50707,14767],{"class":68},[62,50709,119],{"class":68},[62,50711,50712],{"class":122}," AppComponent",[62,50714,126],{"class":72},[62,50716,50717,50720,50722,50725],{"class":64,"line":129},[62,50718,50719],{"class":889}," title",[62,50721,2556],{"class":68},[62,50723,50724],{"class":1675}," 'app works!'",[62,50726,153],{"class":72},[62,50728,50729],{"class":64,"line":134},[62,50730,379],{"class":72},[22,50732,50733,50734,50739],{},"The class sets a title variable and displays it in the template and this is why you see '",[4534,50735,50736],{},[646,50737,50738],{},"app works","' when you fire up your application for the first time. ",[22,50741,50742],{},[653,50743],{"alt":49794,"src":50744},"./2017-06-12_11-40-00.png",[26,50746,50748],{"id":50747},"bootstrapping-angular-conclusion","Bootstrapping Angular Conclusion",[22,50750,50751],{},"If you have done any work in Angular 2/4 you will know that we build out our applications using this component model. What you might not of understand is how that top-level component was called so I really hope this quick tutorial helped clear that up for you. ",[22,50753,50754,50755,50757],{},"_",[646,50756,49733],{}," Do you have any questions about the initial application the Angular CLI creates for you? _",[1527,50759,50760],{},"html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .spoOn, html code.shiki .spoOn{--shiki-default:#E36209;--shiki-dark:#FFAB70;--shiki-sepia:#E36209}",{"title":57,"searchDepth":76,"depth":76,"links":50762},[50763,50764,50765],{"id":49761,"depth":76,"text":49762},{"id":49793,"depth":76,"text":49794},{"id":50747,"depth":76,"text":50748},{"_id":50767,"path":50768,"title":50769,"description":50769,"meta":50770,"body":50775},"content/blog/2017/06/07/angular-forms-clear-input-field.md","/blog/2017/06/07/angular-forms-clear-input-field","Angular Forms: How to clear an input field",{"slug":50771,"date":50772,"published":13,"tags":50773,"author":-1,"cover":50774,"excerpt":-1},"angular-forms-clear-input-field","2017-06-07T09:50:47-04:00",[49752],"./angular-forms.png",{"type":19,"value":50776,"toc":51509},[50777,50785,50789,50792,50796,50799,50803,50806,50817,50823,50831,50835,50838,50911,50914,51028,51031,51051,51054,51130,51133,51243,51246,51250,51253,51283,51286,51290,51299,51304,51311,51385,51388,51496,51498,51501,51506],[22,50778,50779,50780,50784],{},"Recently I was working on an ",[677,50781,49752],{"href":50782,"rel":50783},"https://angular.io/",[681]," Forms application and I needed the ability to clear an input field.",[26,50786,50788],{"id":50787},"how-to-clear-an-input-field-in-angular-forms","How to clear an input field in Angular Forms.",[22,50790,50791],{},"In a normal HTML form you would use find the input on the page and just clear it's text value. In Angular you want to bind the value of a property to the input box and reset the properties value.",[26,50793,50795],{"id":50794},"angular-forms-project","Angular Forms Project",[22,50797,50798],{},"I have been learning a lot lately when it comes to building Angular applications. In this post we are going to talk about a tasks application that I was working on for my latest course. In the tasks application you can list out all of your tasks but I also allow you to add new ones. When a new task was added I wanted to clear the form out so you could quickly add another one. I ran into a problem doing so and I wanted to share that problem and my solution with you today.",[636,50800,50802],{"id":50801},"my-tasks-project","My Tasks Project",[22,50804,50805],{},"The application I am working on is a simple tasks application. It is broken down into 3 components",[34,50807,50808,50811,50814],{},[37,50809,50810],{},"Tasks - Main tasks component that has 2 sub components.",[37,50812,50813],{},"Add Task - A way for you to add a new task",[37,50815,50816],{},"Task List - A way to display all of the components. ",[22,50818,50819],{},[653,50820],{"alt":50821,"src":50822},"Tasks Project","./2017-06-07_08-39-40.png",[22,50824,50825,50826,2755],{},"If you want to check out the project you can grab it on ",[677,50827,50830],{"href":50828,"rel":50829},"https://github.com/danvega/spring-angular2-tasks",[681],"Github",[636,50832,50834],{"id":50833},"angular-forms","Angular Forms",[22,50836,50837],{},"The first thing we have is the template for the add task form and it looks something like this. You will notice that we have an event handler for when the user hits the enter key. When that happens we will call a method onTaskAdd and pass in the event. ",[52,50839,50841],{"className":15773,"code":50840,"language":15775,"meta":57,"style":57},"\u003Cdiv class=\"form-group\">\n \u003Cinput type=\"text\"\n class=\"form-control\"\n placeholder=\"Add New Task\"\n (keyup.enter)=\"onTaskAdd($event)\">\n\u003C/div>\n",[59,50842,50843,50858,50871,50881,50891,50903],{"__ignoreMap":57},[62,50844,50845,50847,50849,50851,50853,50856],{"class":64,"line":65},[62,50846,760],{"class":72},[62,50848,15944],{"class":1780},[62,50850,119],{"class":122},[62,50852,146],{"class":72},[62,50854,50855],{"class":1675},"\"form-group\"",[62,50857,1784],{"class":72},[62,50859,50860,50862,50864,50866,50868],{"class":64,"line":76},[62,50861,1789],{"class":72},[62,50863,8890],{"class":1780},[62,50865,16099],{"class":122},[62,50867,146],{"class":72},[62,50869,50870],{"class":1675},"\"text\"\n",[62,50872,50873,50876,50878],{"class":64,"line":82},[62,50874,50875],{"class":122}," class",[62,50877,146],{"class":72},[62,50879,50880],{"class":1675},"\"form-control\"\n",[62,50882,50883,50886,50888],{"class":64,"line":89},[62,50884,50885],{"class":122}," placeholder",[62,50887,146],{"class":72},[62,50889,50890],{"class":1675},"\"Add New Task\"\n",[62,50892,50893,50896,50898,50901],{"class":64,"line":95},[62,50894,50895],{"class":122}," (keyup.enter)",[62,50897,146],{"class":72},[62,50899,50900],{"class":1675},"\"onTaskAdd($event)\"",[62,50902,1784],{"class":72},[62,50904,50905,50907,50909],{"class":64,"line":101},[62,50906,1818],{"class":72},[62,50908,15944],{"class":1780},[62,50910,1784],{"class":72},[22,50912,50913],{},"In this simple demo, we are creating a new task with the value from the input and calling a service to save the task. ",[52,50915,50917],{"className":32791,"code":50916,"language":32793,"meta":57,"style":57},"onTaskAdd(event) {\n let task: Task = new Task(event.target.value,false, this.getTodayAsString());\n this.taskService.addTask(task)\n .subscribe(\n (newTask: Task) => {\n this.taskService.onTaskAdded.emit(newTask);\n }\n );\n}\n",[59,50918,50919,50927,50962,50974,50983,51001,51014,51019,51024],{"__ignoreMap":57},[62,50920,50921,50924],{"class":64,"line":65},[62,50922,50923],{"class":122},"onTaskAdd",[62,50925,50926],{"class":72},"(event) {\n",[62,50928,50929,50932,50935,50937,50939,50941,50943,50945,50948,50951,50953,50955,50957,50960],{"class":64,"line":76},[62,50930,50931],{"class":68}," let",[62,50933,50934],{"class":72}," task",[62,50936,1266],{"class":68},[62,50938,23121],{"class":122},[62,50940,2556],{"class":68},[62,50942,466],{"class":68},[62,50944,23121],{"class":122},[62,50946,50947],{"class":72},"(event.target.value,",[62,50949,50950],{"class":149},"false",[62,50952,976],{"class":72},[62,50954,1295],{"class":149},[62,50956,2755],{"class":72},[62,50958,50959],{"class":122},"getTodayAsString",[62,50961,1091],{"class":72},[62,50963,50964,50966,50969,50971],{"class":64,"line":82},[62,50965,39124],{"class":149},[62,50967,50968],{"class":72},".taskService.",[62,50970,23644],{"class":122},[62,50972,50973],{"class":72},"(task)\n",[62,50975,50976,50978,50981],{"class":64,"line":89},[62,50977,42872],{"class":72},[62,50979,50980],{"class":122},"subscribe",[62,50982,3301],{"class":72},[62,50984,50985,50988,50991,50993,50995,50997,50999],{"class":64,"line":95},[62,50986,50987],{"class":72}," (",[62,50989,50990],{"class":889},"newTask",[62,50992,1266],{"class":68},[62,50994,23121],{"class":122},[62,50996,5024],{"class":72},[62,50998,21525],{"class":68},[62,51000,126],{"class":72},[62,51002,51003,51006,51009,51012],{"class":64,"line":101},[62,51004,51005],{"class":149}," this",[62,51007,51008],{"class":72},".taskService.onTaskAdded.",[62,51010,51011],{"class":122},"emit",[62,51013,23681],{"class":72},[62,51015,51016],{"class":64,"line":107},[62,51017,51018],{"class":72}," }\n",[62,51020,51021],{"class":64,"line":113},[62,51022,51023],{"class":72}," );\n",[62,51025,51026],{"class":64,"line":129},[62,51027,379],{"class":72},[22,51029,51030],{},"This was working great but after the task was saved I need a way to clear the input so the user could type another task and save it. This seemed like a great opportunity for property binding to work its magic. In the add task component, I would set up a value for the input field and initialize it to an empty string. ",[52,51032,51034],{"className":32791,"code":51033,"language":32793,"meta":57,"style":57},"addTaskValue: string = \"\";\n",[59,51035,51036],{"__ignoreMap":57},[62,51037,51038,51041,51044,51046,51049],{"class":64,"line":65},[62,51039,51040],{"class":122},"addTaskValue",[62,51042,51043],{"class":72},": string ",[62,51045,146],{"class":68},[62,51047,51048],{"class":1675}," \"\"",[62,51050,153],{"class":72},[22,51052,51053],{},"Then in the template, I would set the value to that property. ",[52,51055,51057],{"className":15773,"code":51056,"language":15775,"meta":57,"style":57},"\u003Cdiv class=\"form-group\">\n \u003Cinput type=\"text\"\n class=\"form-control\"\n placeholder=\"Add New Task\"\n (keyup.enter)=\"onTaskAdd($event)\"\n \\[value\\]=\"addTaskValue\">\n\u003C/div>\n",[59,51058,51059,51073,51085,51093,51101,51110,51122],{"__ignoreMap":57},[62,51060,51061,51063,51065,51067,51069,51071],{"class":64,"line":65},[62,51062,760],{"class":72},[62,51064,15944],{"class":1780},[62,51066,119],{"class":122},[62,51068,146],{"class":72},[62,51070,50855],{"class":1675},[62,51072,1784],{"class":72},[62,51074,51075,51077,51079,51081,51083],{"class":64,"line":76},[62,51076,1789],{"class":72},[62,51078,8890],{"class":1780},[62,51080,16099],{"class":122},[62,51082,146],{"class":72},[62,51084,50870],{"class":1675},[62,51086,51087,51089,51091],{"class":64,"line":82},[62,51088,50875],{"class":122},[62,51090,146],{"class":72},[62,51092,50880],{"class":1675},[62,51094,51095,51097,51099],{"class":64,"line":89},[62,51096,50885],{"class":122},[62,51098,146],{"class":72},[62,51100,50890],{"class":1675},[62,51102,51103,51105,51107],{"class":64,"line":95},[62,51104,50895],{"class":122},[62,51106,146],{"class":72},[62,51108,51109],{"class":1675},"\"onTaskAdd($event)\"\n",[62,51111,51112,51115,51117,51120],{"class":64,"line":101},[62,51113,51114],{"class":122}," \\[value\\]",[62,51116,146],{"class":72},[62,51118,51119],{"class":1675},"\"addTaskValue\"",[62,51121,1784],{"class":72},[62,51123,51124,51126,51128],{"class":64,"line":107},[62,51125,1818],{"class":72},[62,51127,15944],{"class":1780},[62,51129,1784],{"class":72},[22,51131,51132],{},"Finally, when we add a new task I thought I could simply set that value back to an empty string. ",[52,51134,51136],{"className":32791,"code":51135,"language":32793,"meta":57,"style":57},"onTaskAdd(event) {\n let task: Task = new Task(event.target.value,false, this.getTodayAsString());\n this.taskService.addTask(task)\n .subscribe(\n (newTask: Task) => {\n this.addTaskValue = '';\n this.taskService.onTaskAdded.emit(newTask);\n }\n );\n}\n",[59,51137,51138,51144,51174,51184,51192,51208,51221,51231,51235,51239],{"__ignoreMap":57},[62,51139,51140,51142],{"class":64,"line":65},[62,51141,50923],{"class":122},[62,51143,50926],{"class":72},[62,51145,51146,51148,51150,51152,51154,51156,51158,51160,51162,51164,51166,51168,51170,51172],{"class":64,"line":76},[62,51147,50931],{"class":68},[62,51149,50934],{"class":72},[62,51151,1266],{"class":68},[62,51153,23121],{"class":122},[62,51155,2556],{"class":68},[62,51157,466],{"class":68},[62,51159,23121],{"class":122},[62,51161,50947],{"class":72},[62,51163,50950],{"class":149},[62,51165,976],{"class":72},[62,51167,1295],{"class":149},[62,51169,2755],{"class":72},[62,51171,50959],{"class":122},[62,51173,1091],{"class":72},[62,51175,51176,51178,51180,51182],{"class":64,"line":82},[62,51177,39124],{"class":149},[62,51179,50968],{"class":72},[62,51181,23644],{"class":122},[62,51183,50973],{"class":72},[62,51185,51186,51188,51190],{"class":64,"line":89},[62,51187,42872],{"class":72},[62,51189,50980],{"class":122},[62,51191,3301],{"class":72},[62,51193,51194,51196,51198,51200,51202,51204,51206],{"class":64,"line":95},[62,51195,50987],{"class":72},[62,51197,50990],{"class":889},[62,51199,1266],{"class":68},[62,51201,23121],{"class":122},[62,51203,5024],{"class":72},[62,51205,21525],{"class":68},[62,51207,126],{"class":72},[62,51209,51210,51212,51215,51217,51219],{"class":64,"line":101},[62,51211,51005],{"class":149},[62,51213,51214],{"class":72},".addTaskValue ",[62,51216,146],{"class":68},[62,51218,21087],{"class":1675},[62,51220,153],{"class":72},[62,51222,51223,51225,51227,51229],{"class":64,"line":107},[62,51224,51005],{"class":149},[62,51226,51008],{"class":72},[62,51228,51011],{"class":122},[62,51230,23681],{"class":72},[62,51232,51233],{"class":64,"line":113},[62,51234,51018],{"class":72},[62,51236,51237],{"class":64,"line":129},[62,51238,51023],{"class":72},[62,51240,51241],{"class":64,"line":134},[62,51242,379],{"class":72},[22,51244,51245],{},"This didn't work and it left me scratching my head. ",[26,51247,51249],{"id":51248},"angular-forms-clearing-an-input-field","Angular Forms: Clearing an input field",[22,51251,51252],{},"While the solution above didn't work I knew I was headed in the right direction. The next thing I tried was to set the string to a null value or an empty string with a space in it. ",[52,51254,51256],{"className":32791,"code":51255,"language":32793,"meta":57,"style":57},"this.addTaskValue = ' ';\nthis.addTaskValue = null;\n",[59,51257,51258,51271],{"__ignoreMap":57},[62,51259,51260,51262,51264,51266,51269],{"class":64,"line":65},[62,51261,1295],{"class":149},[62,51263,51214],{"class":72},[62,51265,146],{"class":68},[62,51267,51268],{"class":1675}," ' '",[62,51270,153],{"class":72},[62,51272,51273,51275,51277,51279,51281],{"class":64,"line":76},[62,51274,1295],{"class":149},[62,51276,51214],{"class":72},[62,51278,146],{"class":68},[62,51280,13324],{"class":149},[62,51282,153],{"class":72},[22,51284,51285],{},"This actually works the very first time that you try it but it will not work on subsequent tries. ",[636,51287,51289],{"id":51288},"ngmodel","ngModel",[22,51291,51292,51293,51298],{},"If you have done any work with forms in Angular you have probably come across the ",[677,51294,51297],{"href":51295,"rel":51296},"https://angular.io/docs/ts/latest/api/forms/index/NgModel-directive.html",[681],"ngModel directive",". What does the ngModle directive do? ",[29685,51300,51301],{},[22,51302,51303],{},"Creates a FormControl instance from a domain model and binds it to a form control element. The FormControl instance will track the value, user interaction, and validation status of the control and keep the view synced with the model. If used within a parent form, the directive will also register itself with the form as a child control.",[22,51305,51306,51307,51310],{},"The problem with the approach we did above is that it is only property binding and not event binding. Angular doesn't run change detection because no event is fired. If you we use ngModel to bind our value to our form control we can get the answer we were looking for. Now we are using 2-way data binding with ",[59,51308,51309],{},"[()]"," syntax (also known as 'banana-box syntax'), the value in the UI will always be synced back to the domain model in your class as well.",[52,51312,51314],{"className":15773,"code":51313,"language":15775,"meta":57,"style":57},"\u003Cdiv class=\"form-group\">\n \u003Cinput type=\"text\"\n class=\"form-control\"\n placeholder=\"Add New Task\"\n (keyup.enter)=\"onTaskAdd($event)\"\n \\[(ngModel)\\]=\"addTaskValue\">\n\u003C/div>\n",[59,51315,51316,51330,51342,51350,51358,51366,51377],{"__ignoreMap":57},[62,51317,51318,51320,51322,51324,51326,51328],{"class":64,"line":65},[62,51319,760],{"class":72},[62,51321,15944],{"class":1780},[62,51323,119],{"class":122},[62,51325,146],{"class":72},[62,51327,50855],{"class":1675},[62,51329,1784],{"class":72},[62,51331,51332,51334,51336,51338,51340],{"class":64,"line":76},[62,51333,1789],{"class":72},[62,51335,8890],{"class":1780},[62,51337,16099],{"class":122},[62,51339,146],{"class":72},[62,51341,50870],{"class":1675},[62,51343,51344,51346,51348],{"class":64,"line":82},[62,51345,50875],{"class":122},[62,51347,146],{"class":72},[62,51349,50880],{"class":1675},[62,51351,51352,51354,51356],{"class":64,"line":89},[62,51353,50885],{"class":122},[62,51355,146],{"class":72},[62,51357,50890],{"class":1675},[62,51359,51360,51362,51364],{"class":64,"line":95},[62,51361,50895],{"class":122},[62,51363,146],{"class":72},[62,51365,51109],{"class":1675},[62,51367,51368,51371,51373,51375],{"class":64,"line":101},[62,51369,51370],{"class":122}," \\[(ngModel)\\]",[62,51372,146],{"class":72},[62,51374,51119],{"class":1675},[62,51376,1784],{"class":72},[62,51378,51379,51381,51383],{"class":64,"line":107},[62,51380,1818],{"class":72},[62,51382,15944],{"class":1780},[62,51384,1784],{"class":72},[22,51386,51387],{},"Now when we add a new task we can set our value to an empty string and Angular updates the input for us.",[52,51389,51390],{"className":32791,"code":51135,"language":32793,"meta":57,"style":57},[59,51391,51392,51398,51428,51438,51446,51462,51474,51484,51488,51492],{"__ignoreMap":57},[62,51393,51394,51396],{"class":64,"line":65},[62,51395,50923],{"class":122},[62,51397,50926],{"class":72},[62,51399,51400,51402,51404,51406,51408,51410,51412,51414,51416,51418,51420,51422,51424,51426],{"class":64,"line":76},[62,51401,50931],{"class":68},[62,51403,50934],{"class":72},[62,51405,1266],{"class":68},[62,51407,23121],{"class":122},[62,51409,2556],{"class":68},[62,51411,466],{"class":68},[62,51413,23121],{"class":122},[62,51415,50947],{"class":72},[62,51417,50950],{"class":149},[62,51419,976],{"class":72},[62,51421,1295],{"class":149},[62,51423,2755],{"class":72},[62,51425,50959],{"class":122},[62,51427,1091],{"class":72},[62,51429,51430,51432,51434,51436],{"class":64,"line":82},[62,51431,39124],{"class":149},[62,51433,50968],{"class":72},[62,51435,23644],{"class":122},[62,51437,50973],{"class":72},[62,51439,51440,51442,51444],{"class":64,"line":89},[62,51441,42872],{"class":72},[62,51443,50980],{"class":122},[62,51445,3301],{"class":72},[62,51447,51448,51450,51452,51454,51456,51458,51460],{"class":64,"line":95},[62,51449,50987],{"class":72},[62,51451,50990],{"class":889},[62,51453,1266],{"class":68},[62,51455,23121],{"class":122},[62,51457,5024],{"class":72},[62,51459,21525],{"class":68},[62,51461,126],{"class":72},[62,51463,51464,51466,51468,51470,51472],{"class":64,"line":101},[62,51465,51005],{"class":149},[62,51467,51214],{"class":72},[62,51469,146],{"class":68},[62,51471,21087],{"class":1675},[62,51473,153],{"class":72},[62,51475,51476,51478,51480,51482],{"class":64,"line":107},[62,51477,51005],{"class":149},[62,51479,51008],{"class":72},[62,51481,51011],{"class":122},[62,51483,23681],{"class":72},[62,51485,51486],{"class":64,"line":113},[62,51487,51018],{"class":72},[62,51489,51490],{"class":64,"line":129},[62,51491,51023],{"class":72},[62,51493,51494],{"class":64,"line":134},[62,51495,379],{"class":72},[26,51497,1499],{"id":1498},[22,51499,51500],{},"Angular gives us some really awesome ways to handle forms but there are some gotchas when you're trying to move from a submitting to a server to a single page application approach. ",[22,51502,50754,51503,51505],{},[646,51504,49733],{}," What do you like or don't like about working with forms in Angular? What are your toughest challenges? _",[1527,51507,51508],{},"html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .suaqH, html code.shiki .suaqH{--shiki-default:#22863A;--shiki-dark:#85E89D;--shiki-sepia:#22863A}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}html pre.shiki code .spoOn, html code.shiki .spoOn{--shiki-default:#E36209;--shiki-dark:#FFAB70;--shiki-sepia:#E36209}",{"title":57,"searchDepth":76,"depth":76,"links":51510},[51511,51512,51516,51519],{"id":50787,"depth":76,"text":50788},{"id":50794,"depth":76,"text":50795,"children":51513},[51514,51515],{"id":50801,"depth":82,"text":50802},{"id":50833,"depth":82,"text":50834},{"id":51248,"depth":76,"text":51249,"children":51517},[51518],{"id":51288,"depth":82,"text":51289},{"id":1498,"depth":76,"text":1499},{"_id":51521,"path":51522,"title":51523,"description":51523,"meta":51524,"body":51529},"content/blog/2017/06/05/getting-started-angular-cli.md","/blog/2017/06/05/getting-started-angular-cli","Getting Started with the Angular CLI",{"slug":51525,"date":51526,"published":13,"tags":51527,"author":-1,"cover":51528,"excerpt":-1},"getting-started-angular-cli","2017-06-05T10:34:50-04:00",[49752],"./2017-06-05_09-34-25-760x226.png",{"type":19,"value":51530,"toc":51852},[51531,51539,51543,51550,51553,51566,51584,51590,51593,51610,51613,51624,51630,51634,51637,51651,51654,51659,51662,51672,51680,51684,51687,51702,51705,51766,51769,51786,51792,51801,51807,51810,51833,51839,51841,51844,51849],[22,51532,51533,51534,2755],{},"I have worked on a variety of Angular applications over the last few months and it truly has been a blast. I know a lot of my readers out there are Java + Spring developers and I want to make sure that I make this very clear in this post and the ones that follow. If you played around with Angular 1.xx and didn't really enjoy writing those applications you need to come back for another look. Writing angular 2/4 applications with Typescript is a familiar setting for Java Developers. I really do love working with the Angular framework and in today's article, I am going to help you get started quickly by using the ",[677,51535,51538],{"href":51536,"rel":51537},"https://cli.angular.io/",[681],"Angular CLI",[26,51540,51542],{"id":51541},"what-is-the-angular-cli","What is the Angular CLI",[22,51544,51545,51546,51549],{},"The Angular CLI is a tool to initialize, develop, scaffold and maintain ",[677,51547,49752],{"href":50782,"rel":51548},[681]," applications. The CLI in Angular CLI stands for Command Line Interface and you have probably come across similar tools before. If you're using Spring Boot we use the Spring Initializr to initialize our applications and there is a Spring CLI to accomplish this from the command line. ",[26,51551,51523],{"id":51552},"getting-started-with-the-angular-cli",[22,51554,51555,51556,51560,51561,51565],{},"The first thing you need to do is to make sure you have ",[677,51557,32634],{"href":51558,"rel":51559},"https://nodejs.org/en/",[681]," & ",[677,51562,32645],{"href":51563,"rel":51564},"https://www.npmjs.com/",[681]," installed. You can check the versions for each by opening a command line and running the following commands. ",[52,51567,51569],{"className":1663,"code":51568,"language":1665,"meta":57,"style":57},"node -v\nnpm -v\n",[59,51570,51571,51578],{"__ignoreMap":57},[62,51572,51573,51575],{"class":64,"line":65},[62,51574,32634],{"class":122},[62,51576,51577],{"class":149}," -v\n",[62,51579,51580,51582],{"class":64,"line":76},[62,51581,32645],{"class":122},[62,51583,51577],{"class":149},[22,51585,51586],{},[653,51587],{"alt":51588,"src":51589},"Angular CLI Check - Node & NPM Versions","./2017-06-05_10-02-47.png",[22,51591,51592],{},"With those in place, you can install the Angular CLI by running the following command. ",[52,51594,51596],{"className":1663,"code":51595,"language":1665,"meta":57,"style":57},"npm install -g @angular/cli\n",[59,51597,51598],{"__ignoreMap":57},[62,51599,51600,51602,51604,51607],{"class":64,"line":65},[62,51601,32645],{"class":122},[62,51603,32750],{"class":1675},[62,51605,51606],{"class":149}," -g",[62,51608,51609],{"class":1675}," @angular/cli\n",[22,51611,51612],{},"Once installed you can always run the following command to see what version of the Angular CLI you're running. ",[52,51614,51616],{"className":1663,"code":51615,"language":1665,"meta":57,"style":57},"ng -v\n",[59,51617,51618],{"__ignoreMap":57},[62,51619,51620,51622],{"class":64,"line":65},[62,51621,49784],{"class":122},[62,51623,51577],{"class":149},[22,51625,51626],{},[653,51627],{"alt":51628,"src":51629},"Angular CLI Checking the version","./2017-06-05_10-05-08.png",[636,51631,51633],{"id":51632},"creating-and-running-your-new-project","Creating and Running your new Project",[22,51635,51636],{},"The first thing you're going to want to do with your shiny new toy is to create a new project. To do that we are simply going to run the following command. ",[52,51638,51640],{"className":1663,"code":51639,"language":1665,"meta":57,"style":57},"ng new your-project-name-here\n",[59,51641,51642],{"__ignoreMap":57},[62,51643,51644,51646,51648],{"class":64,"line":65},[62,51645,49784],{"class":122},[62,51647,466],{"class":1675},[62,51649,51650],{"class":1675}," your-project-name-here\n",[22,51652,51653],{},"You will want to run this from the directory where you want your new project to be created. The Angular CLI will create the folder and all the files you need to get started. ",[22,51655,51656],{},[653,51657],{"alt":51658,"src":49771},"Angular CLI Generating a new project",[22,51660,51661],{},"When that is finished you are ready to go. Navigate to the project folder (cd hello-angular) and run the following command. ",[52,51663,51664],{"className":1663,"code":49777,"language":1665,"meta":57,"style":57},[59,51665,51666],{"__ignoreMap":57},[62,51667,51668,51670],{"class":64,"line":65},[62,51669,49784],{"class":122},[62,51671,49787],{"class":1675},[22,51673,51674,51675,51679],{},"Navigate to ",[677,51676,51677],{"href":51677,"rel":51678},"http://localhost:4200/",[681],". The app will automatically reload if you change any of the source files.",[636,51681,51683],{"id":51682},"generatingblueprints","Generating Blueprints",[22,51685,51686],{},"Another very useful command is the generate command. ",[52,51688,51690],{"className":1663,"code":51689,"language":1665,"meta":57,"style":57},"ng generate [name]\n",[59,51691,51692],{"__ignoreMap":57},[62,51693,51694,51696,51699],{"class":64,"line":65},[62,51695,49784],{"class":122},[62,51697,51698],{"class":1675}," generate",[62,51700,51701],{"class":72}," [name]\n",[22,51703,51704],{},"This will allow you to generate the following blueprints. ",[915,51706,51707,51713,51720,51727,51734,51741,51747,51753,51760],{},[37,51708,51709],{},[677,51710,11671],{"href":51711,"rel":51712},"https://github.com/angular/angular-cli/wiki/generate-class",[681],[37,51714,51715],{},[677,51716,51719],{"href":51717,"rel":51718},"https://github.com/angular/angular-cli/wiki/generate-component",[681],"component",[37,51721,51722],{},[677,51723,51726],{"href":51724,"rel":51725},"https://github.com/angular/angular-cli/wiki/generate-directive",[681],"directive",[37,51728,51729],{},[677,51730,51733],{"href":51731,"rel":51732},"https://github.com/angular/angular-cli/wiki/generate-enum",[681],"enum",[37,51735,51736],{},[677,51737,51740],{"href":51738,"rel":51739},"https://github.com/angular/angular-cli/wiki/generate-guard",[681],"guard",[37,51742,51743],{},[677,51744,36687],{"href":51745,"rel":51746},"https://github.com/angular/angular-cli/wiki/generate-interface",[681],[37,51748,51749],{},[677,51750,32813],{"href":51751,"rel":51752},"https://github.com/angular/angular-cli/wiki/generate-module",[681],[37,51754,51755],{},[677,51756,51759],{"href":51757,"rel":51758},"https://github.com/angular/angular-cli/wiki/generate-pipe",[681],"pipe",[37,51761,51762],{},[677,51763,48855],{"href":51764,"rel":51765},"https://github.com/angular/angular-cli/wiki/generate-service",[681],[22,51767,51768],{},"We won't go through them all but I will walk through one example here. When you get into Angular development you will quickly realize that everything is component based. When we want to create a new component like a users component to manage the users we can run the following command. ",[52,51770,51772],{"className":1663,"code":51771,"language":1665,"meta":57,"style":57},"ng generate component users\n",[59,51773,51774],{"__ignoreMap":57},[62,51775,51776,51778,51780,51783],{"class":64,"line":65},[62,51777,49784],{"class":122},[62,51779,51698],{"class":1675},[62,51781,51782],{"class":1675}," component",[62,51784,51785],{"class":1675}," users\n",[22,51787,51788],{},[653,51789],{"alt":51790,"src":51791},"Angular CLI Generating Components","./2017-06-05_10-28-11.png",[22,51793,51794,51800],{},[4534,51795,51796,51797],{},"* We can also use the shortcut ",[646,51798,51799],{},"ng g c users"," When you run this command you can see that it will generate CSS & HTML along with the component and test typescript files. If you want to generate a child component just give the full path for your component. ",[22,51802,51803],{},[653,51804],{"alt":51805,"src":51806},"Angular CLI Generate Components","./2017-06-05_10-31-30.png",[636,51808,8106],{"id":51809},"testing",[22,51811,51812,51813,51818,51819,51824,51825,51828,51829,51832],{},"Angular uses the ",[677,51814,51817],{"href":51815,"rel":51816},"https://karma-runner.github.io/1.0/index.html",[681],"Karma Test Runner"," for all of its testing needs. If you run the following command the Angular CLI will spin up a test runner for you and run all of your tests. Tests will execute after a build is executed via ",[677,51820,51823],{"href":51821,"rel":51822},"http://karma-runner.github.io/0.13/index.html",[681],"Karma",", and it will automatically watch your files for changes. You can run tests a single time via ",[59,51826,51827],{},"--watch=false"," or ",[59,51830,51831],{},"--single-run"," .",[22,51834,51835],{},[653,51836],{"alt":51837,"src":51838},"Angular CLI Tests","./2017-06-05_10-20-41.png",[26,51840,1499],{"id":1498},[22,51842,51843],{},"The Angular CLI is the best way to get started with Angular development. I hope anyone who looked at Angular in the past and decided it wasn't for them gives it another shot. It is a really great framework with a set of tools that makes writing Angular applications a breeze. ",[22,51845,50754,51846,51848],{},[646,51847,49733],{}," Have you looked at the latest version of Angular and if so what are your thoughts? _",[1527,51850,51851],{},"html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}",{"title":57,"searchDepth":76,"depth":76,"links":51853},[51854,51855,51860],{"id":51541,"depth":76,"text":51542},{"id":51552,"depth":76,"text":51523,"children":51856},[51857,51858,51859],{"id":51632,"depth":82,"text":51633},{"id":51682,"depth":82,"text":51683},{"id":51809,"depth":82,"text":8106},{"id":1498,"depth":76,"text":1499},{"_id":51862,"path":51863,"title":51864,"description":51864,"meta":51865,"body":51870},"content/blog/2017/05/31/spring-boot-1-question-students-asking-right-now.md","/blog/2017/05/31/spring-boot-1-question-students-asking-right-now","Spring Boot: The #1 question my students are asking right now",{"slug":51866,"date":51867,"published":13,"tags":51868,"author":-1,"cover":51869,"excerpt":-1},"spring-boot-1-question-students-asking-right-now","2017-05-31T08:48:35-04:00",[38716],"./pexels-photo-92028-760x599.jpeg",{"type":19,"value":51871,"toc":52727},[51872,51881,51885,51888,51894,51898,51901,51930,51934,51937,52406,52409,52467,52470,52663,52667,52675,52681,52692,52697,52700,52712,52714,52717,52724],[22,51873,51874,51875,51880],{},"Today's Question & Answer came in from a student in my ",[677,51876,51879],{"href":51877,"rel":51878},"https://danvega.dev/spring-boot",[681],"Spring Boot Introduction course"," and it is one that I get a lot. This tells me that I probably need to address this in the course but it's also an opportunity for me to share this with you. The question is: \"I created a new application from scratch using the Web, Spring Data JPA & H2 dependencies. If I create an entity and then start the application up I don't see the tables created in the H2 database for me. What is going wrong?\"",[26,51882,51884],{"id":51883},"spring-boot-application","Spring Boot Application",[22,51886,51887],{},"The first thing we are going to do is to create our Spring Boot Application using the Spring Initializer. In this demo, we are going to select the Web, JPA & H2 dependencies. ",[22,51889,51890],{},[653,51891],{"alt":51892,"src":51893},"Sping Boot Application","./2017-05-31_08-15-43-1024x645.png",[636,51895,51897],{"id":51896},"h2-database-settings","H2 Database Settings",[22,51899,51900],{},"The next thing we have to do is enable the H2 database console in our application.properties. You have to enable this if you want to be able to use this awesome little tool. There is also a property to change the path for the H2 console if you don't want to use the default. ",[52,51902,51904],{"className":32791,"code":51903,"language":32793,"meta":57,"style":57},"spring.h2.console.enabled=true\n#spring.h2.console.path=/h2-console\n",[59,51905,51906,51915],{"__ignoreMap":57},[62,51907,51908,51910,51912],{"class":64,"line":65},[62,51909,41993],{"class":72},[62,51911,146],{"class":68},[62,51913,51914],{"class":149},"true\n",[62,51916,51917,51920,51923,51925,51927],{"class":64,"line":76},[62,51918,51919],{"class":72},"#spring.h2.console.path",[62,51921,51922],{"class":68},"=/",[62,51924,26],{"class":72},[62,51926,11635],{"class":68},[62,51928,51929],{"class":72},"console\n",[636,51931,51933],{"id":51932},"create-an-entity","Create an Entity",[22,51935,51936],{},"Now we need to create our entity. I am going to keep it simple here but you can create whatever entity you like. ",[52,51938,51940],{"className":54,"code":51939,"language":56,"meta":57,"style":57},"package com.therealdanvega.domain;\n\nimport javax.persistence.Entity;\nimport javax.persistence.GeneratedValue;\nimport javax.persistence.Id;\n\n@Entity\npublic class Subscriber {\n\n @Id @GeneratedValue\n private Long id;\n private String first;\n private String last;\n private String email;\n\n public Subscriber(String first, String last, String email) {\n this.first = first;\n this.last = last;\n this.email = email;\n }\n\n public Long getId() {\n return id;\n }\n\n public void setId(Long id) {\n this.id = id;\n }\n\n public String getFirst() {\n return first;\n }\n\n public void setFirst(String first) {\n this.first = first;\n }\n\n public String getLast() {\n return last;\n }\n\n public void setLast(String last) {\n this.last = last;\n }\n\n public String getEmail() {\n return email;\n }\n\n public void setEmail(String email) {\n this.email = email;\n }\n\n @Override\n public String toString() {\n return \"Subscriber{\" +\n \"first='\" + first + '\\\\'' +\n \", last='\" + last + '\\\\'' +\n '}';\n }\n}\n",[59,51941,51942,51949,51953,51960,51967,51974,51978,51984,51995,51999,52009,52015,52022,52029,52035,52039,52062,52074,52086,52098,52102,52106,52117,52123,52127,52131,52146,52156,52160,52164,52175,52181,52185,52189,52204,52214,52218,52222,52233,52239,52243,52247,52262,52272,52276,52280,52291,52297,52301,52305,52320,52330,52334,52338,52344,52354,52364,52384,52393,52398,52402],{"__ignoreMap":57},[62,51943,51944,51946],{"class":64,"line":65},[62,51945,69],{"class":68},[62,51947,51948],{"class":72}," com.therealdanvega.domain;\n",[62,51950,51951],{"class":64,"line":76},[62,51952,79],{"emptyLinePlaceholder":13},[62,51954,51955,51957],{"class":64,"line":82},[62,51956,27875],{"class":68},[62,51958,51959],{"class":72}," javax.persistence.Entity;\n",[62,51961,51962,51964],{"class":64,"line":89},[62,51963,27875],{"class":68},[62,51965,51966],{"class":72}," javax.persistence.GeneratedValue;\n",[62,51968,51969,51971],{"class":64,"line":95},[62,51970,27875],{"class":68},[62,51972,51973],{"class":72}," javax.persistence.Id;\n",[62,51975,51976],{"class":64,"line":101},[62,51977,79],{"emptyLinePlaceholder":13},[62,51979,51980,51982],{"class":64,"line":107},[62,51981,942],{"class":72},[62,51983,8999],{"class":68},[62,51985,51986,51988,51990,51993],{"class":64,"line":113},[62,51987,116],{"class":68},[62,51989,119],{"class":68},[62,51991,51992],{"class":122}," Subscriber",[62,51994,126],{"class":72},[62,51996,51997],{"class":64,"line":129},[62,51998,79],{"emptyLinePlaceholder":13},[62,52000,52001,52003,52005,52007],{"class":64,"line":134},[62,52002,2143],{"class":72},[62,52004,9016],{"class":68},[62,52006,9019],{"class":72},[62,52008,9022],{"class":68},[62,52010,52011,52013],{"class":64,"line":156},[62,52012,137],{"class":68},[62,52014,9029],{"class":72},[62,52016,52017,52019],{"class":64,"line":161},[62,52018,137],{"class":68},[62,52020,52021],{"class":72}," String first;\n",[62,52023,52024,52026],{"class":64,"line":167},[62,52025,137],{"class":68},[62,52027,52028],{"class":72}," String last;\n",[62,52030,52031,52033],{"class":64,"line":173},[62,52032,137],{"class":68},[62,52034,22360],{"class":72},[62,52036,52037],{"class":64,"line":179},[62,52038,79],{"emptyLinePlaceholder":13},[62,52040,52041,52043,52045,52047,52050,52052,52055,52057,52060],{"class":64,"line":185},[62,52042,194],{"class":68},[62,52044,51992],{"class":122},[62,52046,1049],{"class":72},[62,52048,52049],{"class":889},"first",[62,52051,8624],{"class":72},[62,52053,52054],{"class":889},"last",[62,52056,8624],{"class":72},[62,52058,52059],{"class":889},"email",[62,52061,768],{"class":72},[62,52063,52064,52066,52069,52071],{"class":64,"line":191},[62,52065,2405],{"class":149},[62,52067,52068],{"class":72},".first ",[62,52070,146],{"class":68},[62,52072,52073],{"class":72}," first;\n",[62,52075,52076,52078,52081,52083],{"class":64,"line":209},[62,52077,2405],{"class":149},[62,52079,52080],{"class":72},".last ",[62,52082,146],{"class":68},[62,52084,52085],{"class":72}," last;\n",[62,52087,52088,52090,52093,52095],{"class":64,"line":220},[62,52089,2405],{"class":149},[62,52091,52092],{"class":72},".email ",[62,52094,146],{"class":68},[62,52096,52097],{"class":72}," email;\n",[62,52099,52100],{"class":64,"line":226},[62,52101,223],{"class":72},[62,52103,52104],{"class":64,"line":231},[62,52105,79],{"emptyLinePlaceholder":13},[62,52107,52108,52110,52113,52115],{"class":64,"line":236},[62,52109,194],{"class":68},[62,52111,52112],{"class":72}," Long ",[62,52114,23363],{"class":122},[62,52116,206],{"class":72},[62,52118,52119,52121],{"class":64,"line":242},[62,52120,360],{"class":68},[62,52122,9954],{"class":72},[62,52124,52125],{"class":64,"line":247},[62,52126,223],{"class":72},[62,52128,52129],{"class":64,"line":252},[62,52130,79],{"emptyLinePlaceholder":13},[62,52132,52133,52135,52137,52140,52142,52144],{"class":64,"line":257},[62,52134,194],{"class":68},[62,52136,200],{"class":68},[62,52138,52139],{"class":122}," setId",[62,52141,39340],{"class":72},[62,52143,6283],{"class":889},[62,52145,768],{"class":72},[62,52147,52148,52150,52152,52154],{"class":64,"line":271},[62,52149,2405],{"class":149},[62,52151,9802],{"class":72},[62,52153,146],{"class":68},[62,52155,9954],{"class":72},[62,52157,52158],{"class":64,"line":281},[62,52159,223],{"class":72},[62,52161,52162],{"class":64,"line":286},[62,52163,79],{"emptyLinePlaceholder":13},[62,52165,52166,52168,52170,52173],{"class":64,"line":291},[62,52167,194],{"class":68},[62,52169,2469],{"class":72},[62,52171,52172],{"class":122},"getFirst",[62,52174,206],{"class":72},[62,52176,52177,52179],{"class":64,"line":296},[62,52178,360],{"class":68},[62,52180,52073],{"class":72},[62,52182,52183],{"class":64,"line":302},[62,52184,223],{"class":72},[62,52186,52187],{"class":64,"line":308},[62,52188,79],{"emptyLinePlaceholder":13},[62,52190,52191,52193,52195,52198,52200,52202],{"class":64,"line":314},[62,52192,194],{"class":68},[62,52194,200],{"class":68},[62,52196,52197],{"class":122}," setFirst",[62,52199,1049],{"class":72},[62,52201,52049],{"class":889},[62,52203,768],{"class":72},[62,52205,52206,52208,52210,52212],{"class":64,"line":320},[62,52207,2405],{"class":149},[62,52209,52068],{"class":72},[62,52211,146],{"class":68},[62,52213,52073],{"class":72},[62,52215,52216],{"class":64,"line":326},[62,52217,223],{"class":72},[62,52219,52220],{"class":64,"line":338},[62,52221,79],{"emptyLinePlaceholder":13},[62,52223,52224,52226,52228,52231],{"class":64,"line":343},[62,52225,194],{"class":68},[62,52227,2469],{"class":72},[62,52229,52230],{"class":122},"getLast",[62,52232,206],{"class":72},[62,52234,52235,52237],{"class":64,"line":357},[62,52236,360],{"class":68},[62,52238,52085],{"class":72},[62,52240,52241],{"class":64,"line":366},[62,52242,223],{"class":72},[62,52244,52245],{"class":64,"line":371},[62,52246,79],{"emptyLinePlaceholder":13},[62,52248,52249,52251,52253,52256,52258,52260],{"class":64,"line":376},[62,52250,194],{"class":68},[62,52252,200],{"class":68},[62,52254,52255],{"class":122}," setLast",[62,52257,1049],{"class":72},[62,52259,52054],{"class":889},[62,52261,768],{"class":72},[62,52263,52264,52266,52268,52270],{"class":64,"line":16333},[62,52265,2405],{"class":149},[62,52267,52080],{"class":72},[62,52269,146],{"class":68},[62,52271,52085],{"class":72},[62,52273,52274],{"class":64,"line":16349},[62,52275,223],{"class":72},[62,52277,52278],{"class":64,"line":16365},[62,52279,79],{"emptyLinePlaceholder":13},[62,52281,52282,52284,52286,52289],{"class":64,"line":16381},[62,52283,194],{"class":68},[62,52285,2469],{"class":72},[62,52287,52288],{"class":122},"getEmail",[62,52290,206],{"class":72},[62,52292,52293,52295],{"class":64,"line":16402},[62,52294,360],{"class":68},[62,52296,52097],{"class":72},[62,52298,52299],{"class":64,"line":16411},[62,52300,223],{"class":72},[62,52302,52303],{"class":64,"line":16427},[62,52304,79],{"emptyLinePlaceholder":13},[62,52306,52307,52309,52311,52314,52316,52318],{"class":64,"line":16448},[62,52308,194],{"class":68},[62,52310,200],{"class":68},[62,52312,52313],{"class":122}," setEmail",[62,52315,1049],{"class":72},[62,52317,52059],{"class":889},[62,52319,768],{"class":72},[62,52321,52322,52324,52326,52328],{"class":64,"line":16457},[62,52323,2405],{"class":149},[62,52325,52092],{"class":72},[62,52327,146],{"class":68},[62,52329,52097],{"class":72},[62,52331,52332],{"class":64,"line":16466},[62,52333,223],{"class":72},[62,52335,52336],{"class":64,"line":16471},[62,52337,79],{"emptyLinePlaceholder":13},[62,52339,52340,52342],{"class":64,"line":16487},[62,52341,2143],{"class":72},[62,52343,13555],{"class":68},[62,52345,52346,52348,52350,52352],{"class":64,"line":16504},[62,52347,194],{"class":68},[62,52349,2469],{"class":72},[62,52351,23175],{"class":122},[62,52353,206],{"class":72},[62,52355,52356,52358,52361],{"class":64,"line":16517},[62,52357,360],{"class":68},[62,52359,52360],{"class":1675}," \"Subscriber{\"",[62,52362,52363],{"class":68}," +\n",[62,52365,52366,52369,52371,52374,52376,52379,52381],{"class":64,"line":16523},[62,52367,52368],{"class":1675}," \"first='\"",[62,52370,4507],{"class":68},[62,52372,52373],{"class":72}," first ",[62,52375,1148],{"class":68},[62,52377,52378],{"class":1675}," '",[62,52380,6632],{"class":149},[62,52382,52383],{"class":1675},"'' +\n",[62,52385,52386,52389,52391],{"class":64,"line":16532},[62,52387,52388],{"class":1675}," \", last='\" + last + '",[62,52390,6632],{"class":149},[62,52392,52383],{"class":1675},[62,52394,52395],{"class":64,"line":16546},[62,52396,52397],{"class":1675}," '}';\n",[62,52399,52400],{"class":64,"line":16557},[62,52401,223],{"class":1675},[62,52403,52404],{"class":64,"line":16563},[62,52405,379],{"class":1675},[22,52407,52408],{},"Next, I will create a simple repository so that I can create some dummy data on startup. ",[52,52410,52412],{"className":54,"code":52411,"language":56,"meta":57,"style":57},"package com.therealdanvega.domain;\n\nimport org.springframework.data.repository.CrudRepository;\n\npublic interface SubscriberRepository extends CrudRepository\u003CSubscriber,Long> {\n\n}\n",[59,52413,52414,52420,52424,52431,52435,52459,52463],{"__ignoreMap":57},[62,52415,52416,52418],{"class":64,"line":65},[62,52417,69],{"class":68},[62,52419,51948],{"class":72},[62,52421,52422],{"class":64,"line":76},[62,52423,79],{"emptyLinePlaceholder":13},[62,52425,52426,52428],{"class":64,"line":82},[62,52427,27875],{"class":68},[62,52429,52430],{"class":72}," org.springframework.data.repository.CrudRepository;\n",[62,52432,52433],{"class":64,"line":89},[62,52434,79],{"emptyLinePlaceholder":13},[62,52436,52437,52439,52441,52444,52446,52448,52450,52453,52455,52457],{"class":64,"line":95},[62,52438,116],{"class":68},[62,52440,8531],{"class":68},[62,52442,52443],{"class":122}," SubscriberRepository",[62,52445,8537],{"class":68},[62,52447,22395],{"class":122},[62,52449,760],{"class":72},[62,52451,52452],{"class":68},"Subscriber",[62,52454,32225],{"class":72},[62,52456,6850],{"class":68},[62,52458,8552],{"class":72},[62,52460,52461],{"class":64,"line":101},[62,52462,79],{"emptyLinePlaceholder":13},[62,52464,52465],{"class":64,"line":107},[62,52466,379],{"class":72},[22,52468,52469],{},"Finally, I will load some data using the Command Line Runner. ",[52,52471,52473],{"className":54,"code":52472,"language":56,"meta":57,"style":57},"package com.therealdanvega;\n\nimport com.therealdanvega.domain.Subscriber;\nimport com.therealdanvega.domain.SubscriberRepository;\nimport org.springframework.boot.CommandLineRunner;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.context.annotation.Bean;\n\n@SpringBootApplication\npublic class H2demoApplication {\n\n public static void main(String\\[\\] args) {\n SpringApplication.run(H2demoApplication.class, args);\n }\n\n @Bean\n CommandLineRunner runner(SubscriberRepository repository){\n return args -> {\n repository.save( new Subscriber(\"Dan\", \"Vega\", \"danvega@gmail.com\"));\n };\n }\n}\n\n",[59,52474,52475,52482,52486,52493,52500,52507,52514,52521,52528,52532,52538,52549,52553,52574,52583,52587,52591,52597,52611,52621,52651,52655,52659],{"__ignoreMap":57},[62,52476,52477,52479],{"class":64,"line":65},[62,52478,69],{"class":68},[62,52480,52481],{"class":72}," com.therealdanvega;\n",[62,52483,52484],{"class":64,"line":76},[62,52485,79],{"emptyLinePlaceholder":13},[62,52487,52488,52490],{"class":64,"line":82},[62,52489,27875],{"class":68},[62,52491,52492],{"class":72}," com.therealdanvega.domain.Subscriber;\n",[62,52494,52495,52497],{"class":64,"line":89},[62,52496,27875],{"class":68},[62,52498,52499],{"class":72}," com.therealdanvega.domain.SubscriberRepository;\n",[62,52501,52502,52504],{"class":64,"line":95},[62,52503,27875],{"class":68},[62,52505,52506],{"class":72}," org.springframework.boot.CommandLineRunner;\n",[62,52508,52509,52511],{"class":64,"line":101},[62,52510,27875],{"class":68},[62,52512,52513],{"class":72}," org.springframework.boot.SpringApplication;\n",[62,52515,52516,52518],{"class":64,"line":107},[62,52517,27875],{"class":68},[62,52519,52520],{"class":72}," org.springframework.boot.autoconfigure.SpringBootApplication;\n",[62,52522,52523,52525],{"class":64,"line":113},[62,52524,27875],{"class":68},[62,52526,52527],{"class":72}," org.springframework.context.annotation.Bean;\n",[62,52529,52530],{"class":64,"line":129},[62,52531,79],{"emptyLinePlaceholder":13},[62,52533,52534,52536],{"class":64,"line":134},[62,52535,942],{"class":72},[62,52537,2079],{"class":68},[62,52539,52540,52542,52544,52547],{"class":64,"line":156},[62,52541,116],{"class":68},[62,52543,119],{"class":68},[62,52545,52546],{"class":122}," H2demoApplication",[62,52548,126],{"class":72},[62,52550,52551],{"class":64,"line":161},[62,52552,79],{"emptyLinePlaceholder":13},[62,52554,52555,52557,52559,52561,52563,52565,52567,52570,52572],{"class":64,"line":167},[62,52556,194],{"class":68},[62,52558,2101],{"class":68},[62,52560,200],{"class":68},[62,52562,2106],{"class":122},[62,52564,2109],{"class":72},[62,52566,973],{"class":889},[62,52568,52569],{"class":72},"\\[\\] ",[62,52571,2117],{"class":889},[62,52573,768],{"class":72},[62,52575,52576,52578,52580],{"class":64,"line":173},[62,52577,2124],{"class":72},[62,52579,2127],{"class":122},[62,52581,52582],{"class":72},"(H2demoApplication.class, args);\n",[62,52584,52585],{"class":64,"line":179},[62,52586,223],{"class":72},[62,52588,52589],{"class":64,"line":185},[62,52590,79],{"emptyLinePlaceholder":13},[62,52592,52593,52595],{"class":64,"line":191},[62,52594,2143],{"class":72},[62,52596,2146],{"class":68},[62,52598,52599,52601,52604,52607,52609],{"class":64,"line":209},[62,52600,2151],{"class":72},[62,52602,52603],{"class":122},"runner",[62,52605,52606],{"class":72},"(SubscriberRepository ",[62,52608,23540],{"class":889},[62,52610,34126],{"class":72},[62,52612,52613,52615,52617,52619],{"class":64,"line":220},[62,52614,360],{"class":68},[62,52616,2169],{"class":72},[62,52618,800],{"class":68},[62,52620,126],{"class":72},[62,52622,52623,52626,52628,52631,52633,52635,52637,52639,52641,52644,52646,52649],{"class":64,"line":226},[62,52624,52625],{"class":72}," repository.",[62,52627,22562],{"class":122},[62,52629,52630],{"class":72},"( ",[62,52632,2426],{"class":68},[62,52634,51992],{"class":122},[62,52636,2109],{"class":72},[62,52638,25684],{"class":1675},[62,52640,976],{"class":72},[62,52642,52643],{"class":1675},"\"Vega\"",[62,52645,976],{"class":72},[62,52647,52648],{"class":1675},"\"danvega@gmail.com\"",[62,52650,6979],{"class":72},[62,52652,52653],{"class":64,"line":231},[62,52654,2252],{"class":72},[62,52656,52657],{"class":64,"line":236},[62,52658,223],{"class":72},[62,52660,52661],{"class":64,"line":242},[62,52662,379],{"class":72},[26,52664,52666],{"id":52665},"running-your-spring-boot-application","Running your Spring Boot Application",[22,52668,52669,52670,52674],{},"With all of that in place, this is where people usually run into this issue. If you were to start your application up and visit the H2 console using ",[677,52671,52672],{"href":52672,"rel":52673},"http://localhost:8080/h2-console",[681]," you would see the following screen. What I would expect is when I login to the console to see my Subscriber table with a new row inserted. ",[22,52676,52677],{},[653,52678],{"alt":52679,"src":52680},"H2 Database Console","./2017-05-31_08-29-29.png",[22,52682,52683,52684,52689,52691],{},"The issue here is usually with the JDBC URL that students use to connect to their database. If you put anything in the place of subscribers it would let you connect, but you wouldn't see anything. The URL is usually the last visited URL on your machine but I am not sure what comes up by default. The problem is not understanding what the default datasource name is. In a Spring Boot application, the default datasource is named ",[646,52685,52686],{},[4534,52687,52688],{},"testdb",[4534,52690,2755],{}," If you were to use the URL \"jdbc:h2:mem:testdb\" and connect to the database you would see your table created. ",[22,52693,52694],{},[653,52695],{"alt":52679,"src":52696},"./2017-05-31_08-46-40.png",[22,52698,52699],{},"If you don't want to use testdb as the datasource name for every application you build (and you shouldn't) you can change it using the following property. ",[52,52701,52703],{"className":1663,"code":52702,"language":1665,"meta":57,"style":57},"spring.datasource.name=subscribers\n",[59,52704,52705],{"__ignoreMap":57},[62,52706,52707,52709],{"class":64,"line":65},[62,52708,41985],{"class":122},[62,52710,52711],{"class":1675},"=subscribers\n",[26,52713,1499],{"id":1498},[22,52715,52716],{},"As we saw in this demo it usually isn't the Spring Boot application not working correctly but more often than not my students were connecting to the wrong database. I hope this helped clear up this issue for someone out there and I would like to leave you with a question.",[22,52718,52719],{},[4534,52720,52721,52723],{},[646,52722,49733],{}," What are some common mistakes you run into in your Spring Boot Applications?",[1527,52725,52726],{},"html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .spoOn, html code.shiki .spoOn{--shiki-default:#E36209;--shiki-dark:#FFAB70;--shiki-sepia:#E36209}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}",{"title":57,"searchDepth":76,"depth":76,"links":52728},[52729,52733,52734],{"id":51883,"depth":76,"text":51884,"children":52730},[52731,52732],{"id":51896,"depth":82,"text":51897},{"id":51932,"depth":82,"text":51933},{"id":52665,"depth":76,"text":52666},{"id":1498,"depth":76,"text":1499},{"_id":52736,"path":52737,"title":52738,"description":52738,"meta":52739,"body":52745},"content/blog/2017/05/26/so-you-want-to-create-an-online-course.md","/blog/2017/05/26/so-you-want-to-create-an-online-course","So you want to create an online course?",{"slug":52740,"date":52741,"published":13,"tags":52742,"author":-1,"cover":52744,"excerpt":-1},"so-you-want-to-create-an-online-course","2017-05-26T12:34:08-04:00",[52743],"course","./carl-heyerdahl-181868-1-760x507.jpg",{"type":19,"value":52746,"toc":53509},[52747,52756,52759,52762,52767,52770,52775,52781,52784,52800,52803,52807,52810,52816,52820,52823,52826,52829,52833,52836,52839,52845,52848,52851,52856,52859,52863,52866,52869,52875,52878,52881,52884,52888,52891,52894,52900,52904,52907,52912,52915,52921,52925,52928,52931,52951,52957,52960,52974,52977,52982,52986,52989,53015,53019,53022,53045,53049,53052,53057,53060,53083,53088,53091,53114,53118,53127,53137,53147,53150,53154,53157,53163,53181,53187,53204,53208,53211,53214,53224,53228,53231,53234,53257,53261,53264,53287,53291,53294,53298,53301,53309,53312,53329,53333,53335,53349,53351,53356,53360,53363,53366,53376,53380,53383,53386,53390,53393,53399,53405,53408,53413,53419,53425,53429,53432,53437,53453,53457,53466,53471,53493,53496,53500,53503],[22,52748,52749,52750,52755],{},"I recently wrote this article on ",[677,52751,52754],{"href":52752,"rel":52753},"https://medium.com/@therealdanvega/so-you-want-to-create-an-online-course-8c2519da1863",[681],"Medium"," and thought I would share it here with this audience today. I know there are a lot of Software Developers out there that want to get into creating courses and if that's you I think you're really going to enjoy this article. ",[22,52757,52758],{},"I get really fired up about teaching online because I enjoy doing something that is improving the life of someone else. It might be on a very small scale but I still like to think I am making a difference. I also enjoy that, on occasion, I get someone else fired up about teaching online.",[22,52760,52761],{},"Lately, I have been receiving messages from friends and family about how they want to create their own courses. This week I got a message from a friend that I would like to share with you.",[29685,52763,52764],{},[22,52765,52766],{},"Now that I don’t work at Apple anymore I’m not contractually locked to exclusively teaching in an Apple Store. So I’m currently working on my first Udemy course. Based on what I’ve seen you do I’ve been wanting to do this for years. I’m pretty excited about it! Just thought I’d shoot you a quick message and let you know.",[22,52768,52769],{},"I was not only excited that my friend was going to create his first course but that I had some small part in inspiring him to do so. This reminding me of a quote I love and a great way to look at how helping others can lead us to accomplish our goals.",[29685,52771,52772],{},[22,52773,52774],{},"“You will get all you want in life if you help enough other people get what they want.” — Zig Ziglar",[22,52776,52777],{},[653,52778],{"alt":52779,"src":52780},"Lady Video Shooting","./pexels-photo-322045-683x1024.jpeg",[22,52782,52783],{},"Now that you have a little idea of why I sat down and took some to write this article, here's what we are going to cover in it.",[915,52785,52786,52789,52792,52795,52798],{},[37,52787,52788],{},"What are you going to teach?",[37,52790,52791],{},"Creating your course",[37,52793,52794],{},"Where will you publish?",[37,52796,52797],{},"Building an audience",[37,52799,4098],{},[22,52801,52802],{},"I am going to give you a little information about each of these topics. This is meant as an introduction and I could probably write chapters to a book on each of these subjects so just remember that if we don’t go too far down the rabbit hole.",[26,52804,52806],{"id":52805},"what-are-you-going-toteach","What are you going to teach?",[22,52808,52809],{},"The first thing we need to figure out is what your course is going to be about. You probably have some ideas already and are just wondering if these ideas will translate into courses. In this section, we are going to talk about how finding your passion and validating your subject.",[22,52811,52812],{},[653,52813],{"alt":52814,"src":52815},"Creating Online Courses - What are you going to teach?","./startup-photos-1024x683.jpg",[636,52817,52819],{"id":52818},"find-yourpassion","Find your Passion",[22,52821,52822],{},"The first thing I need everyone reading this to do is to stop assuming that what you know is common knowledge. I run across so many people that think what they know or how they perform a specific task a certain way is just how the world works and that just isn’t true. You do have something to offer the world and it's about time you start realizing it.",[22,52824,52825],{},"The easiest subjects to build courses on are things you’re passionate about. If you love to share your experiences in health and fitness, for example, this is what you should be talking about. Talking about cats might be a popular subject but if you aren’t into it, you will get called out on it.",[22,52827,52828],{},"Your students will know if you’re passionate about a subject or not. If you’re not excited about teaching something, how do you expect students to be excited to learn it? So with that my #1 tip when it comes to teaching is you have to pick something you’re passionate about.",[636,52830,52832],{"id":52831},"selecting-aniche","Selecting a Niche",[22,52834,52835],{},"A big mistake new instructors often make is the belief that just because there are other courses in the marketplace already, that they can’t succeed. When I hear this I quickly turn this into a positive thing and get them even more excited.",[22,52837,52838],{},"Existing courses in the marketplace simply tell us that someone else has already done the hard work for us and validated that there is a demand for our course. Now if there 75 courses on the exact same thing then we need to go a layer deeper. In fact no matter how many courses there are I always suggest going at least 2 levels and maybe event 3 levels deep.",[22,52840,52841],{},[653,52842],{"alt":52843,"src":52844},"Creating Online Courses - Finding your niche","./pexels-photo-256541-1024x682.jpeg",[22,52846,52847],{},"This sounds great Dan, but what do you mean to go a layer deeper? Going back to our example of health and fitness let's say that I wanted to create a course on yoga. It’s safe to assume that yoga has a very wide range of topics and not only will you run into competition there but if you were going to teach a course on everything you knew about yoga that would be a huge course.",[22,52849,52850],{},"So we start thinking about topics inside of yoga and realize that we are very good at standing our head. This is often tough for people new to yoga so we are going to teach them all about getting upside down. We just took a subject we are passionate about, yoga, and found a specific subject inside of that niche and created our Headstand Workshop class. This is what I mean by going to the 2nd or even 3rd level.",[22,52852,52853],{},[653,52854],{"alt":52843,"src":52855},"./pexels-photo-169762-1024x684.jpeg",[22,52857,52858],{},"Sticking with our example, what if we wanted to create a course called “How to do a headstand for 20 min straight”. I am just making this up at this point but you get the idea. This is a very specific topic and if there are no other courses on the market we should probably validate this further before we run off and create a course on this subject.",[636,52860,52862],{"id":52861},"validating-your-courseidea","Validating your course idea",[22,52864,52865],{},"We already looked at how to select your niche if there is a market for your course but what if there are no existing courses on your topic? This doesn’t mean that people still don’t want to learn about it. This just means that we should do a little work upfront to validate our idea before we run off and create a huge course on the subject.",[22,52867,52868],{},"The first place you can always start is with friends and family. Going back to our yoga example it's probably safe to assume that if you’re passionate about yoga that you might do it with friends, take a class or even teach classes. In that case, it's pretty simple, ask those people what they think of your idea. If you come up with a good plan for the workshop you might even be able to teach it to them one day and get their opinion.",[22,52870,52871],{},[653,52872],{"alt":52873,"src":52874},"Creating Online Courses - Validating your ideas","./pexels-photo-26135-1024x681.jpg",[22,52876,52877],{},"After running it by friends and family you can start to ask any social networks you’re involved in. Throw something on Facebook on your wall or in a specific group and see what they say.",[22,52879,52880],{},"If all of these things go well you could create a YouTube channel and test a couple videos out. If you get more interest you could create a blog and ask people to signup if there interested in your course.",[22,52882,52883],{},"This is, of course, the simplified version of this but hopefully, you get the idea. Make sure you validate your idea before you just run in blind. Some of the feedback might be great and you might get ideas you would have never thought of if you didn’t do this step first.",[636,52885,52887],{"id":52886},"how-long-should-itbe","How long Should it be?",[22,52889,52890],{},"Another question I hear a lot is how long does my course need to be. Your course should probably be at least an hour in length. After that, there are no restrictions. You can create a course that is 1 hour, I have created courses that are 14 hours and top online instructors have courses in the 40–60-hour range.",[22,52892,52893],{},"The key here is making sure your course has a solid title and description. Your students should be very clear on what they are going to get out of this course before they take it. At that point, your course should be as long as it needs to be to deliver on what you promised them they would learn.",[22,52895,52896],{},[653,52897],{"alt":52898,"src":52899},"Creating Online Courses - Course Length","./pexels-photo-66134-1024x683.jpeg",[26,52901,52903],{"id":52902},"creating-yourcourse","Creating your Course",[22,52905,52906],{},"When I start talking to people about creating courses they start dreaming of big budget Hollywood sets when all they need is a home office. There might be a few things you need to purchase but you can always start off small and scale up later on. In this section, I will give you a good, better and best_*_options for equipment and my suggestions for starting off.",[22,52908,52909],{},[4534,52910,52911],{},"* Best doesn’t mean a $50,000 camera it just means it’s my suggestion for the next logical step.",[22,52913,52914],{},"This is obviously an important step so take your time here and get this one right.",[22,52916,52917],{},[653,52918],{"alt":52919,"src":52920},"Creating Online Courses - Creating your course.","https://cdn-images-1.medium.com/max/800/1*mdUW2rOKrVPk5zxc1aonEQ.jpeg",[636,52922,52924],{"id":52923},"creating-a-curriculum","Creating a Curriculum",[22,52926,52927],{},"The first thing we need to do is create a course curriculum. The important part here to remember is that this is probably going to be fluid and can change during the course creation process.",[22,52929,52930],{},"I like to start with an outline of what each module or section will look like. It is probably a good idea to start with an introduction to you the instructor and what they can plan to get out of the course. If I was teaching a course on how to create your own course my initial outline might look like this.",[915,52932,52933,52936,52939,52942,52945,52948],{},[37,52934,52935],{},"Introduction",[37,52937,52938],{},"What are you going to teach",[37,52940,52941],{},"Creating your Course",[37,52943,52944],{},"Where will you publish",[37,52946,52947],{},"Marketing your course",[37,52949,52950],{},"Goodbye",[22,52952,52953],{},[653,52954],{"alt":52955,"src":52956},"Creating Online Courses - Create your curriculum","./pexels-photo-139496-1024x769.jpeg",[22,52958,52959],{},"This would be my initial outline but just looking at this I can already tell that the creating your course section would get very long. I would probably break curriculum, video, audio & lighting into their own sections. Here are a few tips that I have seen in other courses and try to stick to in any of my new courses.",[915,52961,52962,52965,52968,52971],{},[37,52963,52964],{},"Mix talking head and screen share videos",[37,52966,52967],{},"Keep your videos 4–7 min and no longer than 10 min unless needed",[37,52969,52970],{},"Try for 10 videos or less in a section",[37,52972,52973],{},"Keep students engaged with exercises & quizzes",[22,52975,52976],{},"Those are of course just guidelines. You have to remember that you are here to help your students accomplish a certain goal. If you need to go outside the lines to do that, then have at it.",[22,52978,52979],{},[4534,52980,52981],{},"Some of my links below are affiliate links and if you’re going to purchase something please use my links to support this article.",[636,52983,52985],{"id":52984},"video-equipment","Video Equipment",[22,52987,52988],{},"I shot my first course entirely on my iPhone 6. The only downside to this is you can’t see yourself in the shot so you have to keep running around to see if the shot looks good. I eventually upgraded to the Canon 70d and I love the flip screen and auto focus on that camera.",[915,52990,52991,52999,53007],{},[37,52992,52993,52994],{},"(Good) ",[677,52995,52998],{"href":52996,"rel":52997},"http://amzn.to/2pH94eH",[681],"iPhone 7 Plus",[37,53000,53001,53002],{},"(Better) ",[677,53003,53006],{"href":53004,"rel":53005},"http://amzn.to/2pHsPCX",[681],"Canon 70D",[37,53008,53009,53010],{},"(Best) ",[677,53011,53014],{"href":53012,"rel":53013},"http://amzn.to/2rb0zd8",[681],"Sony Alpha a7s Mirrorless Digital Camera",[28831,53016,53018],{"id":53017},"lighting-equipment","Lighting Equipment",[22,53020,53021],{},"Lighting is one of those things that everyone forgets about. If you can get some good natural light that is great but that limits your production time to those perfect days.",[915,53023,53024,53031,53038],{},[37,53025,52993,53026],{},[677,53027,53030],{"href":53028,"rel":53029},"http://amzn.to/2pIuZlP",[681],"Photography Photo Portrait Studio Kit",[37,53032,53001,53033],{},[677,53034,53037],{"href":53035,"rel":53036},"http://amzn.to/2pvxprS",[681],"Fancierstudio 2400 Watt Lighting Kit",[37,53039,53009,53040],{},[677,53041,53044],{"href":53042,"rel":53043},"http://amzn.to/2qdHSqW",[681],"LimoStudio LED Lighting Kit",[28831,53046,53048],{"id":53047},"audio-equipment","Audio Equipment",[22,53050,53051],{},"Nothing ruins a video for me more than bad audio. If you asked me where you should spend money for a better quality product its here.",[22,53053,53054],{},[646,53055,53056],{},"Talking Head Videos",[22,53058,53059],{},"These are videos where it is just you standing in front of the camera and having a conversation with your students.",[915,53061,53062,53069,53076],{},[37,53063,52993,53064],{},[677,53065,53068],{"href":53066,"rel":53067},"http://amzn.to/2pIQyCF",[681],"Stony-Edge Lavalier Microphone",[37,53070,53001,53071],{},[677,53072,53075],{"href":53073,"rel":53074},"http://amzn.to/2qDiCf7",[681],"Rode VideoMic Pro",[37,53077,53009,53078],{},[677,53079,53082],{"href":53080,"rel":53081},"http://amzn.to/2qDkZOX",[681],"Sennheiser Wireless Mic Kit",[22,53084,53085],{},[646,53086,53087],{},"Screencast",[22,53089,53090],{},"These are videos where you record your screen showing them a presentation or exactly what you’re doing on screen.",[915,53092,53093,53100,53107],{},[37,53094,52993,53095],{},[677,53096,53099],{"href":53097,"rel":53098},"http://amzn.to/2r3RN3x",[681],"Blue Snowball iCE Condenser Microphone",[37,53101,53001,53102],{},[677,53103,53106],{"href":53104,"rel":53105},"http://amzn.to/2rbyrWB",[681],"Audio-Technica AT2020 Condenser Microphone",[37,53108,53009,53109],{},[677,53110,53113],{"href":53111,"rel":53112},"http://amzn.to/2qDjtfx",[681],"Heil PR-40 Dynamic Studio Recording Microphone",[28831,53115,53117],{"id":53116},"backgrounds","Backgrounds",[22,53119,53120,53121,53126],{},"If you have a great looking place around the house to shoot you should take advantage of that. If you don’t you can use ",[677,53122,53125],{"href":53123,"rel":53124},"http://amzn.to/2r4sP45",[681],"seamless background"," paper to give you a really good looking video. To hang that background paper you need either a stand or some type of wall mount.",[22,53128,53129],{},[677,53130,53133,53136],{"href":53131,"rel":53132,"title":53131},"http://amzn.to/2qDfmQQ",[681],[646,53134,53135],{},"LimoStudio Photo Video Studio 10Ft Adjustable Muslin Background Backdrop Support System Stand…"," _Backdrop Support System with 10' Crossbar and Case 2 x Premire Muslin Backdrop Support System Stand * Easy to set up…_amzn.to",[22,53138,53139],{},[677,53140,53143,53146],{"href":53141,"rel":53142,"title":53141},"http://amzn.to/2pIKKso",[681],[646,53144,53145],{},"CowboyStudio Photography 3-Roller Wall Mounting Manual Background Support System"," _This background support system is a smart choice for substitute of expensive electrical roller system. Durable yet…_amzn.to",[22,53148,53149],{},"I have the 3 roller wall mount in my home studio and it works out great.",[28831,53151,53153],{"id":53152},"software","Software",[22,53155,53156],{},"There are a few pieces of software that you are going to need to create and publish your course. There are some free options in this category but as you can imagine a little money spent here goes a long way.",[22,53158,53159,53162],{},[646,53160,53161],{},"Video Editing Software"," When you create your videos you will take them from the source and there will be some editing needed.",[915,53164,53165,53173],{},[37,53166,53167,53172],{},[677,53168,53171],{"href":53169,"rel":53170},"https://www.apple.com/final-cut-pro/",[681],"Final Cut Pro"," (Mac)",[37,53174,53175,53180],{},[677,53176,53179],{"href":53177,"rel":53178},"http://www.adobe.com/products/premiere.html",[681],"Adobe Premiere Pro"," (Mac & Windows)",[22,53182,53183,53186],{},[646,53184,53185],{},"Screen Recording Software"," There are a bunch of options for recording screen presentations but I am going to leave you with 2 of my favorites for Mac and Windows.",[915,53188,53189,53196],{},[37,53190,53191,53172],{},[677,53192,53195],{"href":53193,"rel":53194},"https://www.telestream.net/screenflow/overview.htm",[681],"ScreenFlow",[37,53197,53198,53203],{},[677,53199,53202],{"href":53200,"rel":53201},"https://www.techsmith.com/camtasia.html",[681],"Camtasia Studio"," (Windows)",[636,53205,53207],{"id":53206},"where-to-publish-yourcourse","Where to publish your course?",[22,53209,53210],{},"Now that we have created our course the question is where do we publish it. There are plenty of options depending on your needs and what features you want. This decision is going to depend on what type of existing following you or your brand has or doesn’t have.",[22,53212,53213],{},"If you have an existing audience that comes with a large email list and social media following you are going to be self-hosting your course. If you don’t have an existing following we are going to start with marketplaces. The beauty of starting here is we can build our audience and then begin to move to a self-hosted platform.",[22,53215,53216],{},[677,53217,53220],{"href":53218,"rel":53219},"https://danvega.dev/wp-content/uploads/2017/05/1-Ouwik01c13p99QCkzRT7AA.jpeg",[681],[653,53221],{"alt":53222,"src":53223},"Creating Online Course - Where to publish your course?","./1-Ouwik01c13p99QCkzRT7AA-1024x682.jpeg",[28831,53225,53227],{"id":53226},"marketplace","Marketplace",[22,53229,53230],{},"A marketplace is a site that contains a collection of courses and already has an existing user base in place. What this means to you is that all you need to focus on is publishing the course. If you create a good title, description and eventually get some good reviews the course will sell itself.",[22,53232,53233],{},"Some examples of marketplaces that you can publish your course on are:",[915,53235,53236,53243,53250],{},[37,53237,53238],{},[677,53239,53242],{"href":53240,"rel":53241},"http://www.udemy.com/",[681],"Udemy",[37,53244,53245],{},[677,53246,53249],{"href":53247,"rel":53248},"https://www.skillshare.com/",[681],"SkillShare",[37,53251,53252],{},[677,53253,53256],{"href":53254,"rel":53255},"https://www.pluralsight.com/",[681],"Pluralsight",[28831,53258,53260],{"id":53259},"self-hosted","Self-Hosted",[22,53262,53263],{},"A self-hosted course is one that we host on our own website or we can use one the platforms below. The key in a self-hosted course is we have full control over all aspects of the course from collecting email addresses to what it will be priced at.",[915,53265,53266,53273,53280],{},[37,53267,53268],{},[677,53269,53272],{"href":53270,"rel":53271},"http://www.teachable.com/",[681],"Teachable",[37,53274,53275],{},[677,53276,53279],{"href":53277,"rel":53278},"https://www.thinkific.com/",[681],"Thinkific",[37,53281,53282],{},[677,53283,53286],{"href":53284,"rel":53285},"https://zippycoursesplugin.com/",[681],"Zippy Courses",[28831,53288,53290],{"id":53289},"marketplace-vs-selfhosted","Marketplace vs Self Hosted",[22,53292,53293],{},"There are pros and cons to each of our platforms. As I said earlier if you don’t have much of a following yet, that is ok, but I would start on a marketplace. With that said you should make the move that you think is right for you and your brand.",[22,53295,53296],{},[646,53297,53227],{},[22,53299,53300],{},"Pros",[915,53302,53303,53306],{},[37,53304,53305],{},"Existing Student Base",[37,53307,53308],{},"They will promote your courses",[22,53310,53311],{},"Cons",[915,53313,53314,53317,53320,53323,53326],{},[37,53315,53316],{},"You can’t build your email list",[37,53318,53319],{},"Usually going to sell courses for less",[37,53321,53322],{},"Paid on a monthly basis",[37,53324,53325],{},"You have to go through an approval process",[37,53327,53328],{},"They have control over pricing limits",[22,53330,53331],{},[646,53332,53260],{},[22,53334,53300],{},[915,53336,53337,53340,53343,53346],{},[37,53338,53339],{},"Build your email list",[37,53341,53342],{},"You can usually make more per sale",[37,53344,53345],{},"You can bundle courses together to create new products",[37,53347,53348],{},"You can get paid right away.",[22,53350,53311],{},[915,53352,53353],{},[37,53354,53355],{},"You have to do all the promotion for your course",[26,53357,53359],{"id":53358},"building-anaudience","Building an Audience",[22,53361,53362],{},"The best thing you can do for yourself after you launch your course is to stay involved with your students. This is where I always tell people that there is no such thing as 100% passive income. You need stay involved and respond to questions and feedback you are going to establish trust with your audience.",[22,53364,53365],{},"When you can establish trust and build an audience everyone wins. Your students win because they get out of your course exactly what you promised. You win in both monetary value and knowing that you helped to improve someone's life.",[22,53367,53368],{},[677,53369,53372],{"href":53370,"rel":53371},"https://danvega.dev/wp-content/uploads/2017/05/pexels-photo-66463.jpeg",[681],[653,53373],{"alt":53374,"src":53375},"Creating Online Course - Building an audience?","./pexels-photo-66463-1024x683.jpeg",[636,53377,53379],{"id":53378},"course-feedback","Course Feedback",[22,53381,53382],{},"The first thing you need to do when you launch your course is to get it in front of some people to get some initial feedback. I usually start with friends or family. In my case, my courses were programming related so I created free coupons for my coworkers. This is a great time to get some feedback and see if you can improve on anything.",[22,53384,53385],{},"If your course is in a marketplace this is also a good time to start getting reviews. Potential students are looking at your reviews to see what other people think of your course. I would send out free coupons and say in exchange for the free coupon please give me a review. It doesn’t need to be good just your honest review of the course.",[26,53387,53389],{"id":53388},"promoting-yourcourse","Promoting your Course",[22,53391,53392],{},"No matter where you publish your course you want to be out there promoting your course.",[22,53394,53395],{},[653,53396],{"alt":53397,"src":53398},"Creating Online Course - Promoting your course","./marketing-man-person-communication-1024x683.jpg",[22,53400,53401,53404],{},[646,53402,53403],{},"Blog, Social Media & Email List"," If you have a blog this is obviously the best place to start. I would write up a summary of what your course is about and what potential students can get out of it. Most of the time you will already have this copy in the course description.",[22,53406,53407],{},"If you have your own blog this a great chance to start building your email list. This list will help you market future courses to students who are already interested in what you’re doing.",[22,53409,53410,53412],{},[646,53411,37949],{}," This is one of the best places to market your course. Start a YouTube channel and post some of the videos on your channel. In the video you will want to link to your course landing page so they know where to go to purchase the full course.",[22,53414,53415,53418],{},[646,53416,53417],{},"Webinar"," Webinars are another great resource for promoting your course. This is a chance to give some real value to anyone attending your webinar and to teach them something. At the beginning of the webinar let them know that you have a special offer for them coming at the end of the presentation. This is where you can let them know about the course and good strategy here is to give them some kind of discount.",[22,53420,53421,53424],{},[646,53422,53423],{},"Paid Advertising"," I won’t get too far into strategy here because there is a lot that goes into this. You can use paid advertising (Google/Facebook/YouTube/Pinterest) to drive people to landing pages that will allow the to sign up for webinars or lead magnets related to your course. When you have their email address you can slowly introduce them to your course and eventually ask them to purchase it. Again this is for another article but pushing paid advertising directly to a course landing page usually doesn’t work.",[26,53426,53428],{"id":53427},"learning-resources","Learning / Resources",[22,53430,53431],{},"I really hope this article was useful for you. Now that you have a good idea what it’s going to take to build your course I want to leave you with some resources. These are all great ways to learn more about building your course.",[22,53433,53434],{},[646,53435,53436],{},"Courses",[915,53438,53439,53446],{},[37,53440,53441],{},[677,53442,53445],{"href":53443,"rel":53444},"https://www.udemy.com/online-course-masters/?couponCode=DANVEGA19",[681],"Udemy Masters 2017: Online Course Creation",[37,53447,53448],{},[677,53449,53452],{"href":53450,"rel":53451},"https://www.udemy.com/user/udemymanager/",[681],"Official Udemy Insights",[22,53454,53455],{},[646,53456,37576],{},[915,53458,53459],{},[37,53460,53461],{},[677,53462,53465],{"href":53463,"rel":53464},"https://onlinecoursemasters.com/blog",[681],"Online Course Masters",[22,53467,53468],{},[646,53469,53470],{},"Facebook Groups",[915,53472,53473,53479,53486],{},[37,53474,53475],{},[677,53476,53465],{"href":53477,"rel":53478},"https://www.facebook.com/groups/onlinecoursemasters/",[681],[37,53480,53481],{},[677,53482,53485],{"href":53483,"rel":53484},"https://www.facebook.com/groups/thefedorafamily/",[681],"Teachable Tribe",[37,53487,53488],{},[677,53489,53492],{"href":53490,"rel":53491},"https://www.facebook.com/groups/udemyfacultylounge/",[681],"Udemy Instructors Club",[22,53494,53495],{},"While I do believe that investing in yourself and continuing to learn is the greatest gift you can give yourself you do need to take action at some point. Spend the time to learn but don’t procrastinate creating your first course. What do you have to lose? Take action and build something great!",[26,53497,53499],{"id":53498},"whats-nextdan","What’s Next Dan?",[22,53501,53502],{},"If you enjoyed this article you are really going to enjoy my next project. Subscribe to my mailing list and I will keep you in the loop on my next big thing. If you signup today I will give you a free eBook that will give you my top 10 side hustles to build a passive income.",[22,53504,53505],{},[677,53506,53507],{"href":53507,"rel":53508},"https://danvega.dev/sidehustle",[681],{"title":57,"searchDepth":76,"depth":76,"links":53510},[53511,53517,53522,53525,53526,53527],{"id":52805,"depth":76,"text":52806,"children":53512},[53513,53514,53515,53516],{"id":52818,"depth":82,"text":52819},{"id":52831,"depth":82,"text":52832},{"id":52861,"depth":82,"text":52862},{"id":52886,"depth":82,"text":52887},{"id":52902,"depth":76,"text":52903,"children":53518},[53519,53520,53521],{"id":52923,"depth":82,"text":52924},{"id":52984,"depth":82,"text":52985},{"id":53206,"depth":82,"text":53207},{"id":53358,"depth":76,"text":53359,"children":53523},[53524],{"id":53378,"depth":82,"text":53379},{"id":53388,"depth":76,"text":53389},{"id":53427,"depth":76,"text":53428},{"id":53498,"depth":76,"text":53499},{"_id":53529,"path":53530,"title":53531,"description":53531,"meta":53532,"body":53538},"content/blog/2017/05/24/contributing-groovy-website.md","/blog/2017/05/24/contributing-groovy-website","How you can contribute to the Groovy Website",{"slug":53533,"date":53534,"published":13,"tags":53535,"author":-1,"cover":53537,"excerpt":-1},"contributing-groovy-website","2017-05-24T08:12:01-04:00",[53536],"groovy","./2017-05-23_19-50-54-760x462.png",{"type":19,"value":53539,"toc":54565},[53540,53555,53559,53568,53572,53586,53592,53596,53605,53608,53611,53789,53796,53800,53803,54046,54049,54239,54242,54487,54491,54494,54507,54513,54516,54520,54523,54529,54532,54540,54545,54550,54552,54555,54562],[22,53541,53542,53543,53548,53549,53554],{},"& Anyone who knows me knows that I am a huge fan of ",[677,53544,53547],{"href":53545,"rel":53546},"http://groovy-lang.org/",[681],"The Groovy Programming Language",". So much so that I did my part to spread the good word about Groovy by creating a course titled \"",[677,53550,53553],{"href":53551,"rel":53552},"https://danvega.dev/groovy",[681],"The Complete Apache Groovy Developer Course","\". I recently had the opportunity to contribute to the Groovy website. In this article, I will tell you all about what I did and how you can contribute as well. ",[26,53556,53558],{"id":53557},"contributing-to-the-groovy-website","Contributing to the Groovy Website",[22,53560,53561,53562,53567],{},"The website is ",[677,53563,53566],{"href":53564,"rel":53565},"https://github.com/groovy/groovy-website",[681],"open source and on Github"," so it was really easy to get started with. Contributing to an open source project isn't always all about adding that cool new feature to the core of the project. I wanted to add a resource on the learn page so that people could find my course. I am probably a little biased but I think my course is one of the best resources around for learning Groovy. So now on top of teaching folks how to use the language I made some updates to the website.",[26,53569,53571],{"id":53570},"fork-pull-request","Fork & Pull Request",[22,53573,53574,53575,53579,53580,53585],{},"I am not an official committer to the project so the first thing we need to do is to fork the repo. To do this we are going to head over the repo for the ",[677,53576,53578],{"href":53564,"rel":53577},[681],"Groovy Website"," and fork it. This means that we are going to make changes on our GitHub account and then send them a pull request. You can see my commits below along with the project lead ",[677,53581,53584],{"href":53582,"rel":53583},"http://glaforge.appspot.com/",[681],"Guillaume Laforge"," merging them into the master branch. ",[22,53587,53588],{},[653,53589],{"alt":53590,"src":53591},"Groovy","./2017-05-23_21-47-34.png",[26,53593,53595],{"id":53594},"contributing","Contributing",[22,53597,53598,53599,53604],{},"Now that we understand the workflow we can take a look at how to make that change. If you look at the readme for the source code there is a ton of great information to get you started. The following information is straight from the readme. The website is generated thanks to Gradle and makes use of the ",[677,53600,53603],{"href":53601,"rel":53602},"http://docs.groovy-lang.org/latest/html/documentation/markup-template-engine.html",[681],"Markup Template Engine",". The structure of the project consists of two modules:",[22,53606,53607],{},"generator : utility classes and model for generating the website\nsite : the website itself",[22,53609,53610],{},"The website subproject consists of:",[52,53612,53614],{"className":1663,"code":53613,"language":1665,"meta":57,"style":57},"src/main/site : sources for the static website\n |--- assets : static resources such as images, CSS files, ...\n |--- html : elements that templates include as raw HTML contents\n |--- includes : includes used by templates\n |--- layouts : layouts for the various pages\n |--- pages : individual pages\nbuild.gradle : website weaving logic\n",[59,53615,53616,53638,53675,53708,53731,53755,53772],{"__ignoreMap":57},[62,53617,53618,53621,53624,53627,53630,53633,53635],{"class":64,"line":65},[62,53619,53620],{"class":122},"src/main/site",[62,53622,53623],{"class":1675}," :",[62,53625,53626],{"class":1675}," sources",[62,53628,53629],{"class":1675}," for",[62,53631,53632],{"class":1675}," the",[62,53634,2101],{"class":1675},[62,53636,53637],{"class":1675}," website\n",[62,53639,53640,53643,53646,53649,53652,53654,53657,53660,53663,53666,53669,53672],{"class":64,"line":76},[62,53641,53642],{"class":68}," |",[62,53644,53645],{"class":122},"---",[62,53647,53648],{"class":1675}," assets",[62,53650,53651],{"class":1675}," :",[62,53653,2101],{"class":1675},[62,53655,53656],{"class":1675}," resources",[62,53658,53659],{"class":1675}," such",[62,53661,53662],{"class":1675}," as",[62,53664,53665],{"class":1675}," images,",[62,53667,53668],{"class":1675}," CSS",[62,53670,53671],{"class":1675}," files,",[62,53673,53674],{"class":1675}," ...\n",[62,53676,53677,53679,53681,53683,53686,53689,53691,53694,53697,53699,53702,53705],{"class":64,"line":82},[62,53678,53642],{"class":68},[62,53680,53645],{"class":122},[62,53682,15807],{"class":1675},[62,53684,53685],{"class":1675}," :",[62,53687,53688],{"class":1675}," elements",[62,53690,50417],{"class":1675},[62,53692,53693],{"class":1675}," templates",[62,53695,53696],{"class":1675}," include",[62,53698,53662],{"class":1675},[62,53700,53701],{"class":1675}," raw",[62,53703,53704],{"class":1675}," HTML",[62,53706,53707],{"class":1675}," contents\n",[62,53709,53710,53712,53714,53717,53720,53722,53725,53728],{"class":64,"line":89},[62,53711,53642],{"class":68},[62,53713,53645],{"class":122},[62,53715,53716],{"class":1675}," includes",[62,53718,53719],{"class":1675}," :",[62,53721,53716],{"class":1675},[62,53723,53724],{"class":1675}," used",[62,53726,53727],{"class":1675}," by",[62,53729,53730],{"class":1675}," templates\n",[62,53732,53733,53735,53737,53740,53743,53745,53747,53749,53752],{"class":64,"line":95},[62,53734,53642],{"class":68},[62,53736,53645],{"class":122},[62,53738,53739],{"class":1675}," layouts",[62,53741,53742],{"class":1675}," :",[62,53744,53739],{"class":1675},[62,53746,53629],{"class":1675},[62,53748,53632],{"class":1675},[62,53750,53751],{"class":1675}," various",[62,53753,53754],{"class":1675}," pages\n",[62,53756,53757,53759,53761,53764,53767,53770],{"class":64,"line":101},[62,53758,53642],{"class":68},[62,53760,53645],{"class":122},[62,53762,53763],{"class":1675}," pages",[62,53765,53766],{"class":1675}," :",[62,53768,53769],{"class":1675}," individual",[62,53771,53754],{"class":1675},[62,53773,53774,53777,53780,53783,53786],{"class":64,"line":107},[62,53775,53776],{"class":122},"build.gradle",[62,53778,53779],{"class":1675}," :",[62,53781,53782],{"class":1675}," website",[62,53784,53785],{"class":1675}," weaving",[62,53787,53788],{"class":1675}," logic\n",[22,53790,53791,53792,2755],{},"Additional details can be found in this ",[677,53793,38238],{"href":53794,"rel":53795},"http://melix.github.io/blog/2014/07/new-groovy-website.html",[681],[636,53797,53799],{"id":53798},"adding-courses-to-the-learn-page","Adding Courses to the Learn Page",[22,53801,53802],{},"There was no courses section when I started this so the first thing I needed to do was to add a new model for courses. ",[52,53804,53806],{"className":54,"code":53805,"language":56,"meta":57,"style":57},"package model\n\nimport groovy.transform.CompileStatic\n\n@CompileStatic\nclass Course {\n\n String title\n String instructor\n String url\n String description\n String cover\n\n void title(String title) {\n this.title = title\n }\n\n void instructor(String instructor) {\n this.instructor = instructor\n }\n\n void url(String url){\n this.url = url\n }\n\n void description(String description){\n this.description = description\n }\n\n void cover(String cover){\n this.cover = cover\n }\n\n}\n",[59,53807,53808,53815,53819,53837,53841,53853,53863,53867,53875,53882,53889,53896,53903,53907,53919,53926,53930,53934,53946,53953,53957,53961,53973,53980,53984,53988,54000,54007,54011,54015,54027,54034,54038,54042],{"__ignoreMap":57},[62,53809,53810,53812],{"class":64,"line":65},[62,53811,69],{"class":68},[62,53813,53814],{"class":72}," model\n",[62,53816,53817],{"class":64,"line":76},[62,53818,79],{"emptyLinePlaceholder":13},[62,53820,53821,53823,53826,53829,53832,53834],{"class":64,"line":82},[62,53822,27875],{"class":23824},[62,53824,53825],{"class":72}," groovy.transform.",[62,53827,53828],{"class":23824},"C",[62,53830,53831],{"class":72},"ompile",[62,53833,37504],{"class":23824},[62,53835,53836],{"class":72},"tatic\n",[62,53838,53839],{"class":64,"line":89},[62,53840,79],{"emptyLinePlaceholder":13},[62,53842,53843,53845,53847,53849,53851],{"class":64,"line":95},[62,53844,942],{"class":72},[62,53846,53828],{"class":23824},[62,53848,53831],{"class":72},[62,53850,37504],{"class":23824},[62,53852,53836],{"class":72},[62,53854,53855,53857,53860],{"class":64,"line":101},[62,53856,11671],{"class":23824},[62,53858,53859],{"class":23824}," C",[62,53861,53862],{"class":72},"ourse {\n",[62,53864,53865],{"class":64,"line":107},[62,53866,79],{"emptyLinePlaceholder":13},[62,53868,53869,53872],{"class":64,"line":113},[62,53870,53871],{"class":23824}," S",[62,53873,53874],{"class":72},"tring title\n",[62,53876,53877,53879],{"class":64,"line":129},[62,53878,53871],{"class":23824},[62,53880,53881],{"class":72},"tring instructor\n",[62,53883,53884,53886],{"class":64,"line":134},[62,53885,53871],{"class":23824},[62,53887,53888],{"class":72},"tring url\n",[62,53890,53891,53893],{"class":64,"line":156},[62,53892,53871],{"class":23824},[62,53894,53895],{"class":72},"tring description\n",[62,53897,53898,53900],{"class":64,"line":161},[62,53899,53871],{"class":23824},[62,53901,53902],{"class":72},"tring cover\n",[62,53904,53905],{"class":64,"line":167},[62,53906,79],{"emptyLinePlaceholder":13},[62,53908,53909,53911,53914,53916],{"class":64,"line":173},[62,53910,11710],{"class":23824},[62,53912,53913],{"class":72}," title(",[62,53915,37504],{"class":23824},[62,53917,53918],{"class":72},"tring title) {\n",[62,53920,53921,53923],{"class":64,"line":179},[62,53922,2405],{"class":23824},[62,53924,53925],{"class":72},".title = title\n",[62,53927,53928],{"class":64,"line":185},[62,53929,223],{"class":72},[62,53931,53932],{"class":64,"line":191},[62,53933,79],{"emptyLinePlaceholder":13},[62,53935,53936,53938,53941,53943],{"class":64,"line":209},[62,53937,11710],{"class":23824},[62,53939,53940],{"class":72}," instructor(",[62,53942,37504],{"class":23824},[62,53944,53945],{"class":72},"tring instructor) {\n",[62,53947,53948,53950],{"class":64,"line":220},[62,53949,2405],{"class":23824},[62,53951,53952],{"class":72},".instructor = instructor\n",[62,53954,53955],{"class":64,"line":226},[62,53956,223],{"class":72},[62,53958,53959],{"class":64,"line":231},[62,53960,79],{"emptyLinePlaceholder":13},[62,53962,53963,53965,53968,53970],{"class":64,"line":236},[62,53964,11710],{"class":23824},[62,53966,53967],{"class":72}," url(",[62,53969,37504],{"class":23824},[62,53971,53972],{"class":72},"tring url){\n",[62,53974,53975,53977],{"class":64,"line":242},[62,53976,2405],{"class":23824},[62,53978,53979],{"class":72},".url = url\n",[62,53981,53982],{"class":64,"line":247},[62,53983,223],{"class":72},[62,53985,53986],{"class":64,"line":252},[62,53987,79],{"emptyLinePlaceholder":13},[62,53989,53990,53992,53995,53997],{"class":64,"line":257},[62,53991,11710],{"class":23824},[62,53993,53994],{"class":72}," description(",[62,53996,37504],{"class":23824},[62,53998,53999],{"class":72},"tring description){\n",[62,54001,54002,54004],{"class":64,"line":271},[62,54003,2405],{"class":23824},[62,54005,54006],{"class":72},".description = description\n",[62,54008,54009],{"class":64,"line":281},[62,54010,223],{"class":72},[62,54012,54013],{"class":64,"line":286},[62,54014,79],{"emptyLinePlaceholder":13},[62,54016,54017,54019,54022,54024],{"class":64,"line":291},[62,54018,11710],{"class":23824},[62,54020,54021],{"class":72}," cover(",[62,54023,37504],{"class":23824},[62,54025,54026],{"class":72},"tring cover){\n",[62,54028,54029,54031],{"class":64,"line":296},[62,54030,2405],{"class":23824},[62,54032,54033],{"class":72},".cover = cover\n",[62,54035,54036],{"class":64,"line":302},[62,54037,223],{"class":72},[62,54039,54040],{"class":64,"line":308},[62,54041,79],{"emptyLinePlaceholder":13},[62,54043,54044],{"class":64,"line":314},[62,54045,379],{"class":72},[22,54047,54048],{},"Then in the sitemap which contains all of the data in markup form, I just add a new course. ",[52,54050,54052],{"className":32791,"code":54051,"language":32793,"meta":57,"style":57},"courses {\n course('The Complete Apache Groovy Developer Course') {\n instructor 'Dan Vega'\n url 'https://www.udemy.com/apache-groovy'\n description '''\n \u003Cp>I am going to teach you everything you need to know to start using The Groovy Programming language. This course is really designed\n for 2 different types of people and I think both will benefit from it. If you’re a beginner programmer with a some experience in\n another language like Python or Ruby this course is for you. Dynamic languages are generally thought of as easier for total beginners\n to learn because they’re flexible and fun. If you’re an existing Java Developer (Beginner or Experienced) this course is also for you.\u003C/p>\n\n \u003Cp>This course is packed with almost 14 hours of content. We are going to start off with getting your development environment up and running\n and then go through the very basics of the language. From there we are going to build on that in each section cover topics like closures, meta-programming,\n builders and so much more. I feel like this is one of the most complete courses around and I am excited for you to join me on this adventure.\u003C/p>\n '''\n cover 'groovy-course-cover.png'\n }\n}\n\n",[59,54053,54054,54059,54071,54079,54087,54095,54103,54133,54158,54180,54184,54193,54198,54207,54212,54231,54235],{"__ignoreMap":57},[62,54055,54056],{"class":64,"line":65},[62,54057,54058],{"class":72},"courses {\n",[62,54060,54061,54064,54066,54069],{"class":64,"line":76},[62,54062,54063],{"class":122}," course",[62,54065,2109],{"class":72},[62,54067,54068],{"class":1675},"'The Complete Apache Groovy Developer Course'",[62,54070,768],{"class":72},[62,54072,54073,54076],{"class":64,"line":82},[62,54074,54075],{"class":72}," instructor ",[62,54077,54078],{"class":1675},"'Dan Vega'\n",[62,54080,54081,54084],{"class":64,"line":89},[62,54082,54083],{"class":72}," url ",[62,54085,54086],{"class":1675},"'https://www.udemy.com/apache-groovy'\n",[62,54088,54089,54092],{"class":64,"line":95},[62,54090,54091],{"class":72}," description ",[62,54093,54094],{"class":1675},"'''\n",[62,54096,54097,54100],{"class":64,"line":101},[62,54098,54099],{"class":1675}," \u003Cp>I am going to teach you everything you need to know to start using The Groovy Programming language. This course is really designe",[62,54101,54102],{"class":23824},"d\n",[62,54104,54105,54108,54110,54113,54115,54118,54121,54124,54127,54130],{"class":64,"line":107},[62,54106,54107],{"class":72}," for ",[62,54109,5219],{"class":149},[62,54111,54112],{"class":72}," different types ",[62,54114,3298],{"class":68},[62,54116,54117],{"class":72}," people and ",[62,54119,54120],{"class":149},"I",[62,54122,54123],{"class":72}," think both will benefit from it. If you’re a beginner programmer ",[62,54125,54126],{"class":68},"with",[62,54128,54129],{"class":72}," a some experience ",[62,54131,54132],{"class":68},"in\n",[62,54134,54135,54138,54140,54143,54145,54147,54150,54152,54155],{"class":64,"line":113},[62,54136,54137],{"class":72}," another language like Python or Ruby ",[62,54139,1295],{"class":149},[62,54141,54142],{"class":72}," course is for you. Dynamic languages are generally thought ",[62,54144,3298],{"class":68},[62,54146,53662],{"class":68},[62,54148,54149],{"class":122}," easier",[62,54151,53629],{"class":122},[62,54153,54154],{"class":122}," total",[62,54156,54157],{"class":122}," beginners\n",[62,54159,54160,54163,54166,54169,54171,54174,54176,54178],{"class":64,"line":129},[62,54161,54162],{"class":72}," to learn because they’re flexible and fun. If you’re an existing Java ",[62,54164,54165],{"class":122},"Developer",[62,54167,54168],{"class":72}," (Beginner or Experienced) ",[62,54170,1295],{"class":149},[62,54172,54173],{"class":72}," course is also for you.",[62,54175,1818],{"class":68},[62,54177,22],{"class":72},[62,54179,1784],{"class":68},[62,54181,54182],{"class":64,"line":134},[62,54183,79],{"emptyLinePlaceholder":13},[62,54185,54186,54188,54190],{"class":64,"line":156},[62,54187,1905],{"class":72},[62,54189,22],{"class":1780},[62,54191,54192],{"class":72},">This course is packed with almost 14 hours of content. We are going to start off with getting your development environment up and running\n",[62,54194,54195],{"class":64,"line":161},[62,54196,54197],{"class":72}," and then go through the very basics of the language. From there we are going to build on that in each section cover topics like closures, meta-programming,\n",[62,54199,54200,54203,54205],{"class":64,"line":167},[62,54201,54202],{"class":72}," builders and so much more. I feel like this is one of the most complete courses around and I am excited for you to join me on this adventure.\u003C/",[62,54204,22],{"class":1780},[62,54206,1784],{"class":72},[62,54208,54209],{"class":64,"line":173},[62,54210,54211],{"class":1675}," '''\n",[62,54213,54214,54217,54219,54221,54223,54225,54228],{"class":64,"line":179},[62,54215,54216],{"class":1675}," cover '",[62,54218,53536],{"class":72},[62,54220,11635],{"class":68},[62,54222,52743],{"class":72},[62,54224,11635],{"class":68},[62,54226,54227],{"class":72},"cover.png",[62,54229,54230],{"class":1675},"'\n",[62,54232,54233],{"class":64,"line":185},[62,54234,223],{"class":23824},[62,54236,54237],{"class":64,"line":191},[62,54238,379],{"class":72},[22,54240,54241],{},"Finally, on the learn page, I will loop over and display my course.",[52,54243,54245],{"className":32791,"code":54244,"language":32793,"meta":57,"style":57},"hr(class: 'divider')\n\na(name: 'courses') {}\nh2 {\n i(class: 'fa fa-film') {}\n yield ' Courses'\n}\np '''\n Another great resource for learning Groovy is by watching a course. You could spend time hunting down\n various videos on the web but these courses have all the information you need packed into one place.\n '''\n\ncourses.each { Course course ->\n div(class: 'courses') {\n a(href: course.url, target: \"\\_blank\") {\n img(class: 'screenshot', src: \"img/courses/${course.cover}\")\n }\n div(class: 'metadata') {\n a(href: course.url, target: '\\_blank') {\n h1(class: 'title', course.title)\n }\n span(class: 'instructor', \"By ${course.instructor}\")\n div(class: 'description') {\n yieldUnescaped course.description\n }\n }\n }\n}\n",[59,54246,54247,54259,54263,54275,54280,54292,54300,54304,54311,54319,54324,54329,54333,54340,54351,54369,54387,54391,54403,54420,54433,54437,54454,54466,54471,54475,54479,54483],{"__ignoreMap":57},[62,54248,54249,54251,54254,54257],{"class":64,"line":65},[62,54250,8463],{"class":122},[62,54252,54253],{"class":72},"(class: ",[62,54255,54256],{"class":1675},"'divider'",[62,54258,2212],{"class":72},[62,54260,54261],{"class":64,"line":76},[62,54262,79],{"emptyLinePlaceholder":13},[62,54264,54265,54267,54270,54273],{"class":64,"line":82},[62,54266,677],{"class":122},[62,54268,54269],{"class":72},"(name: ",[62,54271,54272],{"class":1675},"'courses'",[62,54274,4360],{"class":72},[62,54276,54277],{"class":64,"line":89},[62,54278,54279],{"class":72},"h2 {\n",[62,54281,54282,54285,54287,54290],{"class":64,"line":95},[62,54283,54284],{"class":122}," i",[62,54286,54253],{"class":72},[62,54288,54289],{"class":1675},"'fa fa-film'",[62,54291,4360],{"class":72},[62,54293,54294,54297],{"class":64,"line":101},[62,54295,54296],{"class":68}," yield",[62,54298,54299],{"class":1675}," ' Courses'\n",[62,54301,54302],{"class":64,"line":107},[62,54303,379],{"class":72},[62,54305,54306,54309],{"class":64,"line":113},[62,54307,54308],{"class":72},"p ",[62,54310,54094],{"class":1675},[62,54312,54313,54316],{"class":64,"line":129},[62,54314,54315],{"class":1675}," Another great resource for learning Groovy is by watching a course. You could spend time hunting dow",[62,54317,54318],{"class":23824},"n\n",[62,54320,54321],{"class":64,"line":134},[62,54322,54323],{"class":72}," various videos on the web but these courses have all the information you need packed into one place.\n",[62,54325,54326],{"class":64,"line":156},[62,54327,54328],{"class":1675}," '''\n",[62,54330,54331],{"class":64,"line":161},[62,54332,79],{"emptyLinePlaceholder":13},[62,54334,54335,54338],{"class":64,"line":167},[62,54336,54337],{"class":1675},"courses.each { Course course -",[62,54339,1784],{"class":23824},[62,54341,54342,54345,54347,54349],{"class":64,"line":173},[62,54343,54344],{"class":122}," div",[62,54346,54253],{"class":72},[62,54348,54272],{"class":1675},[62,54350,768],{"class":72},[62,54352,54353,54356,54359,54361,54364,54367],{"class":64,"line":179},[62,54354,54355],{"class":122}," a",[62,54357,54358],{"class":72},"(href: course.url, target: ",[62,54360,4498],{"class":1675},[62,54362,54363],{"class":149},"\\_",[62,54365,54366],{"class":1675},"blank\"",[62,54368,768],{"class":72},[62,54370,54371,54374,54376,54379,54382,54385],{"class":64,"line":185},[62,54372,54373],{"class":122}," img",[62,54375,54253],{"class":72},[62,54377,54378],{"class":1675},"'screenshot'",[62,54380,54381],{"class":72},", src: ",[62,54383,54384],{"class":1675},"\"img/courses/${course.cover}\"",[62,54386,2212],{"class":72},[62,54388,54389],{"class":64,"line":191},[62,54390,533],{"class":72},[62,54392,54393,54396,54398,54401],{"class":64,"line":209},[62,54394,54395],{"class":122}," div",[62,54397,54253],{"class":72},[62,54399,54400],{"class":1675},"'metadata'",[62,54402,768],{"class":72},[62,54404,54405,54408,54410,54413,54415,54418],{"class":64,"line":220},[62,54406,54407],{"class":122}," a",[62,54409,54358],{"class":72},[62,54411,54412],{"class":1675},"'",[62,54414,54363],{"class":149},[62,54416,54417],{"class":1675},"blank'",[62,54419,768],{"class":72},[62,54421,54422,54425,54427,54430],{"class":64,"line":226},[62,54423,54424],{"class":122}," h1",[62,54426,54253],{"class":72},[62,54428,54429],{"class":1675},"'title'",[62,54431,54432],{"class":72},", course.title)\n",[62,54434,54435],{"class":64,"line":231},[62,54436,861],{"class":72},[62,54438,54439,54442,54444,54447,54449,54452],{"class":64,"line":236},[62,54440,54441],{"class":122}," span",[62,54443,54253],{"class":72},[62,54445,54446],{"class":1675},"'instructor'",[62,54448,976],{"class":72},[62,54450,54451],{"class":1675},"\"By ${course.instructor}\"",[62,54453,2212],{"class":72},[62,54455,54456,54459,54461,54464],{"class":64,"line":242},[62,54457,54458],{"class":122}," div",[62,54460,54253],{"class":72},[62,54462,54463],{"class":1675},"'description'",[62,54465,768],{"class":72},[62,54467,54468],{"class":64,"line":247},[62,54469,54470],{"class":72}," yieldUnescaped course.description\n",[62,54472,54473],{"class":64,"line":252},[62,54474,861],{"class":72},[62,54476,54477],{"class":64,"line":257},[62,54478,533],{"class":72},[62,54480,54481],{"class":64,"line":271},[62,54482,223],{"class":72},[62,54484,54485],{"class":64,"line":281},[62,54486,379],{"class":72},[26,54488,54490],{"id":54489},"generating-a-static-site","Generating a static site",[22,54492,54493],{},"Now that we made the changes to our source code we need to generate our website. The entire website is a static website that is generated from a whole bunch of groovy code. To generate the website go into the root of the project and run the following command. ",[52,54495,54497],{"className":1663,"code":54496,"language":1665,"meta":57,"style":57},"./gradlew generate\n",[59,54498,54499],{"__ignoreMap":57},[62,54500,54501,54504],{"class":64,"line":65},[62,54502,54503],{"class":122},"./gradlew",[62,54505,54506],{"class":1675}," generate\n",[22,54508,54509],{},[653,54510],{"alt":54511,"src":54512},"Static Site Generation","./2017-05-24_08-00-52-1024x551.png",[22,54514,54515],{},"If you look in the build/site directory you will see the generated website. If you go to that location in your browser you can check out the site and make sure everything looks ok.",[26,54517,54519],{"id":54518},"contributing-to-the-apache-groovy-website-screencast","Contributing to the Apache Groovy Website Screencast.",[22,54521,54522],{},"In this screencast, I am going to show you how I did that. I really enjoyed learning how the entire website was built in Groovy and then they simply generate a static site.",[22,54524,54525],{},[677,54526,54527],{"href":54527,"rel":54528},"https://www.youtube.com/watch?v=BXw-YKxr94w",[681],[26,54530,53553],{"id":54531},"the-complete-apache-groovy-developer-course",[22,54533,54534,54535,2755],{},"Are you looking to learn a new language? Learning a new language helps expand your skill set as a developer and make you more marketable. If you aren't learning a new language because it's not the one you use at work I think you're missing out on the benefits. I often found that seeing how other languages solve problems makes me a better developer with the languages I use on a day to day basis. I am here to tell you all about an awesome dynamic language called ",[677,54536,54539],{"href":54537,"rel":54538},"http://www.groovy-lang.org",[681],"Apache Groovy",[22,54541,54542],{},[653,54543],{"alt":53553,"src":54544},"./756634_ad7b_3.jpg",[22,54546,54547],{},[677,54548,53553],{"href":53551,"rel":54549},[681],[26,54551,1499],{"id":1498},[22,54553,54554],{},"I said this earlier in the article and I would like to repeat it again. You don't always have to contribute code to the core of a project to help an open source project out. I have contributed documentation, static websites, plugins and even just offering up suggestions on the mailing lists. There are tons of ways to get involved so If you really enjoy using an open source project contact them and see how you can get involved.",[22,54556,54557],{},[4534,54558,54559,54561],{},[646,54560,49733],{}," What are ways that you like to contribute to a project?",[1527,54563,54564],{},"html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .shOWo, html code.shiki .shOWo{--shiki-default:#B31D28;--shiki-default-font-style:italic;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic;--shiki-sepia:#B31D28;--shiki-sepia-font-style:italic}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}html pre.shiki code .suaqH, html code.shiki .suaqH{--shiki-default:#22863A;--shiki-dark:#85E89D;--shiki-sepia:#22863A}",{"title":57,"searchDepth":76,"depth":76,"links":54566},[54567,54568,54569,54572,54573,54574,54575],{"id":53557,"depth":76,"text":53558},{"id":53570,"depth":76,"text":53571},{"id":53594,"depth":76,"text":53595,"children":54570},[54571],{"id":53798,"depth":82,"text":53799},{"id":54489,"depth":76,"text":54490},{"id":54518,"depth":76,"text":54519},{"id":54531,"depth":76,"text":53553},{"id":1498,"depth":76,"text":1499},{"_id":54577,"path":54578,"title":54579,"description":54579,"meta":54580,"body":54585},"content/blog/2017/05/22/checking-upgrading-jhipster-version.md","/blog/2017/05/22/checking-upgrading-jhipster-version","How to check your version and upgrade JHipster",{"slug":54581,"date":54582,"published":13,"tags":54583,"author":-1,"cover":54584,"excerpt":-1},"checking-upgrading-jhipster-version","2017-05-22T09:00:26-04:00",[49752,11002],"./wallpaper-001-2560x1440-760x428.png",{"type":19,"value":54586,"toc":54690},[54587,54602,54606,54609,54622,54625,54631,54634,54650,54655,54659,54662,54668,54675,54677,54680,54687],[22,54588,54589,54590,54595,54596,54601],{},"The last few months of my life have been full of Angular + Spring Boot projects and I have been having a blast. When you start building these types of applications you need to start looking into JHipster. If you're new to JHipster I wrote up a tutorial titled ",[677,54591,54594],{"href":54592,"rel":54593},"https://danvega.dev/blog/2017/04/19/what-is-jhipster",[681],"\"What is JHipster & Why you need to start using it today\"",". In this short post, we are going to look at how to tell what version of ",[677,54597,54600],{"href":54598,"rel":54599},"https://jhipster.github.io/",[681],"JHipster"," you're running and how to tell if there are updates available. If there are updates available you will wanna upgrade to the current version. This is not a tutorial on upgrading a current application from one version to another as there is a little more work involved in that.",[26,54603,54605],{"id":54604},"upgrading-jhipster","Upgrading JHipster",[22,54607,54608],{},"To tell what version of JHipster you are running simply open up command prompt and type the following",[52,54610,54612],{"className":1663,"code":54611,"language":1665,"meta":57,"style":57},"yo jhipster\n",[59,54613,54614],{"__ignoreMap":57},[62,54615,54616,54619],{"class":64,"line":65},[62,54617,54618],{"class":122},"yo",[62,54620,54621],{"class":1675}," jhipster\n",[22,54623,54624],{},"You will see that the current version of JHipster is 4.0.2 and there is an update available for 4.5.1. The helpful command prompt also shows you how to update by giving you the command to run.",[22,54626,54627],{},[653,54628],{"alt":54629,"src":54630},"JHipster version","./2017-05-22_08-44-19-1024x623.png",[22,54632,54633],{},"To update run",[52,54635,54637],{"className":1663,"code":54636,"language":1665,"meta":57,"style":57},"npm install -g generator-jhipster\n",[59,54638,54639],{"__ignoreMap":57},[62,54640,54641,54643,54645,54647],{"class":64,"line":65},[62,54642,32645],{"class":122},[62,54644,32750],{"class":1675},[62,54646,51606],{"class":149},[62,54648,54649],{"class":1675}," generator-jhipster\n",[22,54651,54652],{},[653,54653],{"alt":54605,"src":54654},"./2017-05-22_08-52-22-1024x623.png",[26,54656,54658],{"id":54657},"upcoming-course","UPCOMING COURSE",[22,54660,54661],{},"I want all of you reading this to that I love this project so much that I have decided to launch a course on it. If you want to find out more about this please click on the link below. ",[22,54663,54664],{},[653,54665],{"alt":54666,"src":54667},"Spring Boot & Angular 2 Course","./jhipster_course.png",[22,54669,54670],{},[677,54671,54674],{"href":54672,"rel":54673},"https://danvega.dev/jhipster",[681],"Spring Boot & Angular Course",[26,54676,1499],{"id":1498},[22,54678,54679],{},"I hope this little JHipster tip was helpful but I am also curious what type of projects you are looking to build with JHipster.",[22,54681,54682],{},[4534,54683,54684,54686],{},[646,54685,49733],{}," What problems are you facing in your Angular + Spring Boot projects.",[1527,54688,54689],{},"html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}",{"title":57,"searchDepth":76,"depth":76,"links":54691},[54692,54693,54694],{"id":54604,"depth":76,"text":54605},{"id":54657,"depth":76,"text":54658},{"id":1498,"depth":76,"text":1499},{"_id":54696,"path":54697,"title":54698,"description":54698,"meta":54699,"body":54704},"content/blog/2017/05/19/spring-boot-2-first-release.md","/blog/2017/05/19/spring-boot-2-first-release","The first release of Spring Boot 2.0 is now available!",{"slug":54700,"date":54701,"published":13,"tags":54702,"author":-1,"cover":54703,"excerpt":-1},"spring-boot-2-first-release","2017-05-19T10:15:39-04:00",[11002],"./pexels-photo-92904-760x507.jpeg",{"type":19,"value":54705,"toc":55376},[54706,54715,54719,54722,54757,54760,54765,54768,54772,54775,54781,54786,54794,54803,54811,54814,54822,54836,54844,54851,54859,54866,54873,54888,54896,54906,54914,54917,54925,54928,54936,54939,54947,54950,54958,54968,54976,54995,55003,55017,55025,55034,55042,55045,55055,55062,55070,55080,55088,55103,55107,55111,55120,55128,55138,55146,55149,55170,55176,55187,55201,55214,55234,55242,55262,55270,55283,55291,55304,55319,55334,55338,55346,55352,55361,55363,55366,55373],[22,54707,54708,54709,54714],{},"I have had a lot of fun watching the Spring Boot project grow into an amazing product over the last few years. A month ago I looked at the ",[677,54710,54713],{"href":54711,"rel":54712},"https://danvega.dev/blog/2017/04/24/spring-boot-2-0-roadmap",[681],"Spring 2.0 Roadmap"," and today we get to start walking down that road. It is so exciting to see all of the new features coming in both Spring Framework 5 and Spring Boot 2.0. In this article, we are going to look at the first milestone release of Spring Boot 2.0. We will talk about how to update your current projects, start a new one and a brief look at the new features. ",[26,54716,54718],{"id":54717},"getting-started-with-spring-20","Getting Started with Spring 2.0",[22,54720,54721],{},"The first thing you need to do to get started with Spring 2.0 is to update the version number to 2.0.0.M1. ",[52,54723,54725],{"className":1769,"code":54724,"language":1771,"meta":57,"style":57},"\u003Cparent>\n \u003CgroupId>org.springframework.boot\u003C/groupId>\n \u003CartifactId>spring-boot-starter-parent\u003C/artifactId>\n \u003Cversion>2.0.0.M1\u003C/version>\n \u003CrelativePath/> \u003C!-- lookup parent from repository -->\n\u003C/parent>\n",[59,54726,54727,54732,54737,54742,54747,54752],{"__ignoreMap":57},[62,54728,54729],{"class":64,"line":65},[62,54730,54731],{},"\u003Cparent>\n",[62,54733,54734],{"class":64,"line":76},[62,54735,54736],{}," \u003CgroupId>org.springframework.boot\u003C/groupId>\n",[62,54738,54739],{"class":64,"line":82},[62,54740,54741],{}," \u003CartifactId>spring-boot-starter-parent\u003C/artifactId>\n",[62,54743,54744],{"class":64,"line":89},[62,54745,54746],{}," \u003Cversion>2.0.0.M1\u003C/version>\n",[62,54748,54749],{"class":64,"line":95},[62,54750,54751],{}," \u003CrelativePath/> \u003C!-- lookup parent from repository -->\n",[62,54753,54754],{"class":64,"line":101},[62,54755,54756],{},"\u003C/parent>\n",[22,54758,54759],{},"If you're creating a new project in IntelliJ just change the version number on the dependencies screen to 2.0.0.M1. ",[22,54761,54762],{},[653,54763],{"alt":2925,"src":54764},"./2017-05-19_08-02-11-1024x645.png",[22,54766,54767],{},"If you are upgrading from 1.5 there are a few things that you should know. ",[636,54769,54771],{"id":54770},"java-8-baseline","Java 8 baseline",[22,54773,54774],{},"Spring Boot 2.0 requires Java 8 or later. Java 6 and 7 are no longer supported.",[636,54776,54778],{"id":54777},"conditionalonbean",[59,54779,54780],{},"@ConditionalOnBean",[22,54782,54783,54785],{},[59,54784,54780],{}," now uses a logical AND rather than a logical OR when determining whether or not the condition has been met.",[636,54787,54789],{"id":54788},"remote-crash-shell",[677,54790,54793],{"href":54791,"rel":54792},"https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0-Release-Notes#remote-crash-shell",[681],"Remote CRaSH shell",[22,54795,54796,54797,54802],{},"Following its depreciation in 1.5, support for the ",[677,54798,54801],{"href":54799,"rel":54800},"http://www.crashub.org/",[681],"CRaSH project"," and remote actuator SSH support that it provided has been removed.",[636,54804,54806],{"id":54805},"spring-loaded",[677,54807,54810],{"href":54808,"rel":54809},"https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0-Release-Notes#spring-loaded",[681],"Spring Loaded",[22,54812,54813],{},"As the Spring Loaded project has been moved to the attic, its support has been removed. We advise using Devtools instead.",[636,54815,54817],{"id":54816},"dedicated-hazelcast-auto-config-for-caching",[677,54818,54821],{"href":54819,"rel":54820},"https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0-Release-Notes#dedicated-hazelcast-auto-config-for-caching",[681],"Dedicated Hazelcast auto-config for Caching",[22,54823,54824,54825,54828,54829,54831,54832,54835],{},"It is no longer possible to auto-configure both a general ",[59,54826,54827],{},"HazelcastInstance"," and a dedicated ",[59,54830,54827],{}," for caching. As a result, the ",[59,54833,54834],{},"spring.cache.hazelcast.config"," property is no longer available.",[636,54837,54839],{"id":54838},"default-connection-pool",[677,54840,54843],{"href":54841,"rel":54842},"https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0-Release-Notes#default-connection-pool",[681],"Default connection pool",[22,54845,54846,54847,54850],{},"The default connection pool has switched from Tomcat to HikariCP. If you used ",[59,54848,54849],{},"spring.datasource.type"," to force the use of Hikari in a Tomcat-based application, you can now remove that override.",[636,54852,54854],{"id":54853},"servlet-filters",[677,54855,54858],{"href":54856,"rel":54857},"https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0-Release-Notes#servlet-filters",[681],"Servlet Filters",[22,54860,54861,54862,54865],{},"The default dispatcher types for a Filter are now ",[59,54863,54864],{},"DipatcherType.REQUEST"," this aligns Spring Boot’s default with the Servlet specification’s default.",[636,54867,54869],{"id":54868},"spring-security",[677,54870,10914],{"href":54871,"rel":54872},"https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0-Release-Notes#spring-security",[681],[22,54874,54875,54876,54879,54880,54883,54884,54887],{},"Spring Security’s filter is now auto-configured with ",[59,54877,54878],{},"ASYNC"," , ",[59,54881,54882],{},"ERROR"," , and ",[59,54885,54886],{},"REQUEST"," dispatcher types. This aligns Spring Boot’s default configuration with Spring Security’s default configuration.",[636,54889,54891],{"id":54890},"spring-session",[677,54892,54895],{"href":54893,"rel":54894},"https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0-Release-Notes#spring-session",[681],"Spring Session",[22,54897,54898,54899,54879,54901,54883,54903,54905],{},"Spring Session’s filter is now auto-configured with ",[59,54900,54878],{},[59,54902,54882],{},[59,54904,54886],{}," dispatcher types. This aligns Spring Boot’s default configuration with Spring Session’s default configuration. Note that as of Spring Session 2.0, Mongo and GemFire support has been removed.",[636,54907,54909],{"id":54908},"jetty",[677,54910,54913],{"href":54911,"rel":54912},"https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0-Release-Notes#jetty",[681],"Jetty",[22,54915,54916],{},"The minimum supported version of Jetty is now 9.4",[636,54918,54920],{"id":54919},"tomcat",[677,54921,54924],{"href":54922,"rel":54923},"https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0-Release-Notes#tomcat",[681],"Tomcat",[22,54926,54927],{},"The minimum supported version of Tomcat is now 8.5",[636,54929,54931],{"id":54930},"hibernate",[677,54932,54935],{"href":54933,"rel":54934},"https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0-Release-Notes#hibernate",[681],"Hibernate",[22,54937,54938],{},"The minimum supported version of Hibernate is now 5.2",[636,54940,54942],{"id":54941},"gradle",[677,54943,54946],{"href":54944,"rel":54945},"https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0-Release-Notes#gradle",[681],"Gradle",[22,54948,54949],{},"The minimum supported version of Gradle is now 3.4",[636,54951,54953],{"id":54952},"sendgrid",[677,54954,54957],{"href":54955,"rel":54956},"https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0-Release-Notes#sendgrid",[681],"SendGrid",[22,54959,54960,54961,54964,54965,54967],{},"The minimum supported version of SendGrid’s Java client is now 3.2. In support of this upgrade, the ",[59,54962,54963],{},"username"," and ",[59,54966,15698],{}," properties have been removed as an API key is now the only supported means of authentication.",[636,54969,54971],{"id":54970},"starter-transitive-dependencies",[677,54972,54975],{"href":54973,"rel":54974},"https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0-Release-Notes#starter-transitive-dependencies",[681],"Starter transitive dependencies",[22,54977,54978,54979,54981,54982,54964,54985,54988,54989,51828,54991,54994],{},"Previously several Spring Boot starters were transitively depending on Spring MVC with ",[59,54980,47198],{}," . With the new support of Spring WebFlux, ",[59,54983,54984],{},"spring-boot-starter-mustache",[59,54986,54987],{},"spring-boot-starter-thymeleaf"," aren’t depending on on those anymore. It is the developer’s responsibility to choose and add ",[59,54990,47198],{},[59,54992,54993],{},"spring-boot-starter-webflux"," as dependencies.",[636,54996,54998],{"id":54997},"solr-health-indicator",[677,54999,55002],{"href":55000,"rel":55001},"https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0-Release-Notes#solr-health-indicator",[681],"Solr health indicator",[22,55004,55005,55006,55009,55010,55013,55014,55016],{},"The detail of the health check for Solr no longer defines a ",[59,55007,55008],{},"solrStatus"," attribute. Rather a ",[59,55011,55012],{},"status"," property is now defined and corresponds to the integer value of the standard ",[59,55015,55012],{}," property.",[636,55018,55020],{"id":55019},"default-proxying-strategy",[677,55021,55024],{"href":55022,"rel":55023},"https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0-Release-Notes#default-proxying-strategy",[681],"Default Proxying strategy",[22,55026,55027,55028,55031,55032,51832],{},"Spring Boot now uses CGLIB proxying by default, including for the AOP support. If you need proxy-based proxy, you’ll need to set the ",[59,55029,55030],{},"spring.aop.proxy-target-class"," to ",[59,55033,50950],{},[636,55035,55037],{"id":55036},"cli-based-testing",[677,55038,55041],{"href":55039,"rel":55040},"https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0-Release-Notes#cli-based-testing",[681],"CLI-based testing",[22,55043,55044],{},"Testing support has been removed from Spring Boot’s CLI in favour of moving to building the application with Maven or Gradle and using their rich testing support.",[636,55046,55048],{"id":55047},"configurationproperties",[677,55049,55052],{"href":55050,"rel":55051},"https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0-Release-Notes#configurationproperties",[681],[59,55053,55054],{},"@ConfigurationProperties",[22,55056,55057,55058,55061],{},"The ",[59,55059,55060],{},"ignoreNestedProperties"," attribute has been removed.",[636,55063,55065],{"id":55064},"multipart-configuration",[677,55066,55069],{"href":55067,"rel":55068},"https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0-Release-Notes#multipart-configuration",[681],"Multipart configuration",[22,55071,55072,55073,55076,55077,51832],{},"To better reflect their Servlet-specific nature, the multipart ",[59,55074,55075],{},"spring.http.multipart."," configuration properties have been renamed to ",[59,55078,55079],{},"spring.servlet.multipart.",[636,55081,55083],{"id":55082},"mustache-templates-default-file-extension",[677,55084,55087],{"href":55085,"rel":55086},"https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0-Release-Notes#mustache-templates-default-file-extension",[681],"Mustache templates default file extension",[22,55089,55090,55091,55094,55095,55098,55099,55102],{},"The default file extension for Mustache templates was ",[59,55092,55093],{},".html"," , it is now ",[59,55096,55097],{},".mustache"," to align with the official spec and most IDE plugins. You can override this new default by changing the ",[59,55100,55101],{},"spring.mustache.suffix"," configuration key.",[26,55104,55106],{"id":55105},"spring-20-new-features","Spring 2.0 New Features",[636,55108,55110],{"id":55109},"spring-framework-50","Spring Framework 5.0",[22,55112,55113,55114,55119],{},"Spring Boot 2.0 builds on and requires Spring Framework 5.0. There are a number of nice refinements in Spring Framework 5.0 including extensive support for building reactive applications. Please refer to the ",[677,55115,55118],{"href":55116,"rel":55117},"https://github.com/spring-projects/spring-framework/wiki/What%E2%80%99s-New-in-the-Spring-Framework#whats-new-in-spring-framework-5x",[681],"Spring Framework Wiki"," for details.",[636,55121,55123],{"id":55122},"webflux-and-webfluxfn-support",[677,55124,55127],{"href":55125,"rel":55126},"https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0-Release-Notes#webflux-and-webfluxfn-support",[681],"WebFlux and WebFlux.fn support",[22,55129,55130,55131,55133,55134,55137],{},"Spring Boot 2.0 provides a new starter for supporting the Reactive Spring web frameworks, for both annotation and functional based variants. ",[59,55132,54993],{}," brings WebFlux itself, plus Reactor Netty as a default web engine ( ",[59,55135,55136],{},"spring-boot-starter-reactor-netty"," ).",[636,55139,55141],{"id":55140},"reactive-data-support",[677,55142,55145],{"href":55143,"rel":55144},"https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0-Release-Notes#reactive-data-support",[681],"Reactive data support",[22,55147,55148],{},"Spring Boot 2.0 provides auto-configuration for the following data store with reactive support:",[915,55150,55151,55158,55164],{},[37,55152,55153,55154,55157],{},"MongoDB ( ",[59,55155,55156],{},"spring-boot-starter-mongodb-reactive"," )",[37,55159,55160,55161,55157],{},"Redis ( ",[59,55162,55163],{},"spring-boot-starter-redis-reactive",[37,55165,55166,55167,55157],{},"Cassandra ( ",[59,55168,55169],{},"spring-boot-starter-cassandra-reactive",[22,55171,55172,55175],{},[59,55173,55174],{},"@DataMongoTest"," also enables reactive repositories if necessary.",[636,55177,55179],{"id":55178},"webfluxtest-support",[677,55180,55183,55186],{"href":55181,"rel":55182},"https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0-Release-Notes#webfluxtest-support",[681],[59,55184,55185],{},"@WebFluxTest"," support",[22,55188,55189,55190,55192,55193,55196,55197,55200],{},"Reactive controllers can be tested using ",[59,55191,55185],{}," that provides a similar support than ",[59,55194,55195],{},"@WebMvcTest"," for Spring MVC. In particular a ",[59,55198,55199],{},"WebTestClient"," is auto-configured.",[636,55202,55204],{"id":55203},"webtestclient-auto-configuration-with-springboottest",[677,55205,55208,55210,55211],{"href":55206,"rel":55207},"https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0-Release-Notes#webtestclient-auto-configuration-with-springboottest",[681],[59,55209,55199],{}," auto-configuration with ",[59,55212,55213],{},"@SpringBootTest",[22,55215,55216,55217,55219,55220,51828,55223,55226,55227,55229,55230,55233],{},"When using ",[59,55218,55213],{}," with an actual server (that is, either ",[59,55221,55222],{},"DEFINED_PORT",[59,55224,55225],{},"RANDOM_PORT"," ), a ",[59,55228,55199],{}," is available the same way ",[59,55231,55232],{},"TestRestTemplate"," is.",[636,55235,55237],{"id":55236},"gradle-plugin",[677,55238,55241],{"href":55239,"rel":55240},"https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0-Release-Notes#gradle-plugin",[681],"Gradle plugin",[22,55243,55244,55245,55250,55251,19931,55256,55261],{},"Spring Boot’s Gradle plugin has been largely rewritten to enable a ",[677,55246,55249],{"href":55247,"rel":55248},"https://github.com/spring-projects/spring-boot/issues?utf8=%E2%9C%93&q=label%3A%22theme%3A%20gradle-plugin%22%20milestone%3A2.0.0.M1%20",[681],"number of significant improvements",". You can read more about the plugin’s capabilities in its ",[677,55252,55255],{"href":55253,"rel":55254},"https://docs.spring.io/spring-boot/docs/2.0.0.BUILD-SNAPSHOT/gradle-plugin/reference",[681],"reference",[677,55257,55260],{"href":55258,"rel":55259},"https://docs.spring.io/spring-boot/docs/2.0.0.BUILD-SNAPSHOT/gradle-plugin/api",[681],"api"," documentation.",[28831,55263,55265],{"id":55264},"building-executable-jars-and-wars",[677,55266,55269],{"href":55267,"rel":55268},"https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0-Release-Notes#building-executable-jars-and-wars",[681],"Building executable jars and wars",[22,55271,55057,55272,55275,55276,54964,55279,55282],{},[59,55273,55274],{},"bootRepackage"," task has been replaced with ",[59,55277,55278],{},"bootJar",[59,55280,55281],{},"bootWar"," tasks for building executable jars and wars respectively. Both tasks extend their equivalent standard Gradle jar or war task, giving you access to all of the usual configuration options and behaviour.",[28831,55284,55286],{"id":55285},"dependency-management",[677,55287,55290],{"href":55288,"rel":55289},"https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0-Release-Notes#dependency-management",[681],"Dependency management",[22,55292,55293,55294,55299,55300,55303],{},"Spring Boot’s Gradle plugin no longer automatically applies the ",[677,55295,55298],{"href":55296,"rel":55297},"https://github.com/spring-gradle-plugins/dependency-management-plugin",[681],"dependency management plugin",". Instead, Spring Boot’s plugin now reacts to the dependency management plugin being applied by importing the correct version of the ",[59,55301,55302],{},"spring-boot-dependencies"," bom. This gives you more control over how and when dependency management is configured. For most applications applying the dependency management plugin will be sufficient:",[22,55305,55306,55307,3696,55310],{},"apply ",[62,55308,3928],{"style":55309},"pl-c1",[62,55311,55313,55316,55317],{"style":55312},"pl-s",[62,55314,54412],{"style":55315},"pl-pds","io.spring.dependency-management",[62,55318,54412],{"style":55315},[22,55320,55321,55322,55325,55326,55329,55330,55333],{},"Please note that the dependency management plugin remains a transitive dependency of ",[59,55323,55324],{},"spring-boot-gradle-plugin"," so there’s no need for it to be listed as a ",[59,55327,55328],{},"classpath"," dependency in your ",[59,55331,55332],{},"buildscript"," configuration.",[26,55335,55337],{"id":55336},"spring-20-course","SPRING 2.0 COURSE",[22,55339,55340,55341,55345],{},"If you haven’t already had a chance to check out my ",[677,55342,51879],{"href":55343,"rel":55344},"http://courses.danvega.dev/p/spring-boot-intro",[681]," please do. This course is based on Spring Boot 1.3 and was very much an introduction. My announcement today is that a Spring 2.0 course is coming and I will be releasing it sometime around SpringOne. ",[22,55347,55348],{},[653,55349],{"alt":55350,"src":55351},"Spirngboot","./627032_1fbe_7.jpg",[22,55353,55354,55355,55360],{},"This is not just going to be a repeat of the previous course with a few new features. First off, I am going to create 2 free courses for setting up your development environment on both Windows 10 and Mac OS. Next, I will incorporate the feedback I have received from my current students. My current course is the best selling course on one platform and I know there is tons of room for improvement. These courses will allow me to extract that portion of the course that some find necessary and others find boring. These courses will be bundled for free into the Spring Boot 2.0 course. I am working on the curriculum now but if you would like to receive updates check out my ",[677,55356,55359],{"href":55357,"rel":55358},"https://danvega.dev/spring-boot-2-0",[681],"Spring Boot 2.0 Course Page"," and signup for updates. Anyone on this list will be the first to find out when it’s released and will receive a discount. ",[26,55362,1499],{"id":1498},[22,55364,55365],{},"This is just the start of Spring 2.0 and we should have more exciting announcements to follow.",[22,55367,55368],{},[4534,55369,55370,55372],{},[646,55371,49733],{}," What are you most looking forward to in Spring Boot 2.0?",[1527,55374,55375],{},"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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}",{"title":57,"searchDepth":76,"depth":76,"links":55377},[55378,55401,55411,55412],{"id":54717,"depth":76,"text":54718,"children":55379},[55380,55381,55382,55383,55384,55385,55386,55387,55388,55389,55390,55391,55392,55393,55394,55395,55396,55397,55398,55399,55400],{"id":54770,"depth":82,"text":54771},{"id":54777,"depth":82,"text":54780},{"id":54788,"depth":82,"text":54793},{"id":54805,"depth":82,"text":54810},{"id":54816,"depth":82,"text":54821},{"id":54838,"depth":82,"text":54843},{"id":54853,"depth":82,"text":54858},{"id":54868,"depth":82,"text":10914},{"id":54890,"depth":82,"text":54895},{"id":54908,"depth":82,"text":54913},{"id":54919,"depth":82,"text":54924},{"id":54930,"depth":82,"text":54935},{"id":54941,"depth":82,"text":54946},{"id":54952,"depth":82,"text":54957},{"id":54970,"depth":82,"text":54975},{"id":54997,"depth":82,"text":55002},{"id":55019,"depth":82,"text":55024},{"id":55036,"depth":82,"text":55041},{"id":55047,"depth":82,"text":55054},{"id":55064,"depth":82,"text":55069},{"id":55082,"depth":82,"text":55087},{"id":55105,"depth":76,"text":55106,"children":55402},[55403,55404,55405,55406,55408,55410],{"id":55109,"depth":82,"text":55110},{"id":55122,"depth":82,"text":55127},{"id":55140,"depth":82,"text":55145},{"id":55178,"depth":82,"text":55407},"@WebFluxTest support",{"id":55203,"depth":82,"text":55409},"WebTestClient auto-configuration with @SpringBootTest",{"id":55236,"depth":82,"text":55241},{"id":55336,"depth":76,"text":55337},{"id":1498,"depth":76,"text":1499},{"_id":55414,"path":55415,"title":55416,"description":55416,"meta":55417,"body":55422},"content/blog/2017/05/17/spring-component-vs-bean.md","/blog/2017/05/17/spring-component-vs-bean","Spring Beans @Component vs @Bean",{"slug":55418,"date":55419,"published":13,"tags":55420,"author":-1,"cover":55421,"excerpt":-1},"spring-component-vs-bean","2017-05-17T09:00:06-04:00",[16,2925],"./bean-vs-component.png",{"type":19,"value":55423,"toc":56202},[55424,55441,55445,55448,55451,55502,55505,55514,55518,55524,55605,55608,55616,55620,55626,55655,55663,55742,55758,55830,55833,55846,55852,55929,55933,55948,55957,56028,56040,56154,56156,56189,56191,56199],[22,55425,55426,55427,55430,55431,55434,55435,55440],{},"In this article, you will learn what a Spring Bean is and what the annotations ",[59,55428,55429],{},"@Bean"," vs ",[59,55432,55433],{},"@Component"," are used for, and how to use them. Before we dive into how each of these annotations is used it’s important to understand what a Spring Bean is. If you haven’t had a chance to check out my ",[677,55436,55439],{"href":55437,"rel":55438},"https://www.danvega.dev/blog/2023/03/09/spring-boot-crash-course/",[681],"Spring Boot Crash Course"," I cover this topic in more!",[26,55442,55444],{"id":55443},"what-is-a-spring-bean-the-spring-framework","What is a Spring Bean? The Spring Framework",[22,55446,55447],{},"The Spring Framework was built on the principles of Inversion of Control (IoC) and Dependency Injection (DI). When your code depends on another class to function, that class is considered a dependency. Instead of creating an instance of the class yourself, you delegate that responsibility to the framework, creating an inverse of responsibility. This pattern emphasizes loose coupling between components, allowing for more modular and flexible code.",[22,55449,55450],{},"A Spring Bean is an object that is managed by the Spring IoC container, along with some metadata. The data that describes the bean has the following properties: The data that describes a Spring Bean has the following properties:",[915,55452,55453,55459,55464,55470,55476,55482,55492],{},[37,55454,55455,55458],{},[646,55456,55457],{},"Class",": the class of the bean",[37,55460,55461,55463],{},[646,55462,34914],{},": the name of the bean",[37,55465,55466,55469],{},[646,55467,55468],{},"Scope",": the scope of the bean (e.g. singleton or prototype)",[37,55471,55472,55475],{},[646,55473,55474],{},"Constructor arguments",": any arguments that need to be passed to the constructor when creating the bean",[37,55477,55478,55481],{},[646,55479,55480],{},"Properties",": any properties that need to be set on the bean after it is created",[37,55483,55484,55487,55488,55491],{},[646,55485,55486],{},"Initialization method",": The ",[59,55489,55490],{},"InitializingBean","interface lets a bean perform initialization work after the container has set all necessary properties on the bean.",[37,55493,55494,55497,55498,55501],{},[646,55495,55496],{},"Destruction method",": Implementing the ",[59,55499,55500],{},"DisposableBean"," interface lets a bean get a callback when the container that contains it is destroyed.",[22,55503,55504],{},"Beans are the building blocks of a Spring application, as they provide the necessary components and services that other parts of the application can use. Examples of beans might include a database connection pool, a service layer that handles business logic, or a controller that maps incoming requests to the appropriate handler method.",[22,55506,55507,55508,55513],{},"If you would like to learn more about Spring Beans, we suggest reading the ",[677,55509,55512],{"href":55510,"rel":55511},"https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-definition",[681],"reference documentation"," after finishing this article.",[26,55515,55517],{"id":55516},"configuring-spring-beans","Configuring Spring Beans",[22,55519,55520,55521,55523],{},"Now that you understand what beans are, you need to be able to create and configure them in your applications. As we learn what we should do, it's important to start with what we shouldn't do. In the following example, the post controller depends on the post service to function. Whenever you see the ",[59,55522,2426],{}," keyword, it should raise alarm bells in your head that something may be wrong.",[52,55525,55527],{"className":54,"code":55526,"language":56,"meta":57,"style":57},"@RestController\n@RequestMapping(\"/api/posts\")\npublic class PostController {\n\n private PostService postService;\n\n public PostController() {\n this.postService = new PostService();\n }\n\n}\n",[59,55528,55529,55535,55547,55557,55561,55567,55571,55579,55593,55597,55601],{"__ignoreMap":57},[62,55530,55531,55533],{"class":64,"line":65},[62,55532,942],{"class":72},[62,55534,2342],{"class":68},[62,55536,55537,55539,55541,55543,55545],{"class":64,"line":76},[62,55538,942],{"class":72},[62,55540,10592],{"class":68},[62,55542,2109],{"class":72},[62,55544,41368],{"class":1675},[62,55546,2212],{"class":72},[62,55548,55549,55551,55553,55555],{"class":64,"line":82},[62,55550,116],{"class":68},[62,55552,119],{"class":68},[62,55554,41379],{"class":122},[62,55556,126],{"class":72},[62,55558,55559],{"class":64,"line":89},[62,55560,79],{"emptyLinePlaceholder":13},[62,55562,55563,55565],{"class":64,"line":95},[62,55564,137],{"class":68},[62,55566,41394],{"class":72},[62,55568,55569],{"class":64,"line":101},[62,55570,79],{"emptyLinePlaceholder":13},[62,55572,55573,55575,55577],{"class":64,"line":107},[62,55574,194],{"class":68},[62,55576,41379],{"class":122},[62,55578,206],{"class":72},[62,55580,55581,55583,55585,55587,55589,55591],{"class":64,"line":113},[62,55582,2405],{"class":149},[62,55584,41419],{"class":72},[62,55586,146],{"class":68},[62,55588,466],{"class":68},[62,55590,39116],{"class":122},[62,55592,822],{"class":72},[62,55594,55595],{"class":64,"line":129},[62,55596,223],{"class":72},[62,55598,55599],{"class":64,"line":134},[62,55600,79],{"emptyLinePlaceholder":13},[62,55602,55603],{"class":64,"line":156},[62,55604,379],{"class":72},[22,55606,55607],{},"While the code above may work for a simple use case, it is not a best practice and does not scale well. A big issue you will run into is when you write tests against your controller class. If you try to isolate the controller class, you will also bring along the post repository with it. If that repository is talking to a database, you will have inadvertently written a full-blown integration test instead of a unit test with a mock of the repository.",[22,55609,55610,55611,19931,55613,55615],{},"There are several ways to define and configure beans in a Spring application. One approach that was used in the past was to use XML configuration files, which specify the beans, their dependencies, and any associated properties. Another approach is to use Java configuration classes, which use annotations to define the beans and their dependencies. Finally, there are also annotations like ",[59,55612,55433],{},[59,55614,55429],{},"that can be used to define beans directly within Java code.",[636,55617,55619],{"id":55618},"component-annotation","@Component Annotation",[22,55621,55622,55623,55625],{},"The @Component annotation is a class-level annotation. If you mark a class with ",[59,55624,55433],{}," or any of the stereotype annotations (more on that below) the class will be auto-detected using classpath scanning.",[52,55627,55629],{"className":54,"code":55628,"language":56,"meta":57,"style":57},"@Component\npublic class PostService {\n\n}\n",[59,55630,55631,55637,55647,55651],{"__ignoreMap":57},[62,55632,55633,55635],{"class":64,"line":65},[62,55634,942],{"class":72},[62,55636,12585],{"class":68},[62,55638,55639,55641,55643,55645],{"class":64,"line":76},[62,55640,116],{"class":68},[62,55642,119],{"class":68},[62,55644,39116],{"class":122},[62,55646,126],{"class":72},[62,55648,55649],{"class":64,"line":82},[62,55650,79],{"emptyLinePlaceholder":13},[62,55652,55653],{"class":64,"line":89},[62,55654,379],{"class":72},[22,55656,55657,55658,2755],{},"Spring will now create an instance of the post service, which is a singleton by default, and make it available through the application context. If you need an instance of that class, you can obtain it through ",[677,55659,55662],{"href":55660,"rel":55661},"https://tanzu.vmware.com/developer/guides/dependency-injection/",[681],"dependency injection",[52,55664,55666],{"className":54,"code":55665,"language":56,"meta":57,"style":57},"@RestController\n@RequestMapping(\"/api/posts\")\npublic class PostController {\n\n private final PostService postService;\n\n public PostController(PostService postService) {\n this.postService = postService;\n }\n}\n",[59,55667,55668,55674,55686,55696,55700,55708,55712,55724,55734,55738],{"__ignoreMap":57},[62,55669,55670,55672],{"class":64,"line":65},[62,55671,942],{"class":72},[62,55673,2342],{"class":68},[62,55675,55676,55678,55680,55682,55684],{"class":64,"line":76},[62,55677,942],{"class":72},[62,55679,10592],{"class":68},[62,55681,2109],{"class":72},[62,55683,41368],{"class":1675},[62,55685,2212],{"class":72},[62,55687,55688,55690,55692,55694],{"class":64,"line":82},[62,55689,116],{"class":68},[62,55691,119],{"class":68},[62,55693,41379],{"class":122},[62,55695,126],{"class":72},[62,55697,55698],{"class":64,"line":89},[62,55699,79],{"emptyLinePlaceholder":13},[62,55701,55702,55704,55706],{"class":64,"line":95},[62,55703,137],{"class":68},[62,55705,458],{"class":68},[62,55707,41394],{"class":72},[62,55709,55710],{"class":64,"line":101},[62,55711,79],{"emptyLinePlaceholder":13},[62,55713,55714,55716,55718,55720,55722],{"class":64,"line":107},[62,55715,194],{"class":68},[62,55717,41379],{"class":122},[62,55719,41407],{"class":72},[62,55721,41410],{"class":889},[62,55723,768],{"class":72},[62,55725,55726,55728,55730,55732],{"class":64,"line":113},[62,55727,2405],{"class":149},[62,55729,41419],{"class":72},[62,55731,146],{"class":68},[62,55733,41424],{"class":72},[62,55735,55736],{"class":64,"line":129},[62,55737,223],{"class":72},[62,55739,55740],{"class":64,"line":134},[62,55741,379],{"class":72},[22,55743,55744,55745,55747,55748,55751,55752,55754,55755,55757],{},"This approach becomes particularly valuable when dealing with large dependency graphs that would otherwise be difficult to manage manually. For instance, consider the case where ",[59,55746,41346],{}," has a dependency on ",[59,55749,55750],{},"PostRepository",". With Spring, you no longer have to worry about manually managing this dependency: as Spring creates an instance of the ",[59,55753,41346],{},", it can see that it requires a ",[59,55756,55750],{},", create an instance of that class, and provide it to the constructor automatically.",[52,55759,55761],{"className":54,"code":55760,"language":56,"meta":57,"style":57},"@Component\npublic class PostService {\n\n private final PostRepository postRepository;\n\n public PostService(PostRepository postRepository) {\n this.postRepository = postRepository;\n }\n}\n",[59,55762,55763,55769,55779,55783,55792,55796,55810,55822,55826],{"__ignoreMap":57},[62,55764,55765,55767],{"class":64,"line":65},[62,55766,942],{"class":72},[62,55768,12585],{"class":68},[62,55770,55771,55773,55775,55777],{"class":64,"line":76},[62,55772,116],{"class":68},[62,55774,119],{"class":68},[62,55776,39116],{"class":122},[62,55778,126],{"class":72},[62,55780,55781],{"class":64,"line":82},[62,55782,79],{"emptyLinePlaceholder":13},[62,55784,55785,55787,55789],{"class":64,"line":89},[62,55786,137],{"class":68},[62,55788,458],{"class":68},[62,55790,55791],{"class":72}," PostRepository postRepository;\n",[62,55793,55794],{"class":64,"line":95},[62,55795,79],{"emptyLinePlaceholder":13},[62,55797,55798,55800,55802,55805,55808],{"class":64,"line":101},[62,55799,194],{"class":68},[62,55801,39116],{"class":122},[62,55803,55804],{"class":72},"(PostRepository ",[62,55806,55807],{"class":889},"postRepository",[62,55809,768],{"class":72},[62,55811,55812,55814,55817,55819],{"class":64,"line":107},[62,55813,2405],{"class":149},[62,55815,55816],{"class":72},".postRepository ",[62,55818,146],{"class":68},[62,55820,55821],{"class":72}," postRepository;\n",[62,55823,55824],{"class":64,"line":113},[62,55825,223],{"class":72},[62,55827,55828],{"class":64,"line":129},[62,55829,379],{"class":72},[22,55831,55832],{},"There are specialized stereotype annotations that you can use that at the end of the day are also marked with the @Component annotation",[915,55834,55835,55838,55840,55843],{},[37,55836,55837],{},"@Controller",[37,55839,40266],{},[37,55841,55842],{},"@Service",[37,55844,55845],{},"@Repository",[22,55847,55848,55849,55851],{},"This means that you can update your ",[59,55850,41346],{}," to use the service annotation and it will continue to work as expected.",[52,55853,55855],{"className":54,"code":55854,"language":56,"meta":57,"style":57},"import org.springframework.stereotype.Service;\n\n@Service\npublic class PostService {\n\n private final PostRepository postRepository;\n\n public PostService(PostRepository postRepository) {\n this.postRepository = postRepository;\n }\n}\n",[59,55856,55857,55863,55867,55873,55883,55887,55895,55899,55911,55921,55925],{"__ignoreMap":57},[62,55858,55859,55861],{"class":64,"line":65},[62,55860,27875],{"class":68},[62,55862,28194],{"class":72},[62,55864,55865],{"class":64,"line":76},[62,55866,79],{"emptyLinePlaceholder":13},[62,55868,55869,55871],{"class":64,"line":82},[62,55870,942],{"class":72},[62,55872,945],{"class":68},[62,55874,55875,55877,55879,55881],{"class":64,"line":89},[62,55876,116],{"class":68},[62,55878,119],{"class":68},[62,55880,39116],{"class":122},[62,55882,126],{"class":72},[62,55884,55885],{"class":64,"line":95},[62,55886,79],{"emptyLinePlaceholder":13},[62,55888,55889,55891,55893],{"class":64,"line":101},[62,55890,137],{"class":68},[62,55892,458],{"class":68},[62,55894,55791],{"class":72},[62,55896,55897],{"class":64,"line":107},[62,55898,79],{"emptyLinePlaceholder":13},[62,55900,55901,55903,55905,55907,55909],{"class":64,"line":113},[62,55902,194],{"class":68},[62,55904,39116],{"class":122},[62,55906,55804],{"class":72},[62,55908,55807],{"class":889},[62,55910,768],{"class":72},[62,55912,55913,55915,55917,55919],{"class":64,"line":129},[62,55914,2405],{"class":149},[62,55916,55816],{"class":72},[62,55918,146],{"class":68},[62,55920,55821],{"class":72},[62,55922,55923],{"class":64,"line":134},[62,55924,223],{"class":72},[62,55926,55927],{"class":64,"line":156},[62,55928,379],{"class":72},[636,55930,55932],{"id":55931},"bean-annotation","@Bean Annotation",[22,55934,55935,55936,55938,55939,55941,55942,55944,55945,55947],{},"While the ",[59,55937,55433],{}," is convenient and works it is a class-level annotation. What happens if you need to create a bean from a method call? The ",[59,55940,55429],{}," annotation is used to declare a bean in Spring. When applied to a method, this annotation specifies that the method returns a bean that should be managed by the Spring container. ",[59,55943,55429],{}," methods are usually declared within ",[59,55946,48869],{}," classes.",[22,55949,55950,55951,55953,55954,55956],{},"In the following example, you are creating an instance of a ",[59,55952,38596],{},". There are two options: create a new instance in each class where it is needed, or create a single instance of the class and ask for that instance wherever it is needed in the app. The latter option can be achieved by having Spring manage the ",[59,55955,38596],{}," instance for you.",[52,55958,55960],{"className":54,"code":55959,"language":56,"meta":57,"style":57},"@Configuration\npublic class WebConfig {\n\n @Bean\n public RestTemplate restTemplate() {\n return new RestTemplateBuilder().build();\n }\n\n}\n",[59,55961,55962,55968,55979,55983,55989,56001,56016,56020,56024],{"__ignoreMap":57},[62,55963,55964,55966],{"class":64,"line":65},[62,55965,942],{"class":72},[62,55967,11133],{"class":68},[62,55969,55970,55972,55974,55977],{"class":64,"line":76},[62,55971,116],{"class":68},[62,55973,119],{"class":68},[62,55975,55976],{"class":122}," WebConfig",[62,55978,126],{"class":72},[62,55980,55981],{"class":64,"line":82},[62,55982,79],{"emptyLinePlaceholder":13},[62,55984,55985,55987],{"class":64,"line":89},[62,55986,2143],{"class":72},[62,55988,2146],{"class":68},[62,55990,55991,55993,55996,55999],{"class":64,"line":95},[62,55992,194],{"class":68},[62,55994,55995],{"class":72}," RestTemplate ",[62,55997,55998],{"class":122},"restTemplate",[62,56000,206],{"class":72},[62,56002,56003,56005,56007,56010,56012,56014],{"class":64,"line":101},[62,56004,360],{"class":68},[62,56006,466],{"class":68},[62,56008,56009],{"class":122}," RestTemplateBuilder",[62,56011,3229],{"class":72},[62,56013,2189],{"class":122},[62,56015,822],{"class":72},[62,56017,56018],{"class":64,"line":107},[62,56019,223],{"class":72},[62,56021,56022],{"class":64,"line":113},[62,56023,79],{"emptyLinePlaceholder":13},[62,56025,56026],{"class":64,"line":129},[62,56027,379],{"class":72},[22,56029,56030,56031,56033,56034,56037,56038,2755],{},"If you forget to mark the class with ",[59,56032,48869],{}," the bean will not be added to the application context. You might also see bean definitions in the main application class. In the following example, we are creating an instance of a command line runner. This is possible because the ",[59,56035,56036],{},"@SpringBootApplication"," annotation itself is annotated with ",[59,56039,48869],{},[52,56041,56043],{"className":54,"code":56042,"language":56,"meta":57,"style":57},"@SpringBootApplication\npublic class Application {\n\n public static void main(String[] args) {\n SpringApplication.run(Application.class, args);\n }\n\n @Bean\n CommandLineRunner commandLineRunner() {\n return args -> {\n System.out.println(\"Hello 👋🏻\");\n };\n }\n\n}\n",[59,56044,56045,56051,56061,56065,56085,56093,56097,56101,56107,56115,56125,56138,56142,56146,56150],{"__ignoreMap":57},[62,56046,56047,56049],{"class":64,"line":65},[62,56048,942],{"class":72},[62,56050,2079],{"class":68},[62,56052,56053,56055,56057,56059],{"class":64,"line":76},[62,56054,116],{"class":68},[62,56056,119],{"class":68},[62,56058,2088],{"class":122},[62,56060,126],{"class":72},[62,56062,56063],{"class":64,"line":82},[62,56064,79],{"emptyLinePlaceholder":13},[62,56066,56067,56069,56071,56073,56075,56077,56079,56081,56083],{"class":64,"line":89},[62,56068,194],{"class":68},[62,56070,2101],{"class":68},[62,56072,200],{"class":68},[62,56074,2106],{"class":122},[62,56076,2109],{"class":72},[62,56078,973],{"class":68},[62,56080,2114],{"class":72},[62,56082,2117],{"class":889},[62,56084,768],{"class":72},[62,56086,56087,56089,56091],{"class":64,"line":95},[62,56088,2124],{"class":72},[62,56090,2127],{"class":122},[62,56092,2130],{"class":72},[62,56094,56095],{"class":64,"line":101},[62,56096,223],{"class":72},[62,56098,56099],{"class":64,"line":107},[62,56100,79],{"emptyLinePlaceholder":13},[62,56102,56103,56105],{"class":64,"line":113},[62,56104,2143],{"class":72},[62,56106,2146],{"class":68},[62,56108,56109,56111,56113],{"class":64,"line":129},[62,56110,2151],{"class":72},[62,56112,2154],{"class":122},[62,56114,206],{"class":72},[62,56116,56117,56119,56121,56123],{"class":64,"line":134},[62,56118,360],{"class":68},[62,56120,2169],{"class":72},[62,56122,800],{"class":68},[62,56124,126],{"class":72},[62,56126,56127,56129,56131,56133,56136],{"class":64,"line":156},[62,56128,2241],{"class":72},[62,56130,2244],{"class":122},[62,56132,2109],{"class":72},[62,56134,56135],{"class":1675},"\"Hello 👋🏻\"",[62,56137,1133],{"class":72},[62,56139,56140],{"class":64,"line":161},[62,56141,2252],{"class":72},[62,56143,56144],{"class":64,"line":167},[62,56145,223],{"class":72},[62,56147,56148],{"class":64,"line":173},[62,56149,79],{"emptyLinePlaceholder":13},[62,56151,56152],{"class":64,"line":179},[62,56153,379],{"class":72},[26,56155,4098],{"id":4097},[915,56157,56158,56163,56169,56175,56182],{},[37,56159,56160],{},[677,56161,55439],{"href":55437,"rel":56162},[681],[37,56164,56165],{},[677,56166,56168],{"href":55510,"rel":56167},[681],"Spring Beans - Reference Documentation",[37,56170,56171],{},[677,56172,56174],{"href":55660,"rel":56173},[681],"Spring Dependency Injection - Tanzu Dev Center Article",[37,56176,56177],{},[677,56178,56181],{"href":56179,"rel":56180},"https://youtu.be/TBlB2_4_Sqo",[681],"Spring Dependency Injection - YouTube Tutorial",[37,56183,56184],{},[677,56185,56188],{"href":56186,"rel":56187},"https://youtu.be/aX-bgylmprA",[681],"Spring Constructor Injection - YouTube Tutorial",[26,56190,1499],{"id":1498},[22,56192,56193,56194,19931,56196,56198],{},"I hope that cleared up some things on when to use the ",[59,56195,55433],{},[59,56197,55429],{}," annotations in Spring. We covered what a Spring Bean is, how to configure them, and why it's important to use dependency injection instead of creating instances of classes manually.",[1527,56200,56201],{},"html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .spoOn, html code.shiki .spoOn{--shiki-default:#E36209;--shiki-dark:#FFAB70;--shiki-sepia:#E36209}",{"title":57,"searchDepth":76,"depth":76,"links":56203},[56204,56205,56209,56210],{"id":55443,"depth":76,"text":55444},{"id":55516,"depth":76,"text":55517,"children":56206},[56207,56208],{"id":55618,"depth":82,"text":55619},{"id":55931,"depth":82,"text":55932},{"id":4097,"depth":76,"text":4098},{"id":1498,"depth":76,"text":1499},{"_id":56212,"path":56213,"title":56214,"description":56214,"meta":56215,"body":56220},"content/blog/2017/05/15/getting-started-spring-boot-actuator.md","/blog/2017/05/15/getting-started-spring-boot-actuator","Getting Started with the Spring Boot Actuator",{"slug":56216,"date":56217,"published":13,"tags":56218,"author":-1,"cover":56219,"excerpt":-1},"getting-started-spring-boot-actuator","2017-05-15T08:00:22-04:00",[11002],"./pexels-photo-169573-760x507.jpeg",{"type":19,"value":56221,"toc":56640},[56222,56225,56229,56232,56237,56240,56279,56284,56288,56296,56511,56514,56595,56599,56602,56607,56610,56624,56628,56631,56637],[22,56223,56224],{},"Spring Boot includes a number of additional features to help you monitor and manage your application when it’s pushed to production. You can choose to manage and monitor your application using HTTP endpoints, with JMX or even by a remote shell (SSH or Telnet). Auditing, health and metrics gathering can be automatically applied to your application. In this article, we will take a look at how to include the actuator in your next project and what endpoints are available. ",[26,56226,56228],{"id":56227},"actuator-dependencies","Actuator Dependencies",[22,56230,56231],{},"If you look all the way down the list you find the ops category and this is where our Spring Boot Actuator & Actuator Docs dependencies live. ",[22,56233,56234],{},[653,56235],{"alt":56228,"src":56236},"./2017-05-11_18-02-09-1024x645.png",[22,56238,56239],{},"If you want to add the actuator and actuator docs to an existing project simply include the following dependencies. ",[52,56241,56243],{"className":1769,"code":56242,"language":1771,"meta":57,"style":57},"\u003Cdependency>\n \u003CgroupId>org.springframework.boot\u003C/groupId>\n \u003CartifactId>spring-boot-starter-actuator\u003C/artifactId>\n\u003C/dependency>\n\u003Cdependency>\n \u003CgroupId>org.springframework.boot\u003C/groupId>\n \u003CartifactId>spring-boot-actuator-docs\u003C/artifactId>\n\u003C/dependency>\n",[59,56244,56245,56249,56253,56258,56262,56266,56270,56275],{"__ignoreMap":57},[62,56246,56247],{"class":64,"line":65},[62,56248,46425],{},[62,56250,56251],{"class":64,"line":76},[62,56252,54736],{},[62,56254,56255],{"class":64,"line":82},[62,56256,56257],{}," \u003CartifactId>spring-boot-starter-actuator\u003C/artifactId>\n",[62,56259,56260],{"class":64,"line":89},[62,56261,46445],{},[62,56263,56264],{"class":64,"line":95},[62,56265,46425],{},[62,56267,56268],{"class":64,"line":101},[62,56269,54736],{},[62,56271,56272],{"class":64,"line":107},[62,56273,56274],{}," \u003CartifactId>spring-boot-actuator-docs\u003C/artifactId>\n",[62,56276,56277],{"class":64,"line":113},[62,56278,46445],{},[22,56280,56281],{},[4534,56282,56283],{},"* Actuator HTTP endpoints are only available with a Spring MVC-based application.",[26,56285,56287],{"id":56286},"spring-boot-actuator-endpoints","Spring Boot Actuator Endpoints",[22,56289,56290,56291,56295],{},"Actuator endpoints allow you to monitor and interact with your application. Spring Boot includes a number of built-in endpoints and you can also add your own. This means that if you run the application and go to ",[677,56292,56293],{"href":56293,"rel":56294},"http://localhost:8080/health",[681]," you will get the health endpoint. Please create a simple application on your own and trying visiting some of the endpoints. ",[11922,56297,56298,56310],{},[11925,56299,56300],{},[11928,56301,56302,56304,56306],{},[11931,56303,8845],{},[11931,56305,11939],{"align":30239},[11931,56307,56309],{"align":56308},"right","Sensitive Default",[11941,56311,56312,56324,56336,56348,56360,56374,56386,56401,56413,56425,56436,56448,56460,56472,56487,56499],{},[11928,56313,56314,56319,56322],{},[11946,56315,56316],{},[59,56317,56318],{},"actuator",[11946,56320,56321],{"align":30239},"Provides a hypermedia-based “discovery page” for the other endpoints. Requires Spring HATEOAS to be on the classpath.",[11946,56323,21775],{"align":56308},[11928,56325,56326,56331,56334],{},[11946,56327,56328],{},[59,56329,56330],{},"auditevents",[11946,56332,56333],{"align":30239},"Exposes audit events information for the current application.",[11946,56335,21775],{"align":56308},[11928,56337,56338,56343,56346],{},[11946,56339,56340],{},[59,56341,56342],{},"autoconfig",[11946,56344,56345],{"align":30239},"Displays an auto-configuration report showing all auto-configuration candidates and the reason why they ‘were’ or ‘were not’ applied.",[11946,56347,21775],{"align":56308},[11928,56349,56350,56355,56358],{},[11946,56351,56352],{},[59,56353,56354],{},"beans",[11946,56356,56357],{"align":30239},"Displays a complete list of all the Spring beans in your application.",[11946,56359,21775],{"align":56308},[11928,56361,56362,56367,56372],{},[11946,56363,56364],{},[59,56365,56366],{},"configprops",[11946,56368,56369,56370,51832],{"align":30239},"Displays a collated list of all ",[59,56371,55054],{},[11946,56373,21775],{"align":56308},[11928,56375,56376,56381,56384],{},[11946,56377,56378],{},[59,56379,56380],{},"dump",[11946,56382,56383],{"align":30239},"Performs a thread dump.",[11946,56385,21775],{"align":56308},[11928,56387,56388,56393,56399],{},[11946,56389,56390],{},[59,56391,56392],{},"env",[11946,56394,56395,56396,51832],{"align":30239},"Exposes properties from Spring’s ",[59,56397,56398],{},"ConfigurableEnvironment",[11946,56400,21775],{"align":56308},[11928,56402,56403,56408,56411],{},[11946,56404,56405],{},[59,56406,56407],{},"flyway",[11946,56409,56410],{"align":30239},"Shows any Flyway database migrations that have been applied.",[11946,56412,21775],{"align":56308},[11928,56414,56415,56420,56423],{},[11946,56416,56417],{},[59,56418,56419],{},"health",[11946,56421,56422],{"align":30239},"Shows application health information (when the application is secure, a simple ‘status’ when accessed over an unauthenticated connection or full message details when authenticated).",[11946,56424,50950],{"align":56308},[11928,56426,56427,56431,56434],{},[11946,56428,56429],{},[59,56430,12688],{},[11946,56432,56433],{"align":30239},"Displays arbitrary application info.",[11946,56435,50950],{"align":56308},[11928,56437,56438,56443,56446],{},[11946,56439,56440],{},[59,56441,56442],{},"loggers",[11946,56444,56445],{"align":30239},"Shows and modifies the configuration of loggers in the application.",[11946,56447,21775],{"align":56308},[11928,56449,56450,56455,56458],{},[11946,56451,56452],{},[59,56453,56454],{},"liquibase",[11946,56456,56457],{"align":30239},"Shows any Liquibase database migrations that have been applied.",[11946,56459,21775],{"align":56308},[11928,56461,56462,56467,56470],{},[11946,56463,56464],{},[59,56465,56466],{},"metrics",[11946,56468,56469],{"align":30239},"Shows ‘metrics’ information for the current application.",[11946,56471,21775],{"align":56308},[11928,56473,56474,56479,56485],{},[11946,56475,56476],{},[59,56477,56478],{},"mappings",[11946,56480,56369,56481,56484],{"align":30239},[59,56482,56483],{},"@RequestMapping"," paths.",[11946,56486,21775],{"align":56308},[11928,56488,56489,56494,56497],{},[11946,56490,56491],{},[59,56492,56493],{},"shutdown",[11946,56495,56496],{"align":30239},"Allows the application to be gracefully shutdown (not enabled by default).",[11946,56498,21775],{"align":56308},[11928,56500,56501,56506,56509],{},[11946,56502,56503],{},[59,56504,56505],{},"trace",[11946,56507,56508],{"align":30239},"Displays trace information (by default the last 100 HTTP requests).",[11946,56510,21775],{"align":56308},[22,56512,56513],{},"If you are using Spring MVC, the following additional endpoints can also be used:",[11922,56515,56516,56526],{},[11925,56517,56518],{},[11928,56519,56520,56522,56524],{},[11931,56521,8845],{},[11931,56523,11939],{"align":30239},[11931,56525,56309],{"align":56308},[11941,56527,56528,56544,56560,56572],{},[11928,56529,56530,56535,56542],{},[11946,56531,56532],{},[59,56533,56534],{},"docs",[11946,56536,56537,56538,56541],{"align":30239},"Displays documentation, including example requests and responses, for the Actuator’s endpoints. Requires ",[59,56539,56540],{},"spring-boot-actuator-docs"," to be on the classpath.",[11946,56543,50950],{"align":56308},[11928,56545,56546,56551,56558],{},[11946,56547,56548],{},[59,56549,56550],{},"heapdump",[11946,56552,56553,56554,56557],{"align":30239},"Returns a GZip compressed ",[59,56555,56556],{},"hprof"," heap dump file.",[11946,56559,21775],{"align":56308},[11928,56561,56562,56567,56570],{},[11946,56563,56564],{},[59,56565,56566],{},"jolokia",[11946,56568,56569],{"align":30239},"Exposes JMX beans over HTTP (when Jolokia is on the classpath).",[11946,56571,21775],{"align":56308},[11928,56573,56574,56579,56593],{},[11946,56575,56576],{},[59,56577,56578],{},"logfile",[11946,56580,56581,56582,51828,56585,56588,56589,56592],{"align":30239},"Returns the contents of the logfile (if ",[59,56583,56584],{},"logging.file",[59,56586,56587],{},"logging.path"," properties have been set). Supports the use of the HTTP ",[59,56590,56591],{},"Range"," header to retrieve part of the log file’s content.",[11946,56594,21775],{"align":56308},[26,56596,56598],{"id":56597},"actuator-security","Actuator Security",[22,56600,56601],{},"If you tried to visit some of the endpoints you might have received an error that looks like this. ",[22,56603,56604],{},[653,56605],{"alt":56598,"src":56606},"./2017-05-11_18-16-40.png",[22,56608,56609],{},"This is because by default the /beans endpoint is secured. It might have sensitive information and we wouldn't want this information available to just anyone in production. If you look at the list of endpoints above you can see which ones are secured by default. If you want to disable the security of these endpoints in development you can do so by adding the following to your application.properties file.",[52,56611,56613],{"className":1663,"code":56612,"language":1665,"meta":57,"style":57},"management.security.enabled=false\n",[59,56614,56615],{"__ignoreMap":57},[62,56616,56617,56620,56622],{"class":64,"line":65},[62,56618,56619],{"class":122},"management.security.enabled",[62,56621,146],{"class":1675},[62,56623,40782],{"class":149},[26,56625,56627],{"id":56626},"spring-boot-actuator-screencast","Spring Boot Actuator Screencast",[22,56629,56630],{},"I created a short tutorial on everything we walked through in this article. ",[22,56632,56633],{},[677,56634,56635],{"href":56635,"rel":56636},"https://youtu.be/uxGzRyfcSU8",[681],[1527,56638,56639],{},"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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}",{"title":57,"searchDepth":76,"depth":76,"links":56641},[56642,56643,56644,56645],{"id":56227,"depth":76,"text":56228},{"id":56286,"depth":76,"text":56287},{"id":56597,"depth":76,"text":56598},{"id":56626,"depth":76,"text":56627},{"_id":56647,"path":56648,"title":56649,"description":56649,"meta":56650,"body":56655},"content/blog/2017/05/12/6-courses-itunes-u.md","/blog/2017/05/12/6-courses-itunes-u","6 Courses on iTunes U that can upgrade your Software Development Career",{"slug":56651,"date":56652,"published":13,"tags":56653,"author":-1,"cover":56654,"excerpt":-1},"6-courses-itunes-u","2017-05-12T08:00:52-04:00",[31398],"./2017-05-11_16-07-03-760x399.png",{"type":19,"value":56656,"toc":56743},[56657,56660,56671,56674,56682,56685,56693,56696,56704,56707,56715,56718,56726,56729,56737,56740],[22,56658,56659],{},"When I hear people talk about taking online courses I hear the usual marketplaces mentioned. While these are great places to learn I find it a little odd that iTunes U is usually a place that people forget about. If you weren't aware iTunes U is the world's largest catalog of free education content. ",[915,56661,56662,56665,56668],{},[37,56663,56664],{},"Choose from more than 1 million free lectures, videos, books, and other resources on thousands of subjects from leading educational and cultural institutions around the world.",[37,56666,56667],{},"Share your favorite courses with friends using Twitter, Facebook, Mail, and Messages.",[37,56669,56670],{},"iTunes U includes materials from Stanford, Yale, MIT, Oxford, McGill University, La Trobe University, University of Tokyo, Museo Nacional del Prado, Smithsonian Libraries, National Theatre, Library of Congress, and much more.",[22,56672,56673],{},"In this article, I am going to give you 6 courses that can help you grow as a Software Developer. While some of the courses on iTunes U are a little out of date I believe these courses can be of help to you right now.",[26,56675,56677],{"id":56676},"developer-ios-10-apps-with-swift",[677,56678,56681],{"href":56679,"rel":56680},"https://itunes.apple.com/us/course/developing-ios-10-apps-with-swift/id1198467120",[681],"Developer iOS 10 Apps with Swift",[22,56683,56684],{},"Tools and APIs required to build applications for the iPhone and iPad platforms using the iOS SDK. User interface design for mobile devices and unique user interactions using multi-touch technologies. Object-oriented design using model-view-controller paradigm, memory management, Swift programming language. Other topics include object-oriented database API, animation, mobile device power management, multi-threading, networking and performance considerations.",[26,56686,56688],{"id":56687},"machine-learning",[677,56689,56692],{"href":56690,"rel":56691},"https://itunes.apple.com/us/itunes-u/machine-learning/id384233048?mt=10",[681],"Machine Learning",[22,56694,56695],{},"This course provides a broad introduction to machine learning and statistical pattern recognition. The course also discusses recent applications of machine learning, such as to robotic control, data mining, autonomous navigation, bioinformatics, speech recognition, and text and web data processing. Topics include supervised learning (generative/discriminative learning, parametric/non-parametric learning, neural networks, support vector machines); unsupervised learning (clustering, dimensionality reduction, kernel methods); learning theory (bias/variance tradeoffs; VC theory; large margins); reinforcement learning and adaptive control.",[26,56697,56699],{"id":56698},"intro-to-algorithms",[677,56700,56703],{"href":56701,"rel":56702},"https://itunes.apple.com/us/course/introduction-to-algorithms/id495066198",[681],"Intro to Algorithms",[22,56705,56706],{},"This course teaches techniques for the design and analysis of efficient algorithms, emphasizing methods useful in practice. Topics covered include: sorting; search trees, heaps, and hashing; divide-and-conquer; dynamic programming; amortized analysis; graph algorithms; shortest paths; network flow; computational geometry; number-theoretic algorithms; polynomial and matrix calculations; caching; and parallel computing. ",[26,56708,56710],{"id":56709},"introduction-to-python",[677,56711,56714],{"href":56712,"rel":56713},"https://itunes.apple.com/us/course/introduction-to-python/id897854234",[681],"Introduction to Python",[22,56716,56717],{},"This course will show you the basics of Python and you will be able to create some basic programs using it. You will need Python, which can be downloaded from the Python website, and also easygui which can be downloaded from the easygui website!",[26,56719,56721],{"id":56720},"introduction-to-java",[677,56722,56725],{"href":56723,"rel":56724},"https://itunes.apple.com/us/course/introduction-to-java/id551000192",[681],"Introduction to Java",[22,56727,56728],{},"An introduction to the fundamental features of the Java language. Topics include object-oriented programming, generics, collections, and I/O. ",[26,56730,56732],{"id":56731},"this-is-cs50-2016",[677,56733,56736],{"href":56734,"rel":56735},"https://itunes.apple.com/us/course/this-is-cs50-2016/id1191487593",[681],"This is CS50 2016",[22,56738,56739],{},"\"Demanding, but definitely doable. Social, but educational. A focused topic, but broadly applicable skills. CS50 is the quintessential Harvard (and Yale!) course.\"",[22,56741,56742],{},"Introduction to the intellectual enterprises of computer science and the art of programming. This course teaches students how to think algorithmically and solve problems efficiently. Topics include abstraction, algorithms, data structures, encapsulation, resource management, security, software engineering, and web development. Languages include C, Python, SQL, and JavaScript plus CSS and HTML. Problem sets inspired by real-world domains of biology, cryptography, finance, forensics, and gaming. Designed for majors and non-majors alike, with or without prior programming experience.",{"title":57,"searchDepth":76,"depth":76,"links":56744},[56745,56746,56747,56748,56749,56750],{"id":56676,"depth":76,"text":56681},{"id":56687,"depth":76,"text":56692},{"id":56698,"depth":76,"text":56703},{"id":56709,"depth":76,"text":56714},{"id":56720,"depth":76,"text":56725},{"id":56731,"depth":76,"text":56736},{"_id":56752,"path":56753,"title":56754,"description":56754,"meta":56755,"body":56760},"content/blog/2017/05/10/spring-boot-moving-tomcat-jetty.md","/blog/2017/05/10/spring-boot-moving-tomcat-jetty","Moving from Tomcat to Jetty in your Spring Boot Application",{"slug":56756,"date":56757,"published":13,"tags":56758,"author":-1,"cover":56759,"excerpt":-1},"spring-boot-moving-tomcat-jetty","2017-05-10T09:00:06-04:00",[11002],"./emile-perron-190221-760x428.jpg",{"type":19,"value":56761,"toc":57128},[56762,56770,56775,56778,56782,56785,56791,56794,56917,56920,56972,56975,56997,57115,57117,57120,57125],[22,56763,56764,56765,56769],{},"I love getting questions from students because it gives me a great idea of the real world problems people are trying to solve. Today's question comes from a student in my ",[677,56766,56768],{"href":51877,"rel":56767},[681],"Spring Boot Introduction Course"," and has to do with using something other than the default. ",[29685,56771,56772],{},[22,56773,56774],{},"When I start a new Spring Boot Project and I include the web dependency I get Tomcat as the default servlet container. If I want to change that to something else like Jetty can I?",[22,56776,56777],{},"The great thing about Spring Boot is that while it comes with some sensible defaults to get you up and running it doesn't stand in your way if you need to change something. The answer to this question is Yes and in this short tutorial, we will look at how you can do this.",[26,56779,56781],{"id":56780},"replacing-tomcat-with-jetty","Replacing Tomcat with Jetty",[22,56783,56784],{},"If we create a new Spring Boot project and select web as a dependency the default servlet container is going to be Tomcat. ",[22,56786,56787],{},[653,56788],{"alt":56789,"src":56790},"Tomcat with Jetty","./2017-05-10_08-32-05-1024x645.png",[22,56792,56793],{},"The reason we know this is because if you dive into the spring-boot-starter-web dependency you will see the following dependencies declared. ",[52,56795,56797],{"className":1769,"code":56796,"language":1771,"meta":57,"style":57},"\u003Cdependencies>\n \u003Cdependency>\n \u003CgroupId>org.springframework.boot\u003C/groupId>\n \u003CartifactId>spring-boot-starter\u003C/artifactId>\n \u003C/dependency>\n \u003Cdependency>\n \u003CgroupId>org.springframework.boot\u003C/groupId>\n \u003CartifactId>spring-boot-starter-tomcat\u003C/artifactId>\n \u003C/dependency>\n \u003Cdependency>\n \u003CgroupId>org.hibernate\u003C/groupId>\n \u003CartifactId>hibernate-validator\u003C/artifactId>\n \u003C/dependency>\n \u003Cdependency>\n \u003CgroupId>com.fasterxml.jackson.core\u003C/groupId>\n \u003CartifactId>jackson-databind\u003C/artifactId>\n \u003C/dependency>\n \u003Cdependency>\n \u003CgroupId>org.springframework\u003C/groupId>\n \u003CartifactId>spring-web\u003C/artifactId>\n \u003C/dependency>\n \u003Cdependency>\n \u003CgroupId>org.springframework\u003C/groupId>\n \u003CartifactId>spring-webmvc\u003C/artifactId>\n \u003C/dependency>\n\u003C/dependencies>\n",[59,56798,56799,56804,56809,56814,56819,56824,56828,56832,56837,56841,56845,56850,56855,56859,56863,56868,56873,56877,56881,56886,56891,56895,56899,56903,56908,56912],{"__ignoreMap":57},[62,56800,56801],{"class":64,"line":65},[62,56802,56803],{},"\u003Cdependencies>\n",[62,56805,56806],{"class":64,"line":76},[62,56807,56808],{}," \u003Cdependency>\n",[62,56810,56811],{"class":64,"line":82},[62,56812,56813],{}," \u003CgroupId>org.springframework.boot\u003C/groupId>\n",[62,56815,56816],{"class":64,"line":89},[62,56817,56818],{}," \u003CartifactId>spring-boot-starter\u003C/artifactId>\n",[62,56820,56821],{"class":64,"line":95},[62,56822,56823],{}," \u003C/dependency>\n",[62,56825,56826],{"class":64,"line":101},[62,56827,56808],{},[62,56829,56830],{"class":64,"line":107},[62,56831,56813],{},[62,56833,56834],{"class":64,"line":113},[62,56835,56836],{}," \u003CartifactId>spring-boot-starter-tomcat\u003C/artifactId>\n",[62,56838,56839],{"class":64,"line":129},[62,56840,56823],{},[62,56842,56843],{"class":64,"line":134},[62,56844,56808],{},[62,56846,56847],{"class":64,"line":156},[62,56848,56849],{}," \u003CgroupId>org.hibernate\u003C/groupId>\n",[62,56851,56852],{"class":64,"line":161},[62,56853,56854],{}," \u003CartifactId>hibernate-validator\u003C/artifactId>\n",[62,56856,56857],{"class":64,"line":167},[62,56858,56823],{},[62,56860,56861],{"class":64,"line":173},[62,56862,56808],{},[62,56864,56865],{"class":64,"line":179},[62,56866,56867],{}," \u003CgroupId>com.fasterxml.jackson.core\u003C/groupId>\n",[62,56869,56870],{"class":64,"line":185},[62,56871,56872],{}," \u003CartifactId>jackson-databind\u003C/artifactId>\n",[62,56874,56875],{"class":64,"line":191},[62,56876,56823],{},[62,56878,56879],{"class":64,"line":209},[62,56880,56808],{},[62,56882,56883],{"class":64,"line":220},[62,56884,56885],{}," \u003CgroupId>org.springframework\u003C/groupId>\n",[62,56887,56888],{"class":64,"line":226},[62,56889,56890],{}," \u003CartifactId>spring-web\u003C/artifactId>\n",[62,56892,56893],{"class":64,"line":231},[62,56894,56823],{},[62,56896,56897],{"class":64,"line":236},[62,56898,56808],{},[62,56900,56901],{"class":64,"line":242},[62,56902,56885],{},[62,56904,56905],{"class":64,"line":247},[62,56906,56907],{}," \u003CartifactId>spring-webmvc\u003C/artifactId>\n",[62,56909,56910],{"class":64,"line":252},[62,56911,56823],{},[62,56913,56914],{"class":64,"line":257},[62,56915,56916],{},"\u003C/dependencies>\n",[22,56918,56919],{},"The first thing we need to do is remove the Tomcat dependency. We are going to do this in the starter dependency itself so we need some way of doing this in our Maven POM. Luckily we have a way to exclude a dependency using an exclusions block. ",[52,56921,56923],{"className":1769,"code":56922,"language":1771,"meta":57,"style":57},"\u003Cdependency>\n \u003CgroupId>org.springframework.boot\u003C/groupId>\n \u003CartifactId>spring-boot-starter-web\u003C/artifactId>\n \u003Cexclusions>\n \u003Cexclusion>\n \u003CgroupId>org.springframework.boot\u003C/groupId>\n \u003CartifactId>spring-boot-starter-tomcat\u003C/artifactId>\n \u003C/exclusion>\n \u003C/exclusions>\n\u003C/dependency>\n",[59,56924,56925,56929,56933,56938,56943,56948,56953,56958,56963,56968],{"__ignoreMap":57},[62,56926,56927],{"class":64,"line":65},[62,56928,46425],{},[62,56930,56931],{"class":64,"line":76},[62,56932,54736],{},[62,56934,56935],{"class":64,"line":82},[62,56936,56937],{}," \u003CartifactId>spring-boot-starter-web\u003C/artifactId>\n",[62,56939,56940],{"class":64,"line":89},[62,56941,56942],{}," \u003Cexclusions>\n",[62,56944,56945],{"class":64,"line":95},[62,56946,56947],{}," \u003Cexclusion>\n",[62,56949,56950],{"class":64,"line":101},[62,56951,56952],{}," \u003CgroupId>org.springframework.boot\u003C/groupId>\n",[62,56954,56955],{"class":64,"line":107},[62,56956,56957],{}," \u003CartifactId>spring-boot-starter-tomcat\u003C/artifactId>\n",[62,56959,56960],{"class":64,"line":113},[62,56961,56962],{}," \u003C/exclusion>\n",[62,56964,56965],{"class":64,"line":129},[62,56966,56967],{}," \u003C/exclusions>\n",[62,56969,56970],{"class":64,"line":134},[62,56971,46445],{},[22,56973,56974],{},"Now that we have excluded Tomcat we can add our Jetty dependency. ",[52,56976,56978],{"className":1769,"code":56977,"language":1771,"meta":57,"style":57},"\u003Cdependency>\n \u003CgroupId>org.springframework.boot\u003C/groupId>\n \u003CartifactId>spring-boot-starter-jetty\u003C/artifactId>\n\u003C/dependency>\n",[59,56979,56980,56984,56988,56993],{"__ignoreMap":57},[62,56981,56982],{"class":64,"line":65},[62,56983,46425],{},[62,56985,56986],{"class":64,"line":76},[62,56987,54736],{},[62,56989,56990],{"class":64,"line":82},[62,56991,56992],{}," \u003CartifactId>spring-boot-starter-jetty\u003C/artifactId>\n",[62,56994,56995],{"class":64,"line":89},[62,56996,46445],{},[52,56998,57000],{"className":1663,"code":56999,"language":1665,"meta":57,"style":57},"If you go ahead and run the application you will see in the console that we are indeed now running on Jetty. \n\n2017-05-10 08:42:24.880 INFO 32757 --- [ main] .s.b.c.e.j.JettyEmbeddedServletContainer : Jetty started on port(s) 8080 (http/1.1)\n",[59,57001,57002,57060,57064],{"__ignoreMap":57},[62,57003,57004,57006,57008,57011,57014,57016,57018,57020,57023,57025,57027,57029,57032,57034,57037,57039,57042,57045,57048,57051,57054,57057],{"class":64,"line":65},[62,57005,50359],{"class":122},[62,57007,50362],{"class":1675},[62,57009,57010],{"class":1675}," go",[62,57012,57013],{"class":1675}," ahead",[62,57015,50404],{"class":1675},[62,57017,1716],{"class":1675},[62,57019,53632],{"class":1675},[62,57021,57022],{"class":1675}," application",[62,57024,50362],{"class":1675},[62,57026,50380],{"class":1675},[62,57028,50383],{"class":1675},[62,57030,57031],{"class":1675}," in",[62,57033,53632],{"class":1675},[62,57035,57036],{"class":1675}," console",[62,57038,50417],{"class":1675},[62,57040,57041],{"class":1675}," we",[62,57043,57044],{"class":1675}," are",[62,57046,57047],{"class":1675}," indeed",[62,57049,57050],{"class":1675}," now",[62,57052,57053],{"class":1675}," running",[62,57055,57056],{"class":1675}," on",[62,57058,57059],{"class":1675}," Jetty. \n",[62,57061,57062],{"class":64,"line":76},[62,57063,79],{"emptyLinePlaceholder":13},[62,57065,57066,57069,57072,57074,57077,57079,57082,57085,57088,57091,57094,57097,57099,57102,57104,57107,57109,57112],{"class":64,"line":82},[62,57067,57068],{"class":122},"2017-05-10",[62,57070,57071],{"class":1675}," 08:42:24.880",[62,57073,40606],{"class":1675},[62,57075,57076],{"class":149}," 32757",[62,57078,40612],{"class":149},[62,57080,57081],{"class":72}," [ ",[62,57083,57084],{"class":1675},"main]",[62,57086,57087],{"class":1675}," .s.b.c.e.j.JettyEmbeddedServletContainer",[62,57089,57090],{"class":1675}," :",[62,57092,57093],{"class":1675}," Jetty",[62,57095,57096],{"class":1675}," started",[62,57098,57056],{"class":1675},[62,57100,57101],{"class":1675}," port",[62,57103,2109],{"class":72},[62,57105,57106],{"class":122},"s",[62,57108,5024],{"class":72},[62,57110,57111],{"class":149},"8080",[62,57113,57114],{"class":72}," (http/1.1)\n",[26,57116,1499],{"id":1498},[22,57118,57119],{},"I hope this really helps drive home a point about Spring Boot. There are conventions and defaults that help us quickly and easily stand up a new Spring Application. Just because this is the default though doesn't mean that we can change something to fit our needs. ",[22,57121,50754,57122,57124],{},[646,57123,49733],{}," What problems are you facing in your Spring Applications? _",[1527,57126,57127],{},"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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}",{"title":57,"searchDepth":76,"depth":76,"links":57129},[57130,57131],{"id":56780,"depth":76,"text":56781},{"id":1498,"depth":76,"text":1499},{"_id":57133,"path":57134,"title":57135,"description":57135,"meta":57136,"body":57141},"content/blog/2017/05/08/enable-new-youtube-dark-theme-right-now.md","/blog/2017/05/08/enable-new-youtube-dark-theme-right-now","Enable the new YouTube Dark Theme right now",{"slug":57137,"date":57138,"published":13,"tags":57139,"author":-1,"cover":57140,"excerpt":-1},"enable-new-youtube-dark-theme-right-now","2017-05-08T08:00:50-04:00",[37949],"./2017-05-04_23-05-38-760x367.png",{"type":19,"value":57142,"toc":57199},[57143,57151,57157,57160,57166,57169,57175,57178,57184,57193],[22,57144,57145,57146,57150],{},"YouTube is working on rolling out a new design for their desktop application. This new design is cleaner, easier to use and gives channels a brand new look. All that is great but what really has me excited is the all new dark theme. They are still in the process of testing this out but in this article, I will show you how you can take advantage of this today. The 1st thing you need to do is go to ",[677,57147,57148],{"href":57148,"rel":57149},"http://www.youtube.com/new",[681]," and click try it now to opt-in to the new design. ",[22,57152,57153],{},[653,57154],{"alt":57155,"src":57156},"Youtube Fresh Look","./2017-05-04_23-03-41-1024x495.png",[22,57158,57159],{},"The first thing you will notice is that the three tabs across the top are gone. We also have a cleaner tighter look along with some larger typeface across the board. ",[22,57161,57162],{},[653,57163],{"alt":57164,"src":57165},"Youtube Settings","./2017-05-04_23-04-38-1024x495.png",[22,57167,57168],{},"To enable dark mode simply click on your profile icon in the upper right hand corner and select Dark Theme: Off. ",[22,57170,57171],{},[653,57172],{"alt":57173,"src":57174},"Enable Dark Mode","./2017-05-04_23-05-10.png",[22,57176,57177],{},"Finally, click on the activate dark theme. Welcome to the party! ",[22,57179,57180],{},[653,57181],{"alt":57182,"src":57183},"Youtube Dark Mode","./2017-05-04_23-05-38-1024x495.png",[22,57185,57186,57187,57192],{},"This is what the new channel view looks like for ",[677,57188,57191],{"href":57189,"rel":57190},"http://www.youtube.com/therealdanvega",[681],"mine"," in dark mode.",[22,57194,57195],{},[653,57196],{"alt":57197,"src":57198},"Account- Youtube Dark Mode","./2017-05-05_07-40-20-1024x495.png",{"title":57,"searchDepth":76,"depth":76,"links":57200},[],{"_id":57202,"path":57203,"title":57204,"description":57204,"meta":57205,"body":57210},"content/blog/2017/05/05/make-weakness-strength.md","/blog/2017/05/05/make-weakness-strength","How to Make your weakness your strength",{"slug":57206,"date":57207,"published":13,"tags":57208,"author":-1,"cover":57209,"excerpt":-1},"make-weakness-strength","2017-05-05T08:00:15-04:00",[31398],"./lucas-rosas-98304-760x507.jpg",{"type":19,"value":57211,"toc":57611},[57212,57215,57219,57222,57226,57229,57258,57261,57265,57268,57279,57288,57292,57378,57383,57391,57395,57414,57509,57513,57519,57533,57537,57540,57542,57545,57549,57552,57558,57564,57567,57575,57580,57583,57596,57598,57601,57608],[22,57213,57214],{},"I have been writing software for a fairly long time now. I have worked on applications that range from open source projects to a suite of applications that run a $5B dollar company. If you want to stay relevant in this industry though you have always got to be learning and improving your craft. One of the best things you can do is identify an area of weakness and work on improving it. A friend of mine is preparing for an interview next week and asked for some help. I haven't been on an actual coding interview in probably 10 years so I might not be the best person to ask but, what are friends for. In this article, we are going to look at some exercises he was trying to tackle. These questions led me to realize that I have a weakness that I would like to improve on. ",[26,57216,57218],{"id":57217},"the-interview","The Interview",[22,57220,57221],{},"In preparing for his interview he broke down his preparation into 2 different categories, language material, and problem sets. ",[636,57223,57225],{"id":57224},"language-material","Language Material",[22,57227,57228],{},"My advice here is to simply break down the primary language into several categories and write a bunch of questions down. If you can write down about 5-10 questions in each of the following categories and understand who you want to answer them you will be fine. ",[915,57230,57231,57234,57237,57240,57243,57246,57249,57252,57255],{},[37,57232,57233],{},"Java Basics ",[37,57235,57236],{},"Java Classes",[37,57238,57239],{},"Object Oriented Programming (OOP)",[37,57241,57242],{},"Java Collections",[37,57244,57245],{},"Threads",[37,57247,57248],{},"Exception Handling ",[37,57250,57251],{},"Garbage Collection",[37,57253,57254],{},"Java 8",[37,57256,57257],{},"Java 9",[22,57259,57260],{},"The main point of this is to not script answers. You should know these answers and you need to answer them in your own words, not some textbook definition. I also threw in Java 9 as a category for one simple reason. If they ask you if there is anything in the language that you are looking forward to this would be an opportunity for a huge win. If this comes up you can mention 1 or 2 of the new features in Java 9 and what excites you about them. This shows that you are constantly learning and that you are passionate about the language you use. ",[636,57262,57264],{"id":57263},"coding-exercises","Coding Exercises",[22,57266,57267],{},"In the form of exercises, I thought that it would look something like this.",[915,57269,57270,57273,57276],{},[37,57271,57272],{},"Create an interface that has at least 1 default method and then implements that interface",[37,57274,57275],{},"Construct a class that shows the difference between overloading and overriding ",[37,57277,57278],{},"Construct a class to read in a directory and then filter on the filename using a lambda.",[22,57280,57281,57282,57287],{},"When I sent him a bunch of examples like this he was a little thrown off and rightfully so. Again, I haven't interviewed in 10 years so I was just throwing out things that I might use to test someone's knowledge. He was looking for more algorithm based problem sets. Interviews are apparently much more likely to throw someone up on the whiteboard and give them problem sets to solve. This accomplishes 2 things, you get an idea of how someone solves a problem and you can watch their thought process and how they handle a stressful situation. After walking through some examples on ",[677,57283,57286],{"href":57284,"rel":57285},"https://leetcode.com/problemset/algorithms/",[681],"LeetCode"," I found 3 pretty good examples to share with you. If you want to try and go through these remember that you have no IDE to help out and its just you and whiteboard talking out your solution. ",[28831,57289,57291],{"id":57290},"intersection-of-two-arrays","Intersection of Two Arrays",[52,57293,57295],{"className":1663,"code":57294,"language":1665,"meta":57,"style":57},"Given two arrays, write a function to compute their intersection. **Example:**\n\nGiven nums1 = [1, 2, 2, 1], nums2 = [2, 2], return [2].\n",[59,57296,57297,57336,57340],{"__ignoreMap":57},[62,57298,57299,57302,57305,57308,57311,57313,57315,57318,57321,57324,57327,57330,57333],{"class":64,"line":65},[62,57300,57301],{"class":122},"Given",[62,57303,57304],{"class":1675}," two",[62,57306,57307],{"class":1675}," arrays,",[62,57309,57310],{"class":1675}," write",[62,57312,28870],{"class":1675},[62,57314,21929],{"class":1675},[62,57316,57317],{"class":1675}," to",[62,57319,57320],{"class":1675}," compute",[62,57322,57323],{"class":1675}," their",[62,57325,57326],{"class":1675}," intersection.",[62,57328,57329],{"class":149}," **",[62,57331,57332],{"class":1675},"Example:",[62,57334,57335],{"class":149},"**\n",[62,57337,57338],{"class":64,"line":76},[62,57339,79],{"emptyLinePlaceholder":13},[62,57341,57342,57344,57347,57349,57352,57355,57358,57361,57364,57366,57369,57372,57375],{"class":64,"line":82},[62,57343,57301],{"class":122},[62,57345,57346],{"class":1675}," nums1",[62,57348,2556],{"class":1675},[62,57350,57351],{"class":72}," [1, ",[62,57353,57354],{"class":1675},"2,",[62,57356,57357],{"class":1675}," 2,",[62,57359,57360],{"class":1675}," 1],",[62,57362,57363],{"class":1675}," nums2",[62,57365,2556],{"class":1675},[62,57367,57368],{"class":72}," [2, ",[62,57370,57371],{"class":1675},"2],",[62,57373,57374],{"class":1675}," return",[62,57376,57377],{"class":72}," [2].\n",[22,57379,57380],{},[646,57381,57382],{},"Note:",[915,57384,57385,57388],{},[37,57386,57387],{},"Each element in the result must be unique.",[37,57389,57390],{},"The result can be in any order.",[28831,57392,57394],{"id":57393},"two-sum","Two Sum",[22,57396,57397,57398,57401,57402,57407,57408,57411,57412],{},"Given an array of integers, return ",[646,57399,57400],{},"indices"," of the two numbers such that they add up to a specific target. You may assume that each input would have ",[646,57403,57404],{},[4534,57405,57406],{},"exactly"," one solution, and you may not use the ",[4534,57409,57410],{},"same"," element twice. ",[646,57413,57332],{},[52,57415,57417],{"className":1663,"code":57416,"language":1665,"meta":57,"style":57},"Given nums = [2, 7, 11, 15], target = 9,\n\nBecause nums[**0**] + nums[**1**] = 2 + 7 = 9,\nreturn [**0**, **1**].\n",[59,57418,57419,57447,57451,57493],{"__ignoreMap":57},[62,57420,57421,57423,57426,57428,57430,57433,57436,57439,57442,57444],{"class":64,"line":65},[62,57422,57301],{"class":122},[62,57424,57425],{"class":1675}," nums",[62,57427,2556],{"class":1675},[62,57429,57368],{"class":72},[62,57431,57432],{"class":1675},"7,",[62,57434,57435],{"class":1675}," 11,",[62,57437,57438],{"class":1675}," 15],",[62,57440,57441],{"class":1675}," target",[62,57443,2556],{"class":1675},[62,57445,57446],{"class":1675}," 9,\n",[62,57448,57449],{"class":64,"line":76},[62,57450,79],{"emptyLinePlaceholder":13},[62,57452,57453,57456,57459,57462,57464,57466,57468,57470,57472,57474,57476,57478,57480,57482,57484,57486,57489,57491],{"class":64,"line":82},[62,57454,57455],{"class":122},"Because",[62,57457,57458],{"class":1675}," nums[",[62,57460,57461],{"class":149},"**",[62,57463,1130],{"class":1675},[62,57465,57461],{"class":149},[62,57467,36672],{"class":1675},[62,57469,4507],{"class":1675},[62,57471,57458],{"class":1675},[62,57473,57461],{"class":149},[62,57475,6689],{"class":1675},[62,57477,57461],{"class":149},[62,57479,36672],{"class":1675},[62,57481,2556],{"class":1675},[62,57483,26797],{"class":149},[62,57485,4507],{"class":1675},[62,57487,57488],{"class":149}," 7",[62,57490,2556],{"class":1675},[62,57492,57446],{"class":1675},[62,57494,57495,57497,57500,57502,57504,57506],{"class":64,"line":89},[62,57496,22008],{"class":68},[62,57498,57499],{"class":72}," [**0**, ",[62,57501,57461],{"class":149},[62,57503,6689],{"class":1675},[62,57505,57461],{"class":149},[62,57507,57508],{"class":1675},"].\n",[28831,57510,57512],{"id":57511},"palindrome-number","Palindrome Number",[22,57514,57515,57516],{},"Determine whether an integer is a palindrome. Do this without extra space. ",[646,57517,57518],{},"Some Hints:",[915,57520,57521,57524,57527,57530],{},[37,57522,57523],{},"Could negative integers be palindromes? (ie, -1)",[37,57525,57526],{},"If you are thinking of converting the integer to string, note the restriction of using extra space.",[37,57528,57529],{},"You could also try reversing an integer. However, if you have solved the problem \"Reverse Integer\", you know that the reversed integer might overflow. How would you handle such case?",[37,57531,57532],{},"There is a more generic way of solving this problem.",[26,57534,57536],{"id":57535},"make-your-weakness-a-strength","Make your weakness a strength",[22,57538,57539],{},"After going through some of these problem sets I realized that I have a real weakness. Now, some of this stuff I won't ever use in my day to day job. I don't need to learn how to manually sort a list of objects because these are methods are given to me in the Java API. This, on the other hand, isn't an excuse not to understand how to solve problems that I was pretty good at in college. I also realize that the ability to break down a problem and solve it is what I do on a day to day basis so getting better at that can only help me out. ",[636,57541,4098],{"id":4097},[22,57543,57544],{},"Now that I realized this is something I wanted to improve on I need get to work. I learn best when I have a variety of resources to learn from. In this case, I found a book, a website and a set of lectures that I think are really going to help out. ",[28831,57546,57548],{"id":57547},"algorithms-fourth-edition","Algorithms (Fourth Edition)",[22,57550,57551],{},"I decided to pick up Algorithms (Fourth Edition) by Robert Sedgewick. This book not only had a ton of great reviews but all of the examples in the book are in Java. With that said I am going to try and focus on solving the problem and not the language because that is the point of this. ",[22,57553,57554],{},[653,57555],{"alt":57556,"src":57557},"Alogrithms","./2017-05-04_08-36-00.png",[22,57559,57560],{},[677,57561,57562],{"href":57562,"rel":57563},"http://amzn.to/2pBWwbf",[681],[28831,57565,57286],{"id":57566},"leetcode",[22,57568,57569,57570,57574],{},"Another great resource that we mentioned earlier was ",[677,57571,57286],{"href":57572,"rel":57573},"https://leetcode.com",[681],". This website gives you some awesome problem sets to solve. You can solve the problem yourself and then look at other solutions and discussions around these problems. ",[22,57576,57577],{},[653,57578],{"alt":57286,"src":57579},"./2017-05-04_09-00-39-291x300.png",[28831,57581,53436],{"id":57582},"courses",[22,57584,57585,57589,57590,57595],{},[677,57586,56703],{"href":57587,"rel":57588},"https://www.coursera.org/specializations/algorithms?action=enroll",[681]," This course looks like it is really well designed. If I wasn't planning for a wedding, bachelor party and honeymoon I think this would be the course I would want to jump into. I won't be able to take this one but I did want to share it as a resource with you. ",[677,57591,57594],{"href":57592,"rel":57593},"https://www.udemy.com/practical-data-structures-algorithms-in-java/",[681],"Practical Data Structures & Algorithms in Java"," I decided to enroll in this course because it had a really good amount of lectures on both data structures and algorithms. The combination of my new course, book and a list of problem sets is going to help me get to where I want to be. ",[26,57597,1499],{"id":1498},[22,57599,57600],{},"There is nothing wrong with having weaknesses. We all have them and anyone who tells you they don't is probably lying. If you want to be better you need to identify your weakness and instead of hanging your head, make it your strength. I hope to have more posts and videos to follow on this so stay tuned! ",[22,57602,57603],{},[4534,57604,57605,57607],{},[646,57606,49733],{}," What are your weaknesses and what are you doing to improve them?",[1527,57609,57610],{},"html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}",{"title":57,"searchDepth":76,"depth":76,"links":57612},[57613,57617,57620],{"id":57217,"depth":76,"text":57218,"children":57614},[57615,57616],{"id":57224,"depth":82,"text":57225},{"id":57263,"depth":82,"text":57264},{"id":57535,"depth":76,"text":57536,"children":57618},[57619],{"id":4097,"depth":82,"text":4098},{"id":1498,"depth":76,"text":1499},{"_id":57622,"path":57623,"title":57624,"description":57624,"meta":57625,"body":57630},"content/blog/2017/05/03/spring-angular-applications.md","/blog/2017/05/03/spring-angular-applications","How to start writing Angular & Spring Applications",{"slug":57626,"date":57627,"published":13,"tags":57628,"author":-1,"cover":57629,"excerpt":-1},"spring-angular-applications","2017-05-03T09:00:37-04:00",[49752,11002],"./carl-heyerdahl-181868-760x507.jpg",{"type":19,"value":57631,"toc":58464},[57632,57635,57639,57648,57651,57669,57811,57814,58030,58033,58037,58065,58069,58072,58392,58395,58399,58434,58438,58445,58448,58456,58461],[22,57633,57634],{},"If your story reads anything like mine this is going to be an exciting post. I have worked with Angular JS 1.x in both projects and large scale projects at work. I honestly liked the framework but always felt that it was a bit too boilerplate and complex. Now that Angular 2, and subsequently Angular 4 has been released I have spent the last few months catching up. If you ever felt like I did towards Angular 1.x I am here to tell you that it is time to give Angular another look. I absolutely love building Angular 2 & Spring Applications and in this article, I am going to give you some resources to help kick start your journey. ",[26,57636,57638],{"id":57637},"getting-started-with-angular-and-spring-boot","Getting Started with Angular and Spring Boot",[22,57640,57641,57642,57647],{},"We are going to walk through some basics and give you lots of resources to start looking into. At the end of this article, you will find a screencast I did where I walk through a simple Tasks application written in Spring Boot & Angular. If you want to check out the source code for this application you can ",[677,57643,57646],{"href":57644,"rel":57645},"https://github.com/cfaddict/spring-angular2-tasks",[681],"grab it here",". ",[26,57649,2925],{"id":57650},"spring-boot",[22,57652,57653,57654,57657,57658,57663,57664,57668],{},"Any great modern application is going to need a proven application framework on the back end. Spring Boot is an amazing project that helps us quickly get our Spring Framework Applications up and running. In our case, we will use the ",[677,57655,24606],{"href":45295,"rel":57656},[681]," to help bootstrap our application. We can do this right in our ",[677,57659,57662],{"href":57660,"rel":57661},"https://www.jetbrains.com/idea/",[681],"favorite IDE"," or directly from ",[677,57665,57667],{"href":45295,"rel":57666},[681],"the website",". In Spring we are going to create a REST Controller that can handle a call from our Angular application. This controller will accept a GET request and return to it a list of Tasks. ",[52,57670,57672],{"className":54,"code":57671,"language":56,"meta":57,"style":57},"@RestController\n@RequestMapping(\"/api/tasks\")\npublic class TaskController {\n\n private TaskService taskService;\n\n public TaskController(TaskService taskService) {\n this.taskService = taskService;\n }\n\n @GetMapping( value = {\"\",\"/\"})\n public Iterable\u003CTask> listTasks(){\n return taskService.list();\n }\n}\n",[59,57673,57674,57680,57693,57703,57707,57714,57718,57732,57744,57748,57752,57775,57792,57803,57807],{"__ignoreMap":57},[62,57675,57676,57678],{"class":64,"line":65},[62,57677,942],{"class":72},[62,57679,2342],{"class":68},[62,57681,57682,57684,57686,57688,57691],{"class":64,"line":76},[62,57683,942],{"class":72},[62,57685,10592],{"class":68},[62,57687,2109],{"class":72},[62,57689,57690],{"class":1675},"\"/api/tasks\"",[62,57692,2212],{"class":72},[62,57694,57695,57697,57699,57701],{"class":64,"line":82},[62,57696,116],{"class":68},[62,57698,119],{"class":68},[62,57700,23494],{"class":122},[62,57702,126],{"class":72},[62,57704,57705],{"class":64,"line":89},[62,57706,79],{"emptyLinePlaceholder":13},[62,57708,57709,57711],{"class":64,"line":95},[62,57710,137],{"class":68},[62,57712,57713],{"class":72}," TaskService taskService;\n",[62,57715,57716],{"class":64,"line":101},[62,57717,79],{"emptyLinePlaceholder":13},[62,57719,57720,57722,57724,57727,57730],{"class":64,"line":107},[62,57721,194],{"class":68},[62,57723,23494],{"class":122},[62,57725,57726],{"class":72},"(TaskService ",[62,57728,57729],{"class":889},"taskService",[62,57731,768],{"class":72},[62,57733,57734,57736,57739,57741],{"class":64,"line":113},[62,57735,2405],{"class":149},[62,57737,57738],{"class":72},".taskService ",[62,57740,146],{"class":68},[62,57742,57743],{"class":72}," taskService;\n",[62,57745,57746],{"class":64,"line":129},[62,57747,223],{"class":72},[62,57749,57750],{"class":64,"line":134},[62,57751,79],{"emptyLinePlaceholder":13},[62,57753,57754,57756,57758,57760,57762,57764,57767,57769,57771,57773],{"class":64,"line":156},[62,57755,2143],{"class":72},[62,57757,2548],{"class":68},[62,57759,52630],{"class":72},[62,57761,2553],{"class":149},[62,57763,2556],{"class":68},[62,57765,57766],{"class":72}," {",[62,57768,25895],{"class":1675},[62,57770,32225],{"class":72},[62,57772,15635],{"class":1675},[62,57774,50607],{"class":72},[62,57776,57777,57779,57782,57784,57786,57789],{"class":64,"line":161},[62,57778,194],{"class":68},[62,57780,57781],{"class":72}," Iterable\u003C",[62,57783,23107],{"class":68},[62,57785,3135],{"class":72},[62,57787,57788],{"class":122},"listTasks",[62,57790,57791],{"class":72},"(){\n",[62,57793,57794,57796,57799,57801],{"class":64,"line":167},[62,57795,360],{"class":68},[62,57797,57798],{"class":72}," taskService.",[62,57800,38739],{"class":122},[62,57802,822],{"class":72},[62,57804,57805],{"class":64,"line":173},[62,57806,223],{"class":72},[62,57808,57809],{"class":64,"line":179},[62,57810,379],{"class":72},[22,57812,57813],{},"This will call a service and repository that will fetch our 4 tasks from the database. We are going to load our initial data by using a Command Line Runner. ",[52,57815,57817],{"className":54,"code":57816,"language":56,"meta":57,"style":57},"@SpringBootApplication\npublic class TasksApplication {\n\n public static void main(String\\[\\] args) {\n SpringApplication.run(TasksApplication.class, args);\n }\n\n @Bean\n CommandLineRunner runner(TaskService taskService){\n return args -> {\n taskService.save( new Task(1L,\"Create Spring Boot Application\",true));\n taskService.save( new Task(2L,\"Create Angular 2 Application\",true));\n taskService.save( new Task(3L,\"Run the demo application\",true));\n taskService.save( new Task(4L, \"Make 1 Million Dollars\", false));\n };\n }\n}\n",[59,57818,57819,57825,57836,57840,57860,57869,57873,57877,57883,57895,57905,57934,57962,57990,58018,58022,58026],{"__ignoreMap":57},[62,57820,57821,57823],{"class":64,"line":65},[62,57822,942],{"class":72},[62,57824,2079],{"class":68},[62,57826,57827,57829,57831,57834],{"class":64,"line":76},[62,57828,116],{"class":68},[62,57830,119],{"class":68},[62,57832,57833],{"class":122}," TasksApplication",[62,57835,126],{"class":72},[62,57837,57838],{"class":64,"line":82},[62,57839,79],{"emptyLinePlaceholder":13},[62,57841,57842,57844,57846,57848,57850,57852,57854,57856,57858],{"class":64,"line":89},[62,57843,194],{"class":68},[62,57845,2101],{"class":68},[62,57847,200],{"class":68},[62,57849,2106],{"class":122},[62,57851,2109],{"class":72},[62,57853,973],{"class":889},[62,57855,52569],{"class":72},[62,57857,2117],{"class":889},[62,57859,768],{"class":72},[62,57861,57862,57864,57866],{"class":64,"line":95},[62,57863,2124],{"class":72},[62,57865,2127],{"class":122},[62,57867,57868],{"class":72},"(TasksApplication.class, args);\n",[62,57870,57871],{"class":64,"line":101},[62,57872,223],{"class":72},[62,57874,57875],{"class":64,"line":107},[62,57876,79],{"emptyLinePlaceholder":13},[62,57878,57879,57881],{"class":64,"line":113},[62,57880,2143],{"class":72},[62,57882,2146],{"class":68},[62,57884,57885,57887,57889,57891,57893],{"class":64,"line":129},[62,57886,2151],{"class":72},[62,57888,52603],{"class":122},[62,57890,57726],{"class":72},[62,57892,57729],{"class":889},[62,57894,34126],{"class":72},[62,57896,57897,57899,57901,57903],{"class":64,"line":134},[62,57898,360],{"class":68},[62,57900,2169],{"class":72},[62,57902,800],{"class":68},[62,57904,126],{"class":72},[62,57906,57907,57910,57912,57914,57916,57918,57920,57923,57925,57928,57930,57932],{"class":64,"line":156},[62,57908,57909],{"class":72}," taskService.",[62,57911,22562],{"class":122},[62,57913,52630],{"class":72},[62,57915,2426],{"class":68},[62,57917,23121],{"class":122},[62,57919,2109],{"class":72},[62,57921,57922],{"class":149},"1L",[62,57924,32225],{"class":72},[62,57926,57927],{"class":1675},"\"Create Spring Boot Application\"",[62,57929,32225],{"class":72},[62,57931,21775],{"class":149},[62,57933,6979],{"class":72},[62,57935,57936,57938,57940,57942,57944,57946,57948,57951,57953,57956,57958,57960],{"class":64,"line":161},[62,57937,57909],{"class":72},[62,57939,22562],{"class":122},[62,57941,52630],{"class":72},[62,57943,2426],{"class":68},[62,57945,23121],{"class":122},[62,57947,2109],{"class":72},[62,57949,57950],{"class":149},"2L",[62,57952,32225],{"class":72},[62,57954,57955],{"class":1675},"\"Create Angular 2 Application\"",[62,57957,32225],{"class":72},[62,57959,21775],{"class":149},[62,57961,6979],{"class":72},[62,57963,57964,57966,57968,57970,57972,57974,57976,57979,57981,57984,57986,57988],{"class":64,"line":167},[62,57965,57909],{"class":72},[62,57967,22562],{"class":122},[62,57969,52630],{"class":72},[62,57971,2426],{"class":68},[62,57973,23121],{"class":122},[62,57975,2109],{"class":72},[62,57977,57978],{"class":149},"3L",[62,57980,32225],{"class":72},[62,57982,57983],{"class":1675},"\"Run the demo application\"",[62,57985,32225],{"class":72},[62,57987,21775],{"class":149},[62,57989,6979],{"class":72},[62,57991,57992,57994,57996,57998,58000,58002,58004,58007,58009,58012,58014,58016],{"class":64,"line":173},[62,57993,57909],{"class":72},[62,57995,22562],{"class":122},[62,57997,52630],{"class":72},[62,57999,2426],{"class":68},[62,58001,23121],{"class":122},[62,58003,2109],{"class":72},[62,58005,58006],{"class":149},"4L",[62,58008,976],{"class":72},[62,58010,58011],{"class":1675},"\"Make 1 Million Dollars\"",[62,58013,976],{"class":72},[62,58015,50950],{"class":149},[62,58017,6979],{"class":72},[62,58019,58020],{"class":64,"line":179},[62,58021,2252],{"class":72},[62,58023,58024],{"class":64,"line":185},[62,58025,223],{"class":72},[62,58027,58028],{"class":64,"line":191},[62,58029,379],{"class":72},[22,58031,58032],{},"There is a little more going on but that is essentially our back end service. If you want to learn more about Spring Boot please check out some of these resources. ",[636,58034,58036],{"id":58035},"spring-boot-resources","Spring Boot Resources",[915,58038,58039,58044,58051,58058],{},[37,58040,58041],{},[677,58042,56768],{"href":55343,"rel":58043},[681],[37,58045,58046],{},[677,58047,58050],{"href":58048,"rel":58049},"https://projects.spring.io/spring-boot/",[681],"Spring Boot Project Page",[37,58052,58053],{},[677,58054,58057],{"href":58055,"rel":58056},"http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/",[681],"Spring Boot Reference Guide",[37,58059,58060],{},[677,58061,58064],{"href":58062,"rel":58063},"http://docs.spring.io/spring-boot/docs/current/api/",[681],"Spring Boot API",[26,58066,58068],{"id":58067},"angular-24","Angular 2/4",[22,58070,58071],{},"The first thing you need to know is that Angular 1/2/4 JS is just going by Angular these days. So when I refer to the newest version of Angular from now on, I will just be calling it Angular. As I said before I was never a huge fan of previous versions of Angular. With the new releases, writing components in TypeScript and the Angular CLI this is really a development cycle I can get behind. The 1st thing I would do is work on getting the Angular CLI installed and your local development environment up and running. From there you can generate your first component. Everything is component based in Angular and here is my TypeScript component that I really enjoyed writing. ",[52,58073,58075],{"className":54,"code":58074,"language":56,"meta":57,"style":57},"import { Component, OnInit } from '@angular/core';\nimport {Task} from \"../task.model\";\nimport {TaskService} from \"../task.service\";\n\n@Component({\n selector: 'app-tasks-list',\n templateUrl: './tasks-list.component.html',\n styleUrls: \\['./tasks-list.component.css'\\]\n})\nexport class TasksListComponent implements OnInit {\n tasks: Task\\[\\] = \\[\\];\n constructor(private taskService: TaskService) {\n // fetch our tasks from our Spring Boot Application\n taskService.getTasks()\n .subscribe(\n (tasks: any\\[\\]) => this.tasks = tasks,\n (error) => console.log(error),\n () => console.log('Task Service completed.')\n );\n }\n\n ngOnInit() {\n }\n\n getTaskClass(task: Task){\n let completed: string = 'list-group-item list-group-item-success';\n let incomplete: string = 'list-group-item list-group-item-danger';\n return task.completed ? completed : incomplete;\n }\n}\n",[59,58076,58077,58084,58096,58108,58112,58120,58132,58144,58159,58163,58180,58195,58214,58219,58229,58237,58259,58275,58292,58296,58300,58304,58311,58315,58319,58334,58351,58367,58384,58388],{"__ignoreMap":57},[62,58078,58079,58081],{"class":64,"line":65},[62,58080,27875],{"class":68},[62,58082,58083],{"class":72}," { Component, OnInit } from '@angular/core';\n",[62,58085,58086,58088,58091,58093],{"class":64,"line":76},[62,58087,27875],{"class":68},[62,58089,58090],{"class":72}," {Task} from \".",[62,58092,2755],{"class":23824},[62,58094,58095],{"class":72},"/task.model\";\n",[62,58097,58098,58100,58103,58105],{"class":64,"line":82},[62,58099,27875],{"class":68},[62,58101,58102],{"class":72}," {TaskService} from \".",[62,58104,2755],{"class":23824},[62,58106,58107],{"class":72},"/task.service\";\n",[62,58109,58110],{"class":64,"line":89},[62,58111,79],{"emptyLinePlaceholder":13},[62,58113,58114,58116,58118],{"class":64,"line":95},[62,58115,942],{"class":72},[62,58117,50666],{"class":68},[62,58119,50544],{"class":72},[62,58121,58122,58125,58127,58130],{"class":64,"line":101},[62,58123,58124],{"class":72}," selector",[62,58126,1266],{"class":68},[62,58128,58129],{"class":1675}," 'app-tasks-list'",[62,58131,3338],{"class":72},[62,58133,58134,58137,58139,58142],{"class":64,"line":107},[62,58135,58136],{"class":72}," templateUrl",[62,58138,1266],{"class":68},[62,58140,58141],{"class":1675}," './tasks-list.component.html'",[62,58143,3338],{"class":72},[62,58145,58146,58149,58151,58154,58157],{"class":64,"line":113},[62,58147,58148],{"class":72}," styleUrls",[62,58150,1266],{"class":68},[62,58152,58153],{"class":72}," \\[",[62,58155,58156],{"class":1675},"'./tasks-list.component.css'",[62,58158,50699],{"class":72},[62,58160,58161],{"class":64,"line":129},[62,58162,50607],{"class":72},[62,58164,58165,58168,58170,58173,58175,58178],{"class":64,"line":134},[62,58166,58167],{"class":72},"export ",[62,58169,11671],{"class":68},[62,58171,58172],{"class":122}," TasksListComponent",[62,58174,13520],{"class":68},[62,58176,58177],{"class":122}," OnInit",[62,58179,126],{"class":72},[62,58181,58182,58185,58187,58190,58192],{"class":64,"line":156},[62,58183,58184],{"class":72}," tasks",[62,58186,1266],{"class":68},[62,58188,58189],{"class":72}," Task\\[\\] ",[62,58191,146],{"class":68},[62,58193,58194],{"class":72}," \\[\\];\n",[62,58196,58197,58200,58202,58204,58207,58209,58212],{"class":64,"line":161},[62,58198,58199],{"class":122}," constructor",[62,58201,2109],{"class":72},[62,58203,1359],{"class":889},[62,58205,58206],{"class":889}," taskService",[62,58208,3696],{"class":72},[62,58210,58211],{"class":889},"TaskService",[62,58213,768],{"class":72},[62,58215,58216],{"class":64,"line":167},[62,58217,58218],{"class":85}," // fetch our tasks from our Spring Boot Application\n",[62,58220,58221,58224,58227],{"class":64,"line":173},[62,58222,58223],{"class":72}," taskService.",[62,58225,58226],{"class":122},"getTasks",[62,58228,2223],{"class":72},[62,58230,58231,58233,58235],{"class":64,"line":179},[62,58232,3862],{"class":72},[62,58234,50980],{"class":122},[62,58236,3301],{"class":72},[62,58238,58239,58242,58244,58247,58249,58251,58254,58256],{"class":64,"line":185},[62,58240,58241],{"class":72}," (tasks",[62,58243,1266],{"class":68},[62,58245,58246],{"class":72}," any\\[\\]) ",[62,58248,21525],{"class":68},[62,58250,9961],{"class":149},[62,58252,58253],{"class":72},".tasks ",[62,58255,146],{"class":68},[62,58257,58258],{"class":72}," tasks,\n",[62,58260,58261,58264,58266,58269,58272],{"class":64,"line":191},[62,58262,58263],{"class":72}," (error) ",[62,58265,21525],{"class":68},[62,58267,58268],{"class":72}," console.",[62,58270,58271],{"class":122},"log",[62,58273,58274],{"class":72},"(error),\n",[62,58276,58277,58279,58281,58283,58285,58287,58290],{"class":64,"line":209},[62,58278,6238],{"class":72},[62,58280,21525],{"class":68},[62,58282,58268],{"class":72},[62,58284,58271],{"class":122},[62,58286,2109],{"class":72},[62,58288,58289],{"class":1675},"'Task Service completed.'",[62,58291,2212],{"class":72},[62,58293,58294],{"class":64,"line":220},[62,58295,11766],{"class":72},[62,58297,58298],{"class":64,"line":226},[62,58299,3731],{"class":72},[62,58301,58302],{"class":64,"line":231},[62,58303,79],{"emptyLinePlaceholder":13},[62,58305,58306,58309],{"class":64,"line":236},[62,58307,58308],{"class":122}," ngOnInit",[62,58310,206],{"class":72},[62,58312,58313],{"class":64,"line":242},[62,58314,3731],{"class":72},[62,58316,58317],{"class":64,"line":247},[62,58318,79],{"emptyLinePlaceholder":13},[62,58320,58321,58324,58326,58328,58330,58332],{"class":64,"line":252},[62,58322,58323],{"class":122}," getTaskClass",[62,58325,2109],{"class":72},[62,58327,23307],{"class":889},[62,58329,3696],{"class":72},[62,58331,23107],{"class":889},[62,58333,34126],{"class":72},[62,58335,58336,58339,58341,58344,58346,58349],{"class":64,"line":257},[62,58337,58338],{"class":72}," let completed",[62,58340,1266],{"class":68},[62,58342,58343],{"class":72}," string ",[62,58345,146],{"class":68},[62,58347,58348],{"class":1675}," 'list-group-item list-group-item-success'",[62,58350,153],{"class":72},[62,58352,58353,58356,58358,58360,58362,58365],{"class":64,"line":271},[62,58354,58355],{"class":72}," let incomplete",[62,58357,1266],{"class":68},[62,58359,58343],{"class":72},[62,58361,146],{"class":68},[62,58363,58364],{"class":1675}," 'list-group-item list-group-item-danger'",[62,58366,153],{"class":72},[62,58368,58369,58371,58374,58376,58379,58381],{"class":64,"line":281},[62,58370,2599],{"class":68},[62,58372,58373],{"class":72}," task.completed ",[62,58375,5668],{"class":68},[62,58377,58378],{"class":72}," completed ",[62,58380,1266],{"class":68},[62,58382,58383],{"class":72}," incomplete;\n",[62,58385,58386],{"class":64,"line":286},[62,58387,3731],{"class":72},[62,58389,58390],{"class":64,"line":291},[62,58391,379],{"class":72},[22,58393,58394],{},"I simply can't teach you everything you need to know about Angular in this blog post and that is the reason I being pretty vague here. I am going to leave you with some resources and my screencast below but please feel free to ask any questions you have. ",[636,58396,58398],{"id":58397},"angular-resources","Angular Resources",[915,58400,58401,58408,58413,58420,58427],{},[37,58402,58403],{},[677,58404,58407],{"href":58405,"rel":58406},"https://angularjs.org/",[681],"Angular JS",[37,58409,58410],{},[677,58411,51538],{"href":51536,"rel":58412},[681],[37,58414,58415],{},[677,58416,58419],{"href":58417,"rel":58418},"http://amzn.to/2qrYPPd",[681],"Angular 2 Development with TypeScript",[37,58421,58422],{},[677,58423,58426],{"href":58424,"rel":58425},"http://amzn.to/2qElhRq",[681],"ng-book 2: The Complete Book on Angular 2",[37,58428,58429],{},[677,58430,58433],{"href":58431,"rel":58432},"https://www.udemy.com/the-complete-guide-to-angular-2/",[681],"Angular 4: The Complete Guide (Online Course)",[26,58435,58437],{"id":58436},"spring-boot-angular-tasks-application","Spring Boot & Angular Tasks Application",[22,58439,58440],{},[677,58441,58444],{"href":58442,"rel":58443},"https://www.youtube.com/watch?v=v7X%5C_ZHdcNvc&t=25s",[681],"https://www.youtube.com/watch?v=v7X\\_ZHdcNvc&t=25s",[26,58446,58447],{"id":54657},"Upcoming Course",[22,58449,58450,58451,58455],{},"I am currently working on a course that teaches you all about Spring Boot & Angular 4 development. We will show you how to get started and what it takes to build these projects from scratch. Then we will walk through an even better way to rapidly develop these applications so you can focus on writing business logic and not boilerplate code. If you're interested in learning more please head over to the ",[677,58452,58454],{"href":54672,"rel":58453},[681],"course page"," and signup for free. ",[22,58457,50754,58458,58460],{},[646,58459,49733],{}," What are your biggest challenges in developing Spring Boot & Angular applications? _",[1527,58462,58463],{},"html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .spoOn, html code.shiki .spoOn{--shiki-default:#E36209;--shiki-dark:#FFAB70;--shiki-sepia:#E36209}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .shOWo, html code.shiki .shOWo{--shiki-default:#B31D28;--shiki-default-font-style:italic;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic;--shiki-sepia:#B31D28;--shiki-sepia-font-style:italic}html pre.shiki code .s4-Ip, html code.shiki .s4-Ip{--shiki-default:#6A737D;--shiki-dark:#6A737D;--shiki-sepia:#6A737D}",{"title":57,"searchDepth":76,"depth":76,"links":58465},[58466,58467,58470,58473,58474],{"id":57637,"depth":76,"text":57638},{"id":57650,"depth":76,"text":2925,"children":58468},[58469],{"id":58035,"depth":82,"text":58036},{"id":58067,"depth":76,"text":58068,"children":58471},[58472],{"id":58397,"depth":82,"text":58398},{"id":58436,"depth":76,"text":58437},{"id":54657,"depth":76,"text":58447},{"_id":58476,"path":58477,"title":58478,"description":58478,"meta":58479,"body":58484},"content/blog/2017/05/01/add-validation-spring-entities.md","/blog/2017/05/01/add-validation-spring-entities","How to add validation to your Spring Entities",{"slug":58480,"date":58481,"published":13,"tags":58482,"author":-1,"cover":58483,"excerpt":-1},"add-validation-spring-entities","2017-05-01T16:37:13-04:00",[11002],"./luis-llerena-14779-760x507.jpg",{"type":19,"value":58485,"toc":60021},[58486,58489,58494,58503,58672,58676,58679,58846,58849,59018,59021,59098,59107,59111,59114,59349,59352,59581,59781,59784,59946,60005,60008,60011,60018],[22,58487,58488],{},"A student had a question about validating data at the domain level and so I thought it would share it with you.",[29685,58490,58491],{},[22,58492,58493],{},"Hi Dan, First of all thanks a lot for this great course. It really helped me to get into Spring. But now I'm facing a problem and didn't find a solution yet. I want to use for my DTO classes annotations like @NotNull (javax.validation.constraints) or custom annotations. But both don't work within spring boot. Do you know a good way or practice to solve this? Or is it too expensive to make these annotations work? If yes, is there a spring alternative for such annotations that execute a custom validation like a license-plate for instance? I hope this question isn't too off-topic to this course and perhaps also interesting for another member of this course. Best wishes, Daniel ",[22,58495,58496,58497,58502],{},"Before we get started I just want to thank Daniel for the question. If you want to follow along with this project you can ",[677,58498,58501],{"href":58499,"rel":58500},"https://github.com/cfaddict/spring-boot-validation-demo",[681],"grab the source code here",". We are going to start a new project and select the Web, JPA & H2 dependencies. ",[52,58504,58506],{"className":1769,"code":58505,"language":1771,"meta":57,"style":57},"\u003Cdependencies>\n \u003Cdependency>\n \u003CgroupId>org.springframework.boot\u003C/groupId>\n \u003CartifactId>spring-boot-starter-actuator\u003C/artifactId>\n \u003C/dependency>\n \u003Cdependency>\n \u003CgroupId>org.springframework.boot\u003C/groupId>\n \u003CartifactId>spring-boot-actuator-docs\u003C/artifactId>\n \u003C/dependency>\n \u003Cdependency>\n \u003CgroupId>org.springframework.boot\u003C/groupId>\n \u003CartifactId>spring-boot-starter-data-jpa\u003C/artifactId>\n \u003C/dependency>\n \u003Cdependency>\n \u003CgroupId>org.springframework.boot\u003C/groupId>\n \u003CartifactId>spring-boot-starter-web\u003C/artifactId>\n \u003C/dependency>\n\n \u003Cdependency>\n \u003CgroupId>org.springframework.boot\u003C/groupId>\n \u003CartifactId>spring-boot-devtools\u003C/artifactId>\n \u003Cscope>runtime\u003C/scope>\n \u003C/dependency>\n \u003Cdependency>\n \u003CgroupId>org.projectlombok\u003C/groupId>\n \u003CartifactId>lombok\u003C/artifactId>\n \u003C/dependency>\n \u003Cdependency>\n \u003CgroupId>com.h2database\u003C/groupId>\n \u003CartifactId>h2\u003C/artifactId>\n \u003Cscope>runtime\u003C/scope>\n \u003C/dependency>\n \u003Cdependency>\n \u003CgroupId>org.springframework.boot\u003C/groupId>\n \u003CartifactId>spring-boot-starter-test\u003C/artifactId>\n \u003Cscope>test\u003C/scope>\n \u003C/dependency>\n\u003C/dependencies>\n",[59,58507,58508,58512,58516,58520,58525,58529,58533,58537,58542,58546,58550,58554,58559,58563,58567,58571,58576,58580,58584,58588,58592,58597,58602,58606,58610,58615,58620,58624,58628,58633,58638,58642,58646,58650,58654,58659,58664,58668],{"__ignoreMap":57},[62,58509,58510],{"class":64,"line":65},[62,58511,56803],{},[62,58513,58514],{"class":64,"line":76},[62,58515,56808],{},[62,58517,58518],{"class":64,"line":82},[62,58519,56813],{},[62,58521,58522],{"class":64,"line":89},[62,58523,58524],{}," \u003CartifactId>spring-boot-starter-actuator\u003C/artifactId>\n",[62,58526,58527],{"class":64,"line":95},[62,58528,56823],{},[62,58530,58531],{"class":64,"line":101},[62,58532,56808],{},[62,58534,58535],{"class":64,"line":107},[62,58536,56813],{},[62,58538,58539],{"class":64,"line":113},[62,58540,58541],{}," \u003CartifactId>spring-boot-actuator-docs\u003C/artifactId>\n",[62,58543,58544],{"class":64,"line":129},[62,58545,56823],{},[62,58547,58548],{"class":64,"line":134},[62,58549,56808],{},[62,58551,58552],{"class":64,"line":156},[62,58553,56813],{},[62,58555,58556],{"class":64,"line":161},[62,58557,58558],{}," \u003CartifactId>spring-boot-starter-data-jpa\u003C/artifactId>\n",[62,58560,58561],{"class":64,"line":167},[62,58562,56823],{},[62,58564,58565],{"class":64,"line":173},[62,58566,56808],{},[62,58568,58569],{"class":64,"line":179},[62,58570,56813],{},[62,58572,58573],{"class":64,"line":185},[62,58574,58575],{}," \u003CartifactId>spring-boot-starter-web\u003C/artifactId>\n",[62,58577,58578],{"class":64,"line":191},[62,58579,56823],{},[62,58581,58582],{"class":64,"line":209},[62,58583,79],{"emptyLinePlaceholder":13},[62,58585,58586],{"class":64,"line":220},[62,58587,56808],{},[62,58589,58590],{"class":64,"line":226},[62,58591,56813],{},[62,58593,58594],{"class":64,"line":231},[62,58595,58596],{}," \u003CartifactId>spring-boot-devtools\u003C/artifactId>\n",[62,58598,58599],{"class":64,"line":236},[62,58600,58601],{}," \u003Cscope>runtime\u003C/scope>\n",[62,58603,58604],{"class":64,"line":242},[62,58605,56823],{},[62,58607,58608],{"class":64,"line":247},[62,58609,56808],{},[62,58611,58612],{"class":64,"line":252},[62,58613,58614],{}," \u003CgroupId>org.projectlombok\u003C/groupId>\n",[62,58616,58617],{"class":64,"line":257},[62,58618,58619],{}," \u003CartifactId>lombok\u003C/artifactId>\n",[62,58621,58622],{"class":64,"line":271},[62,58623,56823],{},[62,58625,58626],{"class":64,"line":281},[62,58627,56808],{},[62,58629,58630],{"class":64,"line":286},[62,58631,58632],{}," \u003CgroupId>com.h2database\u003C/groupId>\n",[62,58634,58635],{"class":64,"line":291},[62,58636,58637],{}," \u003CartifactId>h2\u003C/artifactId>\n",[62,58639,58640],{"class":64,"line":296},[62,58641,58601],{},[62,58643,58644],{"class":64,"line":302},[62,58645,56823],{},[62,58647,58648],{"class":64,"line":308},[62,58649,56808],{},[62,58651,58652],{"class":64,"line":314},[62,58653,56813],{},[62,58655,58656],{"class":64,"line":320},[62,58657,58658],{}," \u003CartifactId>spring-boot-starter-test\u003C/artifactId>\n",[62,58660,58661],{"class":64,"line":326},[62,58662,58663],{}," \u003Cscope>test\u003C/scope>\n",[62,58665,58666],{"class":64,"line":338},[62,58667,56823],{},[62,58669,58670],{"class":64,"line":343},[62,58671,56916],{},[26,58673,58675],{"id":58674},"validation","Validation",[22,58677,58678],{},"I am gong to create an entity called the city and the key here is to look at the state property. We are using an annotation on the state @NotNull. This says that we create a new city and try to save it that the state can't be null. ",[52,58680,58682],{"className":54,"code":58681,"language":56,"meta":57,"style":57},"package com.therealdanvega;\n\nimport lombok.Data;\n\nimport javax.persistence.Entity;\nimport javax.persistence.GeneratedValue;\nimport javax.persistence.Id;\nimport javax.validation.constraints.NotNull;\n\n@Entity\n@Data\npublic class City {\n\n @Id @GeneratedValue\n private Long id;\n public String name;\n @NotNull\n public String state;\n\n private City() {}\n\n public City(String name){\n this.name = name;\n }\n}\n",[59,58683,58684,58690,58694,58701,58705,58711,58717,58723,58730,58734,58740,58746,58757,58761,58771,58777,58783,58790,58797,58801,58810,58814,58826,58838,58842],{"__ignoreMap":57},[62,58685,58686,58688],{"class":64,"line":65},[62,58687,69],{"class":68},[62,58689,52481],{"class":72},[62,58691,58692],{"class":64,"line":76},[62,58693,79],{"emptyLinePlaceholder":13},[62,58695,58696,58698],{"class":64,"line":82},[62,58697,27875],{"class":68},[62,58699,58700],{"class":72}," lombok.Data;\n",[62,58702,58703],{"class":64,"line":89},[62,58704,79],{"emptyLinePlaceholder":13},[62,58706,58707,58709],{"class":64,"line":95},[62,58708,27875],{"class":68},[62,58710,51959],{"class":72},[62,58712,58713,58715],{"class":64,"line":101},[62,58714,27875],{"class":68},[62,58716,51966],{"class":72},[62,58718,58719,58721],{"class":64,"line":107},[62,58720,27875],{"class":68},[62,58722,51973],{"class":72},[62,58724,58725,58727],{"class":64,"line":113},[62,58726,27875],{"class":68},[62,58728,58729],{"class":72}," javax.validation.constraints.NotNull;\n",[62,58731,58732],{"class":64,"line":129},[62,58733,79],{"emptyLinePlaceholder":13},[62,58735,58736,58738],{"class":64,"line":134},[62,58737,942],{"class":72},[62,58739,8999],{"class":68},[62,58741,58742,58744],{"class":64,"line":156},[62,58743,942],{"class":72},[62,58745,9388],{"class":68},[62,58747,58748,58750,58752,58755],{"class":64,"line":161},[62,58749,116],{"class":68},[62,58751,119],{"class":68},[62,58753,58754],{"class":122}," City",[62,58756,126],{"class":72},[62,58758,58759],{"class":64,"line":167},[62,58760,79],{"emptyLinePlaceholder":13},[62,58762,58763,58765,58767,58769],{"class":64,"line":173},[62,58764,2143],{"class":72},[62,58766,9016],{"class":68},[62,58768,9019],{"class":72},[62,58770,9022],{"class":68},[62,58772,58773,58775],{"class":64,"line":179},[62,58774,137],{"class":68},[62,58776,9029],{"class":72},[62,58778,58779,58781],{"class":64,"line":185},[62,58780,194],{"class":68},[62,58782,9406],{"class":72},[62,58784,58785,58787],{"class":64,"line":191},[62,58786,2143],{"class":72},[62,58788,58789],{"class":68},"NotNull\n",[62,58791,58792,58794],{"class":64,"line":209},[62,58793,194],{"class":68},[62,58795,58796],{"class":72}," String state;\n",[62,58798,58799],{"class":64,"line":220},[62,58800,79],{"emptyLinePlaceholder":13},[62,58802,58803,58805,58807],{"class":64,"line":226},[62,58804,137],{"class":68},[62,58806,58754],{"class":122},[62,58808,58809],{"class":72},"() {}\n",[62,58811,58812],{"class":64,"line":231},[62,58813,79],{"emptyLinePlaceholder":13},[62,58815,58816,58818,58820,58822,58824],{"class":64,"line":236},[62,58817,194],{"class":68},[62,58819,58754],{"class":122},[62,58821,1049],{"class":72},[62,58823,3107],{"class":889},[62,58825,34126],{"class":72},[62,58827,58828,58830,58833,58835],{"class":64,"line":242},[62,58829,2405],{"class":149},[62,58831,58832],{"class":72},".name ",[62,58834,146],{"class":68},[62,58836,58837],{"class":72}," name;\n",[62,58839,58840],{"class":64,"line":247},[62,58841,223],{"class":72},[62,58843,58844],{"class":64,"line":252},[62,58845,379],{"class":72},[22,58847,58848],{},"I then create a Command Line Runner to insert a new record. I am intentionally not adding the state to this object. ",[52,58850,58852],{"className":54,"code":58851,"language":56,"meta":57,"style":57},"package com.therealdanvega;\n\nimport org.springframework.boot.CommandLineRunner;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.context.annotation.Bean;\n\n@SpringBootApplication\npublic class ValidationApplication {\n\n public static void main(String[] args) {\n SpringApplication.run(ValidationApplication.class, args);\n }\n\n @Bean\n CommandLineRunner runner(CityRepository cityRepository){\n return args -> {\n cityRepository.save( new City(\"Cleveland\") );\n };\n }\n}\n",[59,58853,58854,58860,58864,58870,58876,58882,58888,58892,58898,58909,58913,58933,58943,58947,58951,58958,58973,58984,59005,59010,59014],{"__ignoreMap":57},[62,58855,58856,58858],{"class":64,"line":65},[62,58857,69],{"class":68},[62,58859,52481],{"class":72},[62,58861,58862],{"class":64,"line":76},[62,58863,79],{"emptyLinePlaceholder":13},[62,58865,58866,58868],{"class":64,"line":82},[62,58867,27875],{"class":68},[62,58869,52506],{"class":72},[62,58871,58872,58874],{"class":64,"line":89},[62,58873,27875],{"class":68},[62,58875,52513],{"class":72},[62,58877,58878,58880],{"class":64,"line":95},[62,58879,27875],{"class":68},[62,58881,52520],{"class":72},[62,58883,58884,58886],{"class":64,"line":101},[62,58885,27875],{"class":68},[62,58887,52527],{"class":72},[62,58889,58890],{"class":64,"line":107},[62,58891,79],{"emptyLinePlaceholder":13},[62,58893,58894,58896],{"class":64,"line":113},[62,58895,942],{"class":72},[62,58897,2079],{"class":68},[62,58899,58900,58902,58904,58907],{"class":64,"line":129},[62,58901,116],{"class":68},[62,58903,119],{"class":68},[62,58905,58906],{"class":122}," ValidationApplication",[62,58908,126],{"class":72},[62,58910,58911],{"class":64,"line":134},[62,58912,79],{"emptyLinePlaceholder":13},[62,58914,58915,58917,58919,58921,58923,58925,58927,58929,58931],{"class":64,"line":156},[62,58916,28858],{"class":68},[62,58918,2101],{"class":68},[62,58920,200],{"class":68},[62,58922,2106],{"class":122},[62,58924,2109],{"class":72},[62,58926,973],{"class":68},[62,58928,2114],{"class":72},[62,58930,2117],{"class":889},[62,58932,768],{"class":72},[62,58934,58935,58938,58940],{"class":64,"line":161},[62,58936,58937],{"class":72}," SpringApplication.",[62,58939,2127],{"class":122},[62,58941,58942],{"class":72},"(ValidationApplication.class, args);\n",[62,58944,58945],{"class":64,"line":167},[62,58946,28897],{"class":72},[62,58948,58949],{"class":64,"line":173},[62,58950,79],{"emptyLinePlaceholder":13},[62,58952,58953,58956],{"class":64,"line":179},[62,58954,58955],{"class":72}," @",[62,58957,2146],{"class":68},[62,58959,58960,58963,58965,58968,58971],{"class":64,"line":185},[62,58961,58962],{"class":72}," CommandLineRunner ",[62,58964,52603],{"class":122},[62,58966,58967],{"class":72},"(CityRepository ",[62,58969,58970],{"class":889},"cityRepository",[62,58972,34126],{"class":72},[62,58974,58975,58978,58980,58982],{"class":64,"line":191},[62,58976,58977],{"class":68}," return",[62,58979,2169],{"class":72},[62,58981,800],{"class":68},[62,58983,126],{"class":72},[62,58985,58986,58989,58991,58993,58995,58997,58999,59002],{"class":64,"line":209},[62,58987,58988],{"class":72}," cityRepository.",[62,58990,22562],{"class":122},[62,58992,52630],{"class":72},[62,58994,2426],{"class":68},[62,58996,58754],{"class":122},[62,58998,2109],{"class":72},[62,59000,59001],{"class":1675},"\"Cleveland\"",[62,59003,59004],{"class":72},") );\n",[62,59006,59007],{"class":64,"line":220},[62,59008,59009],{"class":72}," };\n",[62,59011,59012],{"class":64,"line":226},[62,59013,28897],{"class":72},[62,59015,59016],{"class":64,"line":231},[62,59017,379],{"class":72},[22,59019,59020],{},"When we try and run this application you will see the following error. ",[52,59022,59024],{"className":1663,"code":59023,"language":1665,"meta":57,"style":57},"Caused by: javax.validation.ConstraintViolationException: Validation failed for classes [com.therealdanvega.City] during persist time for groups [javax.validation.groups.Default, ]\nList of constraint violations:[\n ConstraintViolationImpl{interpolatedMessage='may not be null', propertyPath=state, rootBeanClass=class com.therealdanvega.City, messageTemplate='{javax.validation.constraints.NotNull.message}'}\n",[59,59025,59026,59059,59073],{"__ignoreMap":57},[62,59027,59028,59031,59034,59037,59040,59043,59045,59048,59051,59054,59056],{"class":64,"line":65},[62,59029,59030],{"class":122},"Caused",[62,59032,59033],{"class":1675}," by:",[62,59035,59036],{"class":1675}," javax.validation.ConstraintViolationException:",[62,59038,59039],{"class":1675}," Validation",[62,59041,59042],{"class":1675}," failed",[62,59044,53629],{"class":1675},[62,59046,59047],{"class":1675}," classes",[62,59049,59050],{"class":72}," [com.therealdanvega.City] during persist ",[62,59052,59053],{"class":68},"time",[62,59055,53629],{"class":68},[62,59057,59058],{"class":72}," groups [javax.validation.groups.Default, ]\n",[62,59060,59061,59064,59067,59070],{"class":64,"line":76},[62,59062,59063],{"class":122},"List",[62,59065,59066],{"class":1675}," of",[62,59068,59069],{"class":1675}," constraint",[62,59071,59072],{"class":1675}," violations:[\n",[62,59074,59075,59078,59081,59084,59086,59089,59092,59095],{"class":64,"line":82},[62,59076,59077],{"class":122}," ConstraintViolationImpl",[62,59079,59080],{"class":1675},"{interpolatedMessage=",[62,59082,59083],{"class":122},"'may not be null'",[62,59085,32225],{"class":122},[62,59087,59088],{"class":1675}," propertyPath=state,",[62,59090,59091],{"class":1675}," rootBeanClass=class",[62,59093,59094],{"class":1675}," com.therealdanvega.City,",[62,59096,59097],{"class":1675}," messageTemplate='{javax.validation.constraints.NotNull.message}'}\n",[22,59099,59100,59101,59106],{},"This was so easy to do and the great thing is it doesn't stop there. If you want to add all kinds of validation to different properties you can. ",[677,59102,59105],{"href":59103,"rel":59104},"https://docs.oracle.com/javaee/7/api/javax/validation/constraints/package-summary.html",[681],"Check out the documentation"," to find a list of annotations you can add for validation. ",[26,59108,59110],{"id":59109},"custom-validation","Custom Validation",[22,59112,59113],{},"Most of the time the annotations provided will get the job the done. There are times when you need some type of custom validation done. In these cases, we can create our own custom validator and it's really easy to do. Say on our City object we wanted an annotation where we can make sure the state was equal to \"OHIO\". I know this is a silly example but I want to keep it simple. This is what our domain object would look like now. ",[52,59115,59117],{"className":54,"code":59116,"language":56,"meta":57,"style":57},"package com.therealdanvega;\n\nimport com.therealdanvega.validator.StateValidator;\nimport lombok.Data;\n\nimport javax.persistence.Entity;\nimport javax.persistence.GeneratedValue;\nimport javax.persistence.Id;\nimport javax.validation.constraints.NotNull;\n\n@Entity\n@Data\npublic class City {\n\n @Id @GeneratedValue\n private Long id;\n public String name;\n\n @NotNull\n @StateValidator( value = \"OHIO\" )\n public String state;\n\n private City() {}\n\n public City(String name){\n this.name = name;\n }\n\n public City(String name, String state){\n this.name = name;\n this.state = state;\n }\n}\n",[59,59118,59119,59125,59129,59136,59142,59146,59152,59158,59164,59170,59174,59180,59186,59196,59200,59210,59216,59222,59226,59232,59251,59257,59261,59269,59273,59285,59295,59299,59303,59319,59329,59341,59345],{"__ignoreMap":57},[62,59120,59121,59123],{"class":64,"line":65},[62,59122,69],{"class":68},[62,59124,52481],{"class":72},[62,59126,59127],{"class":64,"line":76},[62,59128,79],{"emptyLinePlaceholder":13},[62,59130,59131,59133],{"class":64,"line":82},[62,59132,27875],{"class":68},[62,59134,59135],{"class":72}," com.therealdanvega.validator.StateValidator;\n",[62,59137,59138,59140],{"class":64,"line":89},[62,59139,27875],{"class":68},[62,59141,58700],{"class":72},[62,59143,59144],{"class":64,"line":95},[62,59145,79],{"emptyLinePlaceholder":13},[62,59147,59148,59150],{"class":64,"line":101},[62,59149,27875],{"class":68},[62,59151,51959],{"class":72},[62,59153,59154,59156],{"class":64,"line":107},[62,59155,27875],{"class":68},[62,59157,51966],{"class":72},[62,59159,59160,59162],{"class":64,"line":113},[62,59161,27875],{"class":68},[62,59163,51973],{"class":72},[62,59165,59166,59168],{"class":64,"line":129},[62,59167,27875],{"class":68},[62,59169,58729],{"class":72},[62,59171,59172],{"class":64,"line":134},[62,59173,79],{"emptyLinePlaceholder":13},[62,59175,59176,59178],{"class":64,"line":156},[62,59177,942],{"class":72},[62,59179,8999],{"class":68},[62,59181,59182,59184],{"class":64,"line":161},[62,59183,942],{"class":72},[62,59185,9388],{"class":68},[62,59187,59188,59190,59192,59194],{"class":64,"line":167},[62,59189,116],{"class":68},[62,59191,119],{"class":68},[62,59193,58754],{"class":122},[62,59195,126],{"class":72},[62,59197,59198],{"class":64,"line":173},[62,59199,79],{"emptyLinePlaceholder":13},[62,59201,59202,59204,59206,59208],{"class":64,"line":179},[62,59203,2143],{"class":72},[62,59205,9016],{"class":68},[62,59207,9019],{"class":72},[62,59209,9022],{"class":68},[62,59211,59212,59214],{"class":64,"line":185},[62,59213,137],{"class":68},[62,59215,9029],{"class":72},[62,59217,59218,59220],{"class":64,"line":191},[62,59219,194],{"class":68},[62,59221,9406],{"class":72},[62,59223,59224],{"class":64,"line":209},[62,59225,79],{"emptyLinePlaceholder":13},[62,59227,59228,59230],{"class":64,"line":220},[62,59229,2143],{"class":72},[62,59231,58789],{"class":68},[62,59233,59234,59236,59239,59241,59243,59245,59248],{"class":64,"line":226},[62,59235,2143],{"class":72},[62,59237,59238],{"class":68},"StateValidator",[62,59240,52630],{"class":72},[62,59242,2553],{"class":149},[62,59244,2556],{"class":68},[62,59246,59247],{"class":1675}," \"OHIO\"",[62,59249,59250],{"class":72}," )\n",[62,59252,59253,59255],{"class":64,"line":231},[62,59254,194],{"class":68},[62,59256,58796],{"class":72},[62,59258,59259],{"class":64,"line":236},[62,59260,79],{"emptyLinePlaceholder":13},[62,59262,59263,59265,59267],{"class":64,"line":242},[62,59264,137],{"class":68},[62,59266,58754],{"class":122},[62,59268,58809],{"class":72},[62,59270,59271],{"class":64,"line":247},[62,59272,79],{"emptyLinePlaceholder":13},[62,59274,59275,59277,59279,59281,59283],{"class":64,"line":252},[62,59276,194],{"class":68},[62,59278,58754],{"class":122},[62,59280,1049],{"class":72},[62,59282,3107],{"class":889},[62,59284,34126],{"class":72},[62,59286,59287,59289,59291,59293],{"class":64,"line":257},[62,59288,2405],{"class":149},[62,59290,58832],{"class":72},[62,59292,146],{"class":68},[62,59294,58837],{"class":72},[62,59296,59297],{"class":64,"line":271},[62,59298,223],{"class":72},[62,59300,59301],{"class":64,"line":281},[62,59302,79],{"emptyLinePlaceholder":13},[62,59304,59305,59307,59309,59311,59313,59315,59317],{"class":64,"line":286},[62,59306,194],{"class":68},[62,59308,58754],{"class":122},[62,59310,1049],{"class":72},[62,59312,3107],{"class":889},[62,59314,8624],{"class":72},[62,59316,43061],{"class":889},[62,59318,34126],{"class":72},[62,59320,59321,59323,59325,59327],{"class":64,"line":291},[62,59322,2405],{"class":149},[62,59324,58832],{"class":72},[62,59326,146],{"class":68},[62,59328,58837],{"class":72},[62,59330,59331,59333,59336,59338],{"class":64,"line":296},[62,59332,2405],{"class":149},[62,59334,59335],{"class":72},".state ",[62,59337,146],{"class":68},[62,59339,59340],{"class":72}," state;\n",[62,59342,59343],{"class":64,"line":302},[62,59344,223],{"class":72},[62,59346,59347],{"class":64,"line":308},[62,59348,379],{"class":72},[22,59350,59351],{},"Now we create our own annotation & StateValidatorCheck constraint. ",[52,59353,59355],{"className":54,"code":59354,"language":56,"meta":57,"style":57},"package com.therealdanvega.validator;\n\nimport javax.validation.Constraint;\nimport javax.validation.Payload;\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.Target;\n\nimport static java.lang.annotation.ElementType.ANNOTATION_TYPE;\nimport static java.lang.annotation.ElementType.FIELD;\nimport static java.lang.annotation.ElementType.METHOD;\nimport static java.lang.annotation.RetentionPolicy.RUNTIME;\n\n@Documented\n@Constraint(validatedBy = StateValidatorCheck.class)\n@Target({ METHOD, FIELD, ANNOTATION_TYPE })\n@Retention(RUNTIME)\npublic @interface StateValidator {\n String message() default \"{com.therealdanvega.state.message}\";\n Class\u003C?>[] groups() default {};\n Class\u003C? extends Payload>[] payload() default {};\n String value() default \"\";\n}\n",[59,59356,59357,59364,59368,59375,59382,59389,59396,59403,59407,59416,59425,59434,59443,59447,59454,59471,59481,59491,59504,59521,59541,59563,59577],{"__ignoreMap":57},[62,59358,59359,59361],{"class":64,"line":65},[62,59360,69],{"class":68},[62,59362,59363],{"class":72}," com.therealdanvega.validator;\n",[62,59365,59366],{"class":64,"line":76},[62,59367,79],{"emptyLinePlaceholder":13},[62,59369,59370,59372],{"class":64,"line":82},[62,59371,27875],{"class":68},[62,59373,59374],{"class":72}," javax.validation.Constraint;\n",[62,59376,59377,59379],{"class":64,"line":89},[62,59378,27875],{"class":68},[62,59380,59381],{"class":72}," javax.validation.Payload;\n",[62,59383,59384,59386],{"class":64,"line":95},[62,59385,27875],{"class":68},[62,59387,59388],{"class":72}," java.lang.annotation.Documented;\n",[62,59390,59391,59393],{"class":64,"line":101},[62,59392,27875],{"class":68},[62,59394,59395],{"class":72}," java.lang.annotation.Retention;\n",[62,59397,59398,59400],{"class":64,"line":107},[62,59399,27875],{"class":68},[62,59401,59402],{"class":72}," java.lang.annotation.Target;\n",[62,59404,59405],{"class":64,"line":113},[62,59406,79],{"emptyLinePlaceholder":13},[62,59408,59409,59411,59413],{"class":64,"line":129},[62,59410,27875],{"class":68},[62,59412,2101],{"class":68},[62,59414,59415],{"class":72}," java.lang.annotation.ElementType.ANNOTATION_TYPE;\n",[62,59417,59418,59420,59422],{"class":64,"line":134},[62,59419,27875],{"class":68},[62,59421,2101],{"class":68},[62,59423,59424],{"class":72}," java.lang.annotation.ElementType.FIELD;\n",[62,59426,59427,59429,59431],{"class":64,"line":156},[62,59428,27875],{"class":68},[62,59430,2101],{"class":68},[62,59432,59433],{"class":72}," java.lang.annotation.ElementType.METHOD;\n",[62,59435,59436,59438,59440],{"class":64,"line":161},[62,59437,27875],{"class":68},[62,59439,2101],{"class":68},[62,59441,59442],{"class":72}," java.lang.annotation.RetentionPolicy.RUNTIME;\n",[62,59444,59445],{"class":64,"line":167},[62,59446,79],{"emptyLinePlaceholder":13},[62,59448,59449,59451],{"class":64,"line":173},[62,59450,942],{"class":72},[62,59452,59453],{"class":68},"Documented\n",[62,59455,59456,59458,59461,59463,59466,59468],{"class":64,"line":179},[62,59457,942],{"class":72},[62,59459,59460],{"class":68},"Constraint",[62,59462,2109],{"class":72},[62,59464,59465],{"class":149},"validatedBy",[62,59467,2556],{"class":68},[62,59469,59470],{"class":72}," StateValidatorCheck.class)\n",[62,59472,59473,59475,59478],{"class":64,"line":185},[62,59474,942],{"class":72},[62,59476,59477],{"class":68},"Target",[62,59479,59480],{"class":72},"({ METHOD, FIELD, ANNOTATION_TYPE })\n",[62,59482,59483,59485,59488],{"class":64,"line":191},[62,59484,942],{"class":72},[62,59486,59487],{"class":68},"Retention",[62,59489,59490],{"class":72},"(RUNTIME)\n",[62,59492,59493,59495,59497,59499,59502],{"class":64,"line":209},[62,59494,116],{"class":68},[62,59496,9019],{"class":72},[62,59498,36687],{"class":68},[62,59500,59501],{"class":68}," StateValidator",[62,59503,126],{"class":72},[62,59505,59506,59509,59511,59513,59516,59519],{"class":64,"line":220},[62,59507,59508],{"class":72}," String ",[62,59510,19844],{"class":122},[62,59512,5398],{"class":72},[62,59514,59515],{"class":68},"default",[62,59517,59518],{"class":1675}," \"{com.therealdanvega.state.message}\"",[62,59520,153],{"class":72},[62,59522,59523,59526,59529,59531,59534,59536,59538],{"class":64,"line":226},[62,59524,59525],{"class":72}," Class",[62,59527,59528],{"class":68},"\u003C?>",[62,59530,2114],{"class":72},[62,59532,59533],{"class":122},"groups",[62,59535,5398],{"class":72},[62,59537,59515],{"class":68},[62,59539,59540],{"class":72}," {};\n",[62,59542,59543,59545,59547,59550,59552,59554,59557,59559,59561],{"class":64,"line":231},[62,59544,59525],{"class":72},[62,59546,5659],{"class":68},[62,59548,59549],{"class":72}," extends Payload",[62,59551,2583],{"class":68},[62,59553,2114],{"class":72},[62,59555,59556],{"class":122},"payload",[62,59558,5398],{"class":72},[62,59560,59515],{"class":68},[62,59562,59540],{"class":72},[62,59564,59565,59567,59569,59571,59573,59575],{"class":64,"line":236},[62,59566,59508],{"class":72},[62,59568,2553],{"class":122},[62,59570,5398],{"class":72},[62,59572,59515],{"class":68},[62,59574,51048],{"class":1675},[62,59576,153],{"class":72},[62,59578,59579],{"class":64,"line":242},[62,59580,379],{"class":72},[52,59582,59584],{"className":54,"code":59583,"language":56,"meta":57,"style":57},"package com.therealdanvega.validator;\n\nimport com.therealdanvega.City;\n\nimport javax.validation.ConstraintValidator;\nimport javax.validation.ConstraintValidatorContext;\n\npublic class StateValidatorCheck implements ConstraintValidator\u003CStateValidator, String> {\n\n private String state;\n\n @Override\n public void initialize(StateValidator constraint) {\n this.state = constraint.value();\n }\n\n @Override\n public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) {\n if( s.equalsIgnoreCase( this.state ))\n return true;\n\n return false;\n }\n}\n",[59,59585,59586,59592,59596,59603,59607,59614,59621,59625,59649,59653,59659,59663,59669,59686,59701,59705,59709,59715,59736,59753,59761,59765,59773,59777],{"__ignoreMap":57},[62,59587,59588,59590],{"class":64,"line":65},[62,59589,69],{"class":68},[62,59591,59363],{"class":72},[62,59593,59594],{"class":64,"line":76},[62,59595,79],{"emptyLinePlaceholder":13},[62,59597,59598,59600],{"class":64,"line":82},[62,59599,27875],{"class":68},[62,59601,59602],{"class":72}," com.therealdanvega.City;\n",[62,59604,59605],{"class":64,"line":89},[62,59606,79],{"emptyLinePlaceholder":13},[62,59608,59609,59611],{"class":64,"line":95},[62,59610,27875],{"class":68},[62,59612,59613],{"class":72}," javax.validation.ConstraintValidator;\n",[62,59615,59616,59618],{"class":64,"line":101},[62,59617,27875],{"class":68},[62,59619,59620],{"class":72}," javax.validation.ConstraintValidatorContext;\n",[62,59622,59623],{"class":64,"line":107},[62,59624,79],{"emptyLinePlaceholder":13},[62,59626,59627,59629,59631,59634,59636,59639,59641,59643,59645,59647],{"class":64,"line":113},[62,59628,116],{"class":68},[62,59630,119],{"class":68},[62,59632,59633],{"class":122}," StateValidatorCheck",[62,59635,13520],{"class":68},[62,59637,59638],{"class":122}," ConstraintValidator",[62,59640,760],{"class":72},[62,59642,59238],{"class":68},[62,59644,976],{"class":72},[62,59646,973],{"class":68},[62,59648,8552],{"class":72},[62,59650,59651],{"class":64,"line":129},[62,59652,79],{"emptyLinePlaceholder":13},[62,59654,59655,59657],{"class":64,"line":134},[62,59656,137],{"class":68},[62,59658,58796],{"class":72},[62,59660,59661],{"class":64,"line":156},[62,59662,79],{"emptyLinePlaceholder":13},[62,59664,59665,59667],{"class":64,"line":161},[62,59666,2143],{"class":72},[62,59668,13555],{"class":68},[62,59670,59671,59673,59675,59678,59681,59684],{"class":64,"line":167},[62,59672,194],{"class":68},[62,59674,200],{"class":68},[62,59676,59677],{"class":122}," initialize",[62,59679,59680],{"class":72},"(StateValidator ",[62,59682,59683],{"class":889},"constraint",[62,59685,768],{"class":72},[62,59687,59688,59690,59692,59694,59697,59699],{"class":64,"line":173},[62,59689,2405],{"class":149},[62,59691,59335],{"class":72},[62,59693,146],{"class":68},[62,59695,59696],{"class":72}," constraint.",[62,59698,2553],{"class":122},[62,59700,822],{"class":72},[62,59702,59703],{"class":64,"line":179},[62,59704,223],{"class":72},[62,59706,59707],{"class":64,"line":185},[62,59708,79],{"emptyLinePlaceholder":13},[62,59710,59711,59713],{"class":64,"line":191},[62,59712,2143],{"class":72},[62,59714,13555],{"class":68},[62,59716,59717,59719,59721,59724,59726,59728,59731,59734],{"class":64,"line":209},[62,59718,194],{"class":68},[62,59720,1043],{"class":68},[62,59722,59723],{"class":122}," isValid",[62,59725,1049],{"class":72},[62,59727,57106],{"class":889},[62,59729,59730],{"class":72},", ConstraintValidatorContext ",[62,59732,59733],{"class":889},"constraintValidatorContext",[62,59735,768],{"class":72},[62,59737,59738,59740,59743,59746,59748,59750],{"class":64,"line":220},[62,59739,12741],{"class":68},[62,59741,59742],{"class":72},"( s.",[62,59744,59745],{"class":122},"equalsIgnoreCase",[62,59747,52630],{"class":72},[62,59749,1295],{"class":149},[62,59751,59752],{"class":72},".state ))\n",[62,59754,59755,59757,59759],{"class":64,"line":226},[62,59756,600],{"class":68},[62,59758,1227],{"class":149},[62,59760,153],{"class":72},[62,59762,59763],{"class":64,"line":231},[62,59764,79],{"emptyLinePlaceholder":13},[62,59766,59767,59769,59771],{"class":64,"line":236},[62,59768,360],{"class":68},[62,59770,1165],{"class":149},[62,59772,153],{"class":72},[62,59774,59775],{"class":64,"line":242},[62,59776,223],{"class":72},[62,59778,59779],{"class":64,"line":247},[62,59780,379],{"class":72},[22,59782,59783],{},"Now if we try and create a city object with a state other than OHIO we will get an error. ",[52,59785,59787],{"className":54,"code":59786,"language":56,"meta":57,"style":57},"package com.therealdanvega;\n\nimport org.springframework.boot.CommandLineRunner;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.context.annotation.Bean;\n\n@SpringBootApplication\npublic class ValidationApplication {\n\n public static void main(String[] args) {\n SpringApplication.run(ValidationApplication.class, args);\n }\n\n @Bean\n CommandLineRunner runner(CityRepository cityRepository){\n return args -> {\n cityRepository.save( new City(\"Cleveland\", \"Tennesee\") );\n };\n }\n}\n",[59,59788,59789,59795,59799,59805,59811,59817,59823,59827,59833,59843,59847,59867,59875,59879,59883,59889,59901,59911,59934,59938,59942],{"__ignoreMap":57},[62,59790,59791,59793],{"class":64,"line":65},[62,59792,69],{"class":68},[62,59794,52481],{"class":72},[62,59796,59797],{"class":64,"line":76},[62,59798,79],{"emptyLinePlaceholder":13},[62,59800,59801,59803],{"class":64,"line":82},[62,59802,27875],{"class":68},[62,59804,52506],{"class":72},[62,59806,59807,59809],{"class":64,"line":89},[62,59808,27875],{"class":68},[62,59810,52513],{"class":72},[62,59812,59813,59815],{"class":64,"line":95},[62,59814,27875],{"class":68},[62,59816,52520],{"class":72},[62,59818,59819,59821],{"class":64,"line":101},[62,59820,27875],{"class":68},[62,59822,52527],{"class":72},[62,59824,59825],{"class":64,"line":107},[62,59826,79],{"emptyLinePlaceholder":13},[62,59828,59829,59831],{"class":64,"line":113},[62,59830,942],{"class":72},[62,59832,2079],{"class":68},[62,59834,59835,59837,59839,59841],{"class":64,"line":129},[62,59836,116],{"class":68},[62,59838,119],{"class":68},[62,59840,58906],{"class":122},[62,59842,126],{"class":72},[62,59844,59845],{"class":64,"line":134},[62,59846,79],{"emptyLinePlaceholder":13},[62,59848,59849,59851,59853,59855,59857,59859,59861,59863,59865],{"class":64,"line":156},[62,59850,28858],{"class":68},[62,59852,2101],{"class":68},[62,59854,200],{"class":68},[62,59856,2106],{"class":122},[62,59858,2109],{"class":72},[62,59860,973],{"class":68},[62,59862,2114],{"class":72},[62,59864,2117],{"class":889},[62,59866,768],{"class":72},[62,59868,59869,59871,59873],{"class":64,"line":161},[62,59870,58937],{"class":72},[62,59872,2127],{"class":122},[62,59874,58942],{"class":72},[62,59876,59877],{"class":64,"line":167},[62,59878,28897],{"class":72},[62,59880,59881],{"class":64,"line":173},[62,59882,79],{"emptyLinePlaceholder":13},[62,59884,59885,59887],{"class":64,"line":179},[62,59886,58955],{"class":72},[62,59888,2146],{"class":68},[62,59890,59891,59893,59895,59897,59899],{"class":64,"line":185},[62,59892,58962],{"class":72},[62,59894,52603],{"class":122},[62,59896,58967],{"class":72},[62,59898,58970],{"class":889},[62,59900,34126],{"class":72},[62,59902,59903,59905,59907,59909],{"class":64,"line":191},[62,59904,58977],{"class":68},[62,59906,2169],{"class":72},[62,59908,800],{"class":68},[62,59910,126],{"class":72},[62,59912,59913,59915,59917,59919,59921,59923,59925,59927,59929,59932],{"class":64,"line":209},[62,59914,58988],{"class":72},[62,59916,22562],{"class":122},[62,59918,52630],{"class":72},[62,59920,2426],{"class":68},[62,59922,58754],{"class":122},[62,59924,2109],{"class":72},[62,59926,59001],{"class":1675},[62,59928,976],{"class":72},[62,59930,59931],{"class":1675},"\"Tennesee\"",[62,59933,59004],{"class":72},[62,59935,59936],{"class":64,"line":220},[62,59937,59009],{"class":72},[62,59939,59940],{"class":64,"line":226},[62,59941,28897],{"class":72},[62,59943,59944],{"class":64,"line":231},[62,59945,379],{"class":72},[52,59947,59949],{"className":1663,"code":59948,"language":1665,"meta":57,"style":57},"Caused by: javax.validation.ConstraintViolationException: Validation failed for classes [com.therealdanvega.City] during persist time for groups [javax.validation.groups.Default, ]\nList of constraint violations:[\n ConstraintViolationImpl{interpolatedMessage='{com.therealdanvega.state.message}', propertyPath=state, rootBeanClass=class com.therealdanvega.City, messageTemplate='{com.therealdanvega.state.message}'}\n",[59,59950,59951,59975,59985],{"__ignoreMap":57},[62,59952,59953,59955,59957,59959,59961,59963,59965,59967,59969,59971,59973],{"class":64,"line":65},[62,59954,59030],{"class":122},[62,59956,59033],{"class":1675},[62,59958,59036],{"class":1675},[62,59960,59039],{"class":1675},[62,59962,59042],{"class":1675},[62,59964,53629],{"class":1675},[62,59966,59047],{"class":1675},[62,59968,59050],{"class":72},[62,59970,59053],{"class":68},[62,59972,53629],{"class":68},[62,59974,59058],{"class":72},[62,59976,59977,59979,59981,59983],{"class":64,"line":76},[62,59978,59063],{"class":122},[62,59980,59066],{"class":1675},[62,59982,59069],{"class":1675},[62,59984,59072],{"class":1675},[62,59986,59987,59989,59991,59994,59996,59998,60000,60002],{"class":64,"line":82},[62,59988,59077],{"class":122},[62,59990,59080],{"class":1675},[62,59992,59993],{"class":122},"'{com.therealdanvega.state.message}'",[62,59995,32225],{"class":122},[62,59997,59088],{"class":1675},[62,59999,59091],{"class":1675},[62,60001,59094],{"class":1675},[62,60003,60004],{"class":1675}," messageTemplate='{com.therealdanvega.state.message}'}\n",[26,60006,60007],{"id":1498},"Conclusion ",[22,60009,60010],{},"As you can see its pretty easy to sprinkle in some validation in your Spring Boot applications. ",[22,60012,60013],{},[4534,60014,60015,60017],{},[646,60016,49733],{}," What are the challenges you face in validating data?",[1527,60019,60020],{},"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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .spoOn, html code.shiki .spoOn{--shiki-default:#E36209;--shiki-dark:#FFAB70;--shiki-sepia:#E36209}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}",{"title":57,"searchDepth":76,"depth":76,"links":60022},[60023,60024,60025],{"id":58674,"depth":76,"text":58675},{"id":59109,"depth":76,"text":59110},{"id":1498,"depth":76,"text":60007},{"_id":60027,"path":60028,"title":60029,"description":60029,"meta":60030,"body":60036},"content/blog/2017/04/28/every-developer-start-blog-right-now.md","/blog/2017/04/28/every-developer-start-blog-right-now","Why every developer should start a blog right now!",{"slug":60031,"date":60032,"published":13,"tags":60033,"author":-1,"cover":60035,"excerpt":-1},"every-developer-start-blog-right-now","2017-04-28T09:15:56-04:00",[60034],"blogging","./ken-tomita-239357-760x507.jpg",{"type":19,"value":60037,"toc":60171},[60038,60041,60045,60048,60052,60066,60071,60074,60078,60085,60089,60092,60098,60113,60117,60124,60128,60131,60135,60138,60142,60145,60151,60159,60161,60164],[22,60039,60040],{},"In this article, I want to dive into why I believe that every developer should start a blog right now. This will be a little bit of a bias opinion because I believe this is where both my career and my life's mission started. I realize that not everyone is walking the same path as I am in life. With that said I do see some real value in sharing your experiences as a developer with others. I am going to share my journey with you and give you the one thing I wish I would have known when I started blogging.",[26,60042,60044],{"id":60043},"why-i-started-blogging","Why I started Blogging",[22,60046,60047],{},"I know that a lot of people start blogging because they want to have their voice heard. That wasn't the case for me when I started writing and it doesn't need to be your reason either.",[636,60049,60051],{"id":60050},"blogcfc","BlogCFC",[22,60053,60054,60055,60060,60061,60065],{},"When I started I did it because I honestly wanted to learn how to build stuff. I looked up to a bunch of developers in the ColdFusion community and all of them all had blogs. One of those bloggers was ",[677,60056,60059],{"href":60057,"rel":60058},"https://www.raymondcamden.com/",[681],"Ray Camden"," and he had this cool open source blogging software called ",[677,60062,60051],{"href":60063,"rel":60064},"http://www.blogcfc.com/index.cfm",[681],". Below is a screenshot of the website that I would one day help Ray create. It might not look like much but I was really excited to be a part of an open source project. If you look closely you can see a screenshot of my old blog. ",[22,60067,60068],{},[653,60069],{"alt":60051,"src":60070},"./2017-04-28_07-21-20.png",[22,60072,60073],{},"So there was this community of bloggers and I really wanted to be one of them. I downloaded this open source blogging software and I learned how to install it locally, customize it and publish it to a production server. So not only was I now blogging but I was also involved in the community and a part of an open source project. This point of being involved in a community is a special one and we will dive into that deeper later in this article. I started writing in October 2005 and while my focus might have changed over the years my goal hasn't. I wanted to learn \"stuff\" and I wanted to understand it to the point where I could help other people understand it as well. This innate sense to want to help others ultimately led me to what I am doing today. This innate sense to lead and help others ultimately led me to what I am doing today. ",[26,60075,60077],{"id":60076},"why-you-should-start-blogging","Why You Should Start Blogging",[22,60079,60080,60081,60084],{},"As I said at the beginning of this article I completely understand that everyone's goals are not the same as mine. I do believe there are some real good reasons for you starting your own blog and we will discuss those here. Before we jump into those I also want to stress that this isn't just for new developers. It is ",[646,60082,60083],{},"never"," too late to start writing and be a part of a community so please don't let silly numbers like age or years on the job stop you from being a part of something great. ",[636,60086,60088],{"id":60087},"improve-your-writing-skills","Improve your writing skills",[22,60090,60091],{},"I don't think there is anyone that is reading this that couldn't benefit from improving your writing skills. This is a skill that will follow you around in life and it doesn't hurt to constantly work on it. Whether you're writing a resume or an email to a friend, good writing and more importantly, terrible writing is easy to spot. ",[22,60093,60094],{},[653,60095],{"alt":60096,"src":60097},"Start a blog to improve your writing skills.","./alejandro-escamilla-4-1024x683.jpg",[22,60099,60100,60103,60104],{},[4534,60101,60102],{},"Pro Tip:"," ",[4534,60105,60106,60107,60112],{},"If you aren't already using ",[677,60108,60111],{"href":60109,"rel":60110},"https://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=1&cad=rja&uact=8&ved=0ahUKEwiqgvCNlcfTAhVrqlQKHZuxB0wQFgg1MAA&url=https%3A%2F%2Fwww.grammarly.com%2F&usg=AFQjCNEEJC76jVSls2ggmUw2JGlwSqyTtQ&sig2=CCSGHVYnlTRRQgZ96zRpAA",[681],"Grammarly"," you need to check it out.",[636,60114,60116],{"id":60115},"boost-you-resume","Boost You Resume",[22,60118,60119,60120,60123],{},"A personal blog where talk passionately about what you do is only going to help your career. You have to remember that potential employers are Googling you to see what comes up. If your personal blog, YouTube channel and Github account come up, that is a ",[646,60121,60122],{},"WIN",". This screams that you are passionate about what you do and that it isn't just a job. ",[636,60125,60127],{"id":60126},"community","Community",[22,60129,60130],{},"When I started writing I would write about stuff that I came across as an everyday developer. In doing so I would start to conversate with other members of the community. To me, this is very important for all developers. If you work for a smaller company chances are you don't have a large number of developers that you can look up to. If you get involved in the community you have a chance to have discussions about the problems you face with developers who have real world experience. The biggest part of the community for me was the connections I made. I am blessed to have met some really great people just by chatting them up at conferences. A majority of these conversations started because of my blog and being involved in the community. Did I happen to mention that I have my current job because of connections I have made in the community? I'm also proud to say that 4 coworkers will be attending my wedding later on this year. These are just work connections, these are friends for life connections. ",[636,60132,60134],{"id":60133},"my-top-10-reasons-to-blog","My Top 10 Reasons to Blog",[22,60136,60137],{},"As I was writing this I realized that I have a top 10 list of reasons every developer should start a blog. I am adding this to my task list and when I get that published I will let you guys know! ",[26,60139,60141],{"id":60140},"the-1thing-you-must-do-when-starting-your-blog","The 1 Thing you must do when starting your blog",[22,60143,60144],{},"I said at the start of this article that I was going to give you 1 thing you must do today. You absolutely need to start an email list. If I could go back in time to 2005 when I started this blog the one thing I would do is start collecting email addresses. This is an audience your building and you need to stay engaged with them. If it's just letting them know about a new post or telling them about a new product your working on. These readers subscribe because of the value that you bring to them and they want to stay up to date with what you are doing. ",[22,60146,60147],{},[653,60148],{"alt":60149,"src":60150},"Building your audience","./luca-bravo-207676-1024x683.jpg",[22,60152,60153,60154,57647],{},"There are many tools that can help you with building your list but my absolute favorite is ",[677,60155,60158],{"href":60156,"rel":60157},"https://danvega.dev/convertkit",[681],"Convertkit",[26,60160,1499],{"id":1498},[22,60162,60163],{},"I am tired of hearing people scream from the rooftops that blogging is dead. Is video on the rise, of course, it is. This doesn't mean that there is real value in starting your own blog. I hope this pushes anyone thinking about starting a blog into action. It was one of the best decisions I have ever made and I hope you will give it a shot. Please let me know if you have any questions on getting started, finding content or anything related to blogging. Happy Friday friends!",[22,60165,60166],{},[4534,60167,60168,60170],{},[646,60169,49733],{}," What is stopping you from starting a blog right now?",{"title":57,"searchDepth":76,"depth":76,"links":60172},[60173,60176,60182,60183],{"id":60043,"depth":76,"text":60044,"children":60174},[60175],{"id":60050,"depth":82,"text":60051},{"id":60076,"depth":76,"text":60077,"children":60177},[60178,60179,60180,60181],{"id":60087,"depth":82,"text":60088},{"id":60115,"depth":82,"text":60116},{"id":60126,"depth":82,"text":60127},{"id":60133,"depth":82,"text":60134},{"id":60140,"depth":76,"text":60141},{"id":1498,"depth":76,"text":1499},{"_id":60185,"path":60186,"title":60187,"description":60187,"meta":60188,"body":60193},"content/blog/2017/04/26/what-is-going-wrong-on-the-spring-boot-view-layer.md","/blog/2017/04/26/what-is-going-wrong-on-the-spring-boot-view-layer","What is going wrong on the Spring Boot View Layer?",{"slug":60189,"date":60190,"published":13,"tags":60191,"author":-1,"cover":60192,"excerpt":-1},"what-is-going-wrong-on-the-spring-boot-view-layer","2017-04-26T09:02:28-04:00",[11002],"./view_layer-760x760.png",{"type":19,"value":60194,"toc":60786},[60195,60198,60207,60211,60214,60219,60223,60230,60322,60329,60336,60390,60393,60397,60400,60429,60432,60436,60439,60461,60464,60564,60567,60762,60765,60770,60773,60775,60778,60783],[22,60196,60197],{},"Today we are going to dive into a question from a student of mine. It may seem obvious to some people what is happening but unless you really understand what is going on in the Spring Boot View Layer It can be a little bit confusing.",[29685,60199,60200],{},[22,60201,60202,60203,60206],{},"Good evening, Mr. Vega. How do you do? My name is Fabio and have a doubt about Spring Boot's behavior. So, I created an index.html file under the static folder (is index.html or any HTML file for that matter what you call a template? Is it just another way to refer to HTML files?) right after importing the project generated by the Initializer and ran the application, with the expected result (the application found and displayed the index). But when I moved on to creating a controller class, with the mapping of the root page, all of a sudden the application couldn't find the HTML anymore, issuing an error message to the console to this effect: ",[59,60204,60205],{},"org.thymeleaf.exceptions.TemplateInputException: Error resolving template \"index\", the template might not exist or might not be accessible by any of the configured Template Resolvers at org.thymeleaf.TemplateRepository.getTemplate(TemplateRepository.java:246) ~[thymeleaf-2.1.5.RELEASE.jar:2.1.5.RELEASE]"," When I move it to the templates folder, suddenly the application finds it. It's as if when I try without a controller, Spring calls the shots, who looks for it under the static folder; and when I have a controller, it falls to Thymeleaf, who looks for it under templates. What's going on?",[26,60208,60210],{"id":60209},"spring-bootresources","Spring Boot Resources",[22,60212,60213],{},"When you create a brand new Spring Boot Project you will see a resources folder. In that folder, you will have 2 folders (static and templates) that are the subject of this discussion. The static folder is used for static content such as HTML, JavaScript, CSS, Images, etc... The templates folder will be used for template engines to serve dynamic content.",[22,60215,60216],{},[653,60217],{"alt":58036,"src":60218},"./2017-04-26_07-44-17.png",[636,60220,60222],{"id":60221},"spring-boot-static-content","Spring Boot Static Content",[22,60224,60225,60226,60229],{},"By default, Spring Boot will serve static content from a directory called ",[59,60227,60228],{},"/static"," (or /public, /resources, /META-INF/resources) in the classpath or from the root of the ServletContext. If you place an index.html in the static folder this will become the homepage for the entire application. ",[52,60231,60233],{"className":15773,"code":60232,"language":15775,"meta":57,"style":57},"\u003Chtml>\n\u003Chead>\n \u003Ctitle>My Spring Boot Application\u003C/title>\n\u003C/head>\n\u003Cbody>\n \u003Ch1>Welcome to my Application\u003C/h1>\n \u003Cp>This is my default index.html page.\u003C/p>\n\u003C/body>\n\u003C/html>\n",[59,60234,60235,60243,60251,60264,60272,60280,60293,60306,60314],{"__ignoreMap":57},[62,60236,60237,60239,60241],{"class":64,"line":65},[62,60238,760],{"class":72},[62,60240,15775],{"class":1780},[62,60242,1784],{"class":72},[62,60244,60245,60247,60249],{"class":64,"line":76},[62,60246,760],{"class":72},[62,60248,15824],{"class":1780},[62,60250,1784],{"class":72},[62,60252,60253,60255,60257,60260,60262],{"class":64,"line":82},[62,60254,1789],{"class":72},[62,60256,3196],{"class":1780},[62,60258,60259],{"class":72},">My Spring Boot Application\u003C/",[62,60261,3196],{"class":1780},[62,60263,1784],{"class":72},[62,60265,60266,60268,60270],{"class":64,"line":89},[62,60267,1818],{"class":72},[62,60269,15824],{"class":1780},[62,60271,1784],{"class":72},[62,60273,60274,60276,60278],{"class":64,"line":95},[62,60275,760],{"class":72},[62,60277,11414],{"class":1780},[62,60279,1784],{"class":72},[62,60281,60282,60284,60286,60289,60291],{"class":64,"line":101},[62,60283,1789],{"class":72},[62,60285,4168],{"class":1780},[62,60287,60288],{"class":72},">Welcome to my Application\u003C/",[62,60290,4168],{"class":1780},[62,60292,1784],{"class":72},[62,60294,60295,60297,60299,60302,60304],{"class":64,"line":107},[62,60296,1789],{"class":72},[62,60298,22],{"class":1780},[62,60300,60301],{"class":72},">This is my default index.html page.\u003C/",[62,60303,22],{"class":1780},[62,60305,1784],{"class":72},[62,60307,60308,60310,60312],{"class":64,"line":113},[62,60309,1818],{"class":72},[62,60311,11414],{"class":1780},[62,60313,1784],{"class":72},[62,60315,60316,60318,60320],{"class":64,"line":129},[62,60317,1818],{"class":72},[62,60319,15775],{"class":1780},[62,60321,1784],{"class":72},[22,60323,60324,60325,60328],{},"We don't have to do anything else at all to our application. If we go ahead and run this demo and visit ",[677,60326,16942],{"href":16942,"rel":60327},[681]," we should see the following page. ",[22,60330,60331,60335],{},[653,60332],{"alt":60333,"src":60334},"Basic Welcome Page","./2017-04-26_07-59-29.png"," If you look in the console you will also see the following log statement. ",[52,60337,60339],{"className":1663,"code":60338,"language":1665,"meta":57,"style":57},"2017-04-26 07:59:07.489 INFO 94384 --- [ restartedMain] oConfiguration$WelcomePageHandlerMapping : Adding welcome page: class path resource [static/index.html]\n",[59,60340,60341],{"__ignoreMap":57},[62,60342,60343,60346,60349,60351,60354,60356,60359,60362,60365,60368,60370,60373,60376,60379,60381,60384,60387],{"class":64,"line":65},[62,60344,60345],{"class":122},"2017-04-26",[62,60347,60348],{"class":1675}," 07:59:07.489",[62,60350,40606],{"class":1675},[62,60352,60353],{"class":149}," 94384",[62,60355,40612],{"class":149},[62,60357,60358],{"class":72}," [ ",[62,60360,60361],{"class":1675},"restartedMain]",[62,60363,60364],{"class":1675}," oConfiguration",[62,60366,60367],{"class":72},"$WelcomePageHandlerMapping ",[62,60369,1266],{"class":1675},[62,60371,60372],{"class":1675}," Adding",[62,60374,60375],{"class":1675}," welcome",[62,60377,60378],{"class":1675}," page:",[62,60380,119],{"class":1675},[62,60382,60383],{"class":1675}," path",[62,60385,60386],{"class":1675}," resource",[62,60388,60389],{"class":72}," [static/index.html]\n",[22,60391,60392],{},"So if we refer back to the students question this is exactly why it worked for him the first time. Also, don't think of this as just a single boring HTML file. As you will see in the coming weeks on this blog you can drop an entire Angular application in that folder if you like and use that as the front-end to your application. ",[636,60394,60396],{"id":60395},"spring-boot-dynamic-content","Spring Boot Dynamic Content",[22,60398,60399],{},"As well as REST web services, you can also use Spring MVC to serve dynamic HTML content. Spring MVC supports a variety of templating technologies including Thymeleaf, FreeMarker, and JSPs. Many other templating engines also ship their own Spring MVC integrations. Spring Boot includes auto-configuration support for the following templating engines:",[915,60401,60402,60409,60415,60422],{},[37,60403,60404],{},[677,60405,60408],{"href":60406,"rel":60407},"http://freemarker.org/docs/",[681],"FreeMarker",[37,60410,60411],{},[677,60412,53590],{"href":60413,"rel":60414},"http://docs.groovy-lang.org/docs/next/html/documentation/template-engines.html#_the_markuptemplateengine",[681],[37,60416,60417],{},[677,60418,60421],{"href":60419,"rel":60420},"http://www.thymeleaf.org/",[681],"Thymeleaf",[37,60423,60424],{},[677,60425,60428],{"href":60426,"rel":60427},"https://mustache.github.io/",[681],"Mustache",[22,60430,60431],{},"When you’re using one of these templating engines with the default configuration, your templates will be picked up automatically from src/main/resources/templates.",[28831,60433,60435],{"id":60434},"spring-boot-controller","Spring Boot Controller",[22,60437,60438],{},"If we want to create a controller and add some dynamic content to our home we can do that as well. For this demo, I am going to use Thymeleaf so I have added the appropriate dependency to my pom.xml",[52,60440,60442],{"className":1769,"code":60441,"language":1771,"meta":57,"style":57},"\u003Cdependency>\n \u003CgroupId>org.springframework.boot\u003C/groupId>\n \u003CartifactId>spring-boot-starter-thymeleaf\u003C/artifactId>\n\u003C/dependency>\n",[59,60443,60444,60448,60452,60457],{"__ignoreMap":57},[62,60445,60446],{"class":64,"line":65},[62,60447,46425],{},[62,60449,60450],{"class":64,"line":76},[62,60451,54736],{},[62,60453,60454],{"class":64,"line":82},[62,60455,60456],{}," \u003CartifactId>spring-boot-starter-thymeleaf\u003C/artifactId>\n",[62,60458,60459],{"class":64,"line":89},[62,60460,46445],{},[22,60462,60463],{},"Here we create a controller class and a request mapping for \"/\". We are going to add a model attribute here called page title that will be pushed down to our view. ",[52,60465,60467],{"className":54,"code":60466,"language":56,"meta":57,"style":57},"package com.therealdanvega.controller;\n\n// imports\n\n@Controller\npublic class HomeController {\n @RequestMapping(\"/\")\n public String home(Model model){\n model.addAttribute(\"pageTitle\",\"Welcome to my Awesome Dynamic Application\");\n return \"index\";\n }\n}\n",[59,60468,60469,60475,60479,60484,60488,60494,60504,60516,60530,60548,60556,60560],{"__ignoreMap":57},[62,60470,60471,60473],{"class":64,"line":65},[62,60472,69],{"class":68},[62,60474,49492],{"class":72},[62,60476,60477],{"class":64,"line":76},[62,60478,79],{"emptyLinePlaceholder":13},[62,60480,60481],{"class":64,"line":82},[62,60482,60483],{"class":85},"// imports\n",[62,60485,60486],{"class":64,"line":89},[62,60487,79],{"emptyLinePlaceholder":13},[62,60489,60490,60492],{"class":64,"line":95},[62,60491,942],{"class":72},[62,60493,16624],{"class":68},[62,60495,60496,60498,60500,60502],{"class":64,"line":101},[62,60497,116],{"class":68},[62,60499,119],{"class":68},[62,60501,25636],{"class":122},[62,60503,126],{"class":72},[62,60505,60506,60508,60510,60512,60514],{"class":64,"line":107},[62,60507,2143],{"class":72},[62,60509,10592],{"class":68},[62,60511,2109],{"class":72},[62,60513,15635],{"class":1675},[62,60515,2212],{"class":72},[62,60517,60518,60520,60522,60524,60526,60528],{"class":64,"line":113},[62,60519,194],{"class":68},[62,60521,2469],{"class":72},[62,60523,18647],{"class":122},[62,60525,22475],{"class":72},[62,60527,16671],{"class":889},[62,60529,34126],{"class":72},[62,60531,60532,60534,60536,60538,60541,60543,60546],{"class":64,"line":129},[62,60533,22484],{"class":72},[62,60535,16736],{"class":122},[62,60537,2109],{"class":72},[62,60539,60540],{"class":1675},"\"pageTitle\"",[62,60542,32225],{"class":72},[62,60544,60545],{"class":1675},"\"Welcome to my Awesome Dynamic Application\"",[62,60547,1133],{"class":72},[62,60549,60550,60552,60554],{"class":64,"line":134},[62,60551,360],{"class":68},[62,60553,22506],{"class":1675},[62,60555,153],{"class":72},[62,60557,60558],{"class":64,"line":156},[62,60559,223],{"class":72},[62,60561,60562],{"class":64,"line":161},[62,60563,379],{"class":72},[22,60565,60566],{},"As you can see from the return statement we are returning index. You don't need to add the HTML file extension here because based on the template engine we use the extension is configured and already known to Spring. This is our Thymeleaf template and should be located in /resources/templates/index.html",[52,60568,60570],{"className":15773,"code":60569,"language":15775,"meta":57,"style":57},"\u003C!DOCTYPE html>\n\u003Chtml lang=\"en\"\n xmlns=\"http://www.w3.org/1999/xhtml\"\n xmlns:th=\"http://www.thymleaf.org\">\n\u003Chead>\n \u003Cmeta charset=\"utf-8\"/>\n \u003Cmeta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\"/>\n \u003Cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1\"/>\n \u003Ctitle>My Spring Boot Application\u003C/title>\n\u003C/head>\n\u003Cbody>\n \u003Ch1 th:text=\"${pageTitle}\">\u003C/h1>\n \u003Cp>This is my Thymeleaf template located at /resources/templates/index.html\u003C/p>\n\u003C/body>\n\u003C/html>\n",[59,60571,60572,60582,60595,60605,60617,60625,60641,60664,60685,60697,60705,60713,60733,60746,60754],{"__ignoreMap":57},[62,60573,60574,60576,60578,60580],{"class":64,"line":65},[62,60575,15801],{"class":72},[62,60577,15804],{"class":1780},[62,60579,15807],{"class":122},[62,60581,1784],{"class":72},[62,60583,60584,60586,60588,60590,60592],{"class":64,"line":76},[62,60585,760],{"class":72},[62,60587,15775],{"class":1780},[62,60589,20679],{"class":122},[62,60591,146],{"class":72},[62,60593,60594],{"class":1675},"\"en\"\n",[62,60596,60597,60600,60602],{"class":64,"line":82},[62,60598,60599],{"class":122}," xmlns",[62,60601,146],{"class":72},[62,60603,60604],{"class":1675},"\"http://www.w3.org/1999/xhtml\"\n",[62,60606,60607,60610,60612,60615],{"class":64,"line":89},[62,60608,60609],{"class":122}," xmlns:th",[62,60611,146],{"class":72},[62,60613,60614],{"class":1675},"\"http://www.thymleaf.org\"",[62,60616,1784],{"class":72},[62,60618,60619,60621,60623],{"class":64,"line":95},[62,60620,760],{"class":72},[62,60622,15824],{"class":1780},[62,60624,1784],{"class":72},[62,60626,60627,60629,60631,60633,60635,60638],{"class":64,"line":101},[62,60628,1789],{"class":72},[62,60630,20701],{"class":1780},[62,60632,20704],{"class":122},[62,60634,146],{"class":72},[62,60636,60637],{"class":1675},"\"utf-8\"",[62,60639,60640],{"class":72},"/>\n",[62,60642,60643,60645,60647,60650,60652,60655,60657,60659,60662],{"class":64,"line":107},[62,60644,1789],{"class":72},[62,60646,20701],{"class":1780},[62,60648,60649],{"class":122}," http-equiv",[62,60651,146],{"class":72},[62,60653,60654],{"class":1675},"\"X-UA-Compatible\"",[62,60656,20727],{"class":122},[62,60658,146],{"class":72},[62,60660,60661],{"class":1675},"\"IE=edge\"",[62,60663,60640],{"class":72},[62,60665,60666,60668,60670,60672,60674,60676,60678,60680,60683],{"class":64,"line":113},[62,60667,1789],{"class":72},[62,60669,20701],{"class":1780},[62,60671,16107],{"class":122},[62,60673,146],{"class":72},[62,60675,20724],{"class":1675},[62,60677,20727],{"class":122},[62,60679,146],{"class":72},[62,60681,60682],{"class":1675},"\"width=device-width, initial-scale=1\"",[62,60684,60640],{"class":72},[62,60686,60687,60689,60691,60693,60695],{"class":64,"line":129},[62,60688,1789],{"class":72},[62,60690,3196],{"class":1780},[62,60692,60259],{"class":72},[62,60694,3196],{"class":1780},[62,60696,1784],{"class":72},[62,60698,60699,60701,60703],{"class":64,"line":134},[62,60700,1818],{"class":72},[62,60702,15824],{"class":1780},[62,60704,1784],{"class":72},[62,60706,60707,60709,60711],{"class":64,"line":156},[62,60708,760],{"class":72},[62,60710,11414],{"class":1780},[62,60712,1784],{"class":72},[62,60714,60715,60717,60719,60722,60724,60727,60729,60731],{"class":64,"line":161},[62,60716,1789],{"class":72},[62,60718,4168],{"class":1780},[62,60720,60721],{"class":122}," th:text",[62,60723,146],{"class":72},[62,60725,60726],{"class":1675},"\"${pageTitle}\"",[62,60728,15857],{"class":72},[62,60730,4168],{"class":1780},[62,60732,1784],{"class":72},[62,60734,60735,60737,60739,60742,60744],{"class":64,"line":167},[62,60736,1789],{"class":72},[62,60738,22],{"class":1780},[62,60740,60741],{"class":72},">This is my Thymeleaf template located at /resources/templates/index.html\u003C/",[62,60743,22],{"class":1780},[62,60745,1784],{"class":72},[62,60747,60748,60750,60752],{"class":64,"line":173},[62,60749,1818],{"class":72},[62,60751,11414],{"class":1780},[62,60753,1784],{"class":72},[62,60755,60756,60758,60760],{"class":64,"line":179},[62,60757,1818],{"class":72},[62,60759,15775],{"class":1780},[62,60761,1784],{"class":72},[22,60763,60764],{},"Running the application should produce the following. ",[22,60766,60767],{},[653,60768],{"alt":51884,"src":60769},"./2017-04-26_08-52-04.png",[22,60771,60772],{},"_** Make sure you remove the static HTML file or that will overwrite everything as your home page. _",[26,60774,1499],{"id":1498},[22,60776,60777],{},"This can be pretty confusing, especially to new Spring Boot developers. We can have an index.html file in both the static and templates folders. We just need to think about what type of content we are trying to serve and that might clear things up for everyone. ",[22,60779,50754,60780,60782],{},[646,60781,49733],{}," What is your favorite view layer technology? _",[1527,60784,60785],{},"html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .suaqH, html code.shiki .suaqH{--shiki-default:#22863A;--shiki-dark:#85E89D;--shiki-sepia:#22863A}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .s4-Ip, html code.shiki .s4-Ip{--shiki-default:#6A737D;--shiki-dark:#6A737D;--shiki-sepia:#6A737D}html pre.shiki code .spoOn, html code.shiki .spoOn{--shiki-default:#E36209;--shiki-dark:#FFAB70;--shiki-sepia:#E36209}",{"title":57,"searchDepth":76,"depth":76,"links":60787},[60788,60792],{"id":60209,"depth":76,"text":60210,"children":60789},[60790,60791],{"id":60221,"depth":82,"text":60222},{"id":60395,"depth":82,"text":60396},{"id":1498,"depth":76,"text":1499},{"_id":60794,"path":60795,"title":60796,"description":60796,"meta":60797,"body":60802},"content/blog/2017/04/24/spring-boot-2-0-roadmap.md","/blog/2017/04/24/spring-boot-2-0-roadmap","Spring Boot 2.0 Roadmap",{"slug":60798,"date":60799,"published":13,"tags":60800,"author":-1,"cover":60801,"excerpt":-1},"spring-boot-2-0-roadmap","2017-04-24T08:00:05-04:00",[11002],"./annie-spratt-161511-760x1140.jpg",{"type":19,"value":60803,"toc":61009},[60804,60807,60811,60826,60831,60834,60840,60843,60864,60873,60877,60880,60882,60888,60891,60899,60903,60906,60912,60916,60923,60925,60929,60933,60946,60948,60950,60955,60957,60962,60964,60969,60971,60975,60978,60981,60987,60991,60997,60999,61002],[22,60805,60806],{},"I am a huge fan of open source projects but I love a well-maintained project even more. I like to know when milestones are scheduled to be released and what are some of the features we can expect in them. The Spring Boot team does a very good job of making this information available to the public. Thanks to that we have a pretty good idea of when Spring Boot 2.0 is going to land and some of the features that it will include. In this article, I want to talk about that roadmap and share with you a pretty big announcement. ",[26,60808,60810],{"id":60809},"spring-boot-20-milestones","Spring Boot 2.0 Milestones",[22,60812,60813,60814,60819,60820,60825],{},"The current date is April 24, 2017, just so you understand my perspective looking at this roadmap. Obviously, if you look at it months from now it might look a little different so please keep that in mind. If you head over to the ",[677,60815,60818],{"href":60816,"rel":60817},"https://github.com/spring-projects/spring-boot/",[681],"Github project page"," you will have access to all of the source code as well as the readme to get you started. If you click on the issues tab though and then ",[677,60821,60824],{"href":60822,"rel":60823},"https://github.com/spring-projects/spring-boot/milestones",[681],"click on milestones"," you will be looking at something that looks like this. ",[22,60827,60828],{},[653,60829],{"alt":2925,"src":60830},"./2017-04-21_17-00-48-300x276.png",[22,60832,60833],{},"What you are looking at here is a list of milestone releases. Clicking on the release, in this case, 2.0.0.M1 you will be shown a list of all that tasks that belong to that milestone. ",[22,60835,60836],{},[653,60837],{"alt":60838,"src":60839},"Spring Boot Milestones","./2017-04-21_17-03-39-300x269.png",[22,60841,60842],{},"From here you can see that there is a due date of May 2 and that it is 72% completed. This gives you some awesome insight into what the Spring Boot team is working on in each release and how far along they are. The big milestones that I care about are the release candidates (RC) because this means we are getting real close to the actual release. ",[915,60844,60845,60853,60861],{},[37,60846,60847,60852],{},[677,60848,60851],{"href":60849,"rel":60850},"https://github.com/spring-projects/spring-boot/milestone/56",[681],"2.0.0.RC1"," (October 23, 2017)",[37,60854,60855,60860],{},[677,60856,60859],{"href":60857,"rel":60858},"https://github.com/spring-projects/spring-boot/milestone/80",[681],"2.0.0.RC2"," (November 9, 2017) ",[37,60862,60863],{},"2.0.0.RELEASE (December 4th???)",[22,60865,60866,60867,60872],{},"If we look at this timeline and then take a look at what is around the corner can take a good guess at when Spring 2.0 will be released. The ",[677,60868,60871],{"href":60869,"rel":60870},"https://springoneplatform.io/",[681],"SpringOne conference"," takes place in San Francisco on December 4th. It is safe to assume we will probably see the release the night before or on December 4th. So, if you haven't already booked your ticket for SpringOne you might want to start thinking about doing so soon.",[26,60874,60876],{"id":60875},"spring-20-features","Spring 2.0 Features",[22,60878,60879],{},"Please remember that I am not on the Spring Boot Team and anything written here is purely based off of information I found. This means that something might change by the time we actually get to the release. I am pulling this information from the milestones, release notes and anything that I can find on the interwebs. ",[636,60881,55110],{"id":55109},[22,60883,55113,60884,60887],{},[677,60885,55118],{"href":55116,"rel":60886},[681]," for details. This is probably the biggest feature of the release and I know many of use have been looking forward to this release for awhile now. Spring Framework 5.0 brings with it a bunch of new features but none bigger than their Reactive initiative. ",[636,60889,60890],{"id":54770},"Java 8 Baseline",[22,60892,60893,60894,57647],{},"Spring Boot 2.0 is going to require that you run Java 8. This means that Java 6 & 7 will no longer be supported. My guess is that one of the 1.5.x releases will be the last version to support this. I also am excited that ",[677,60895,60898],{"href":60896,"rel":60897},"https://github.com/spring-projects/spring-boot/issues/7226",[681],"Java 9 is going to be supported as well",[636,60900,60902],{"id":60901},"new-gradle-plugin","New Gradle Plugin",[22,60904,60905],{},"Spring Boot’s Gradle plugin no longer automatically applies the dependency management plugin. Instead, Spring Boot’s plugin now reacts to the dependency management plugin being applied by importing the correct version of the spring-boot-dependencies bom. This gives you more control over how and when dependency management is configured.",[22,60907,60908],{},[677,60909,60910],{"href":60910,"rel":60911},"https://spring.io/blog/2017/04/05/spring-boot-s-new-gradle-plugin",[681],[636,60913,60915],{"id":60914},"thymeleaf-3","Thymeleaf 3",[22,60917,60918,60919,2755],{},"Spring Boot 2.0 will now support Thymeleaf 3. This will be an update to both the Thymeleaf & Spring Security Thymeleaf dependencies. If you want to read more about Thymeleaf 3 you can do so ",[677,60920,8441],{"href":60921,"rel":60922},"http://www.thymeleaf.org/doc/articles/thymeleaf3migration.html",[681],[636,60924,54780],{"id":54777},[22,60926,60927,54785],{},[59,60928,54780],{},[636,60930,60932],{"id":60931},"junit-5","JUnit 5",[22,60934,60935,60936,60939,60940,60945],{},"This is probably a huge ",[4534,60937,60938],{},"maybe"," at this point. There is a ",[677,60941,60944],{"href":60942,"rel":60943},"https://github.com/spring-projects/spring-boot/issues/6402",[681],"task to investigate"," if an upgrade to JUnit 5 makes sense. ",[636,60947,54913],{"id":54908},[22,60949,54916],{},[636,60951,60952],{"id":54919},[677,60953,54924],{"href":54922,"rel":60954},[681],[22,60956,54927],{},[636,60958,60959],{"id":54930},[677,60960,54935],{"href":54933,"rel":60961},[681],[22,60963,54938],{},[636,60965,60966],{"id":54941},[677,60967,54946],{"href":54944,"rel":60968},[681],[22,60970,54949],{},[636,60972,60974],{"id":60973},"bug-fixes-feature-enhancements","Bug Fixes & Feature Enhancements",[22,60976,60977],{},"There is plenty more to come and I will try and keep this article up to date as I find them.",[26,60979,60980],{"id":55336},"Spring 2.0 Course",[22,60982,60983,60984,55345],{},"If you haven't already had a chance to check out my ",[677,60985,51879],{"href":55343,"rel":60986},[681],[22,60988,60989],{},[653,60990],{"alt":60980,"src":55351},[22,60992,55354,60993,60996],{},[677,60994,55359],{"href":55357,"rel":60995},[681]," and signup for updates. Anyone on this list will be the first to find out when it's released and will receive a discount. ",[26,60998,1499],{"id":1498},[22,61000,61001],{},"A lot of exciting things happening around Spring Boot 2.0. You have the release itself, The Spring One Conference and now a course to along with it. Invest in yourself and some pretty amazing things can happen. Take care friends! ",[22,61003,61004],{},[4534,61005,61006,61008],{},[646,61007,49733],{}," What are you looking forward to in Spring Framework 5.0 & Spring Boot 2.0?",{"title":57,"searchDepth":76,"depth":76,"links":61010},[61011,61012,61025,61026],{"id":60809,"depth":76,"text":60810},{"id":60875,"depth":76,"text":60876,"children":61013},[61014,61015,61016,61017,61018,61019,61020,61021,61022,61023,61024],{"id":55109,"depth":82,"text":55110},{"id":54770,"depth":82,"text":60890},{"id":60901,"depth":82,"text":60902},{"id":60914,"depth":82,"text":60915},{"id":54777,"depth":82,"text":54780},{"id":60931,"depth":82,"text":60932},{"id":54908,"depth":82,"text":54913},{"id":54919,"depth":82,"text":54924},{"id":54930,"depth":82,"text":54935},{"id":54941,"depth":82,"text":54946},{"id":60973,"depth":82,"text":60974},{"id":55336,"depth":76,"text":60980},{"id":1498,"depth":76,"text":1499},{"_id":61028,"path":61029,"title":61030,"description":61030,"meta":61031,"body":61037},"content/blog/2017/04/21/how-do-you-define-success.md","/blog/2017/04/21/how-do-you-define-success","How do you Define Success as a Software Developer",{"slug":61032,"date":61033,"published":13,"tags":61034,"author":-1,"cover":61036,"excerpt":-1},"how-do-you-define-success","2017-04-21T10:00:07-04:00",[61035],"programming","./william-stitt-224301-760x1140.jpg",{"type":19,"value":61038,"toc":61113},[61039,61042,61046,61049,61055,61059,61062,61066,61069,61073,61076,61082,61086,61089,61095,61098,61100,61108],[22,61040,61041],{},"I had a personal situation that transpired yesterday and while I won't get into the details of that here, It did inspire me to write this article. What I do want to share with you is the amazing advice a person in a position of leadership gave to a friend. There was one person involved that was forced into questioning where they were in their career and whether or not they were successful. The person on the other end of the conversation gave some excellent advice and this is what you can expect from great leaders. He asked, \"How do you define success?\". This is a fantastic question and I want to dive into this today from a Software Engineer perspective.",[26,61043,61045],{"id":61044},"my-background","My Background",[22,61047,61048],{},"I want to give you a little background on me so you can understand where my perspective comes from. I am going to date myself a little but I am currently 38 years old. When I graduated high school way back in 1996 I got my first computer. I quickly fell in love with this machine and when It would break I would buy parts for it and learned how to fix it myself. I knew within a year of owning that thing that I was destined for a career in computers, but I had no idea what that looked like or how I was going to get there. ",[22,61050,61051],{},[653,61052],{"alt":61053,"src":61054},"Dell Old Days","./Dell_XPS_T600R-223x300.jpg",[636,61056,61058],{"id":61057},"education","Education",[22,61060,61061],{},"I was a pretty solid student in high school and math was by far my favorite subject. Like most students, I could have done much better if I applied myself but I just wasn't interested. When I was 18 I was living on my own and working full time as a landscaper. I know that I didn't have the means or desire to go to a 4-year college. I could learn a whole ton about computers there but I also realized that I would learn a lot of stuff I didn't care about and when I was finished I would be swimming in a pile of debt. So I opted to go the 2-year technical college route and I think it was a smart one. I would work during the day and go to school at night for 4 hours 4 days a week. Looking back on that it was some pretty amazing dedication and hard work on my part. ",[636,61063,61065],{"id":61064},"career","Career",[22,61067,61068],{},"While I was going to school I started teaching myself web development. My very first \"customer\" was a friend of mine who owned his own business. I built a full-scale e-commerce application from scratch. For my second gig, in the most unlikely corners of the internet, a chat room, I picked up a pretty large gig for an online luggage company that needed a new eCommerce solution. I didn't realize it then but I discovered an important concept of education at that age. You aren't going to learn everything you can from a book or an instructor. You have to take what you learn and apply it to the real world. I don't consider the technical school a success but the initiative I took during those years was one. The next 15 - 18 years of my life was spent moving from one great company to the next. They all played a vital role in my career development and I am thankful for all of them. What I do remember about this time period and even as I sat here today writing this article is that I never lost my thirst for knowledge. ",[26,61070,61072],{"id":61071},"what-can-i-do","What can I do? ",[22,61074,61075],{},"So what can you do as an aspiring developer? You are going to need some combination of schooling and self-taught education. If you think a 4-year college is right for you I am not one to talk someone out of an education. What I would say is make sure your vision of the end goal aligns with what you're going to school for. To me, this means that if you want to be an iOS developer a 4-year college doesn't make sense. You can accomplish this in far less time for far less money. If you want to study computer science at Harvard as well as a business because you're going to run the next facebook, then, by all means, look into that. There are great Bootcamp style school's popping up everywhere. A Bootcamp style school offers to teach you a certain skill, like \"Learn how to program in Java in 14 weeks\". These are focused classes and if they were around when I was getting into programming I would have jumped all over this. If you're in the Cleveland or Columbus area I would highly recommend you check out ",[22,61077,61078],{},[677,61079,61080],{"href":61080,"rel":61081},"http://www.techelevator.com",[681],[26,61083,61085],{"id":61084},"success","Success ",[22,61087,61088],{},"So now we come full circle back to the question of \"How do we define success as a Software Developer?\". When you look back on your career are you going to consider it a success because of the amount of money you made? Will you define success because of what your friends and family think you have accomplished? For me, success is always going to be defined as \"Are you happy?\". I can tell you that from a career perspective (It's not over yet) that I am very happy. I'm happy because I have busted my ass and I am continually learning to improve my craft. Happiness to me is remembering people, even myself, telling me that I couldn't become a Software Developer. I am happy because once I started believing in myself I accomplished something that I set out to do. I get that being a programmer is glamorous but ever since I started playing with that first computer I knew this is where I wanted to be. ",[22,61090,61091],{},[653,61092],{"alt":61093,"src":61094},"Success","./william-stitt-111353-1024x683.jpg",[22,61096,61097],{},"If you're parents, friends or family don't think of you as a success because you're not working at Google or Facebook don't listen to them. If your goal is to work at Google or Facebook then bust your tail and make it happen. Last but not least and I can't stress this enough, is money. Too many people get caught up in the notion that success is defined by your paycheck. I know plenty of people who make a ton of money and are miserable. If your goal is to make more money you can certainly achieve that goal but don't let that be the deciding factor in your pursuit of your career goals. ",[26,61099,1499],{"id":1498},[22,61101,61102,61103,57647],{},"At the end of the day that leaders advice was just what my friend needed to hear. It reminded them of all the opportunities they do have and not of the ones they don't. It reminded them of how happy they are on a day to day basis and how important those things are in life. If you are passionate about what you do and you continue to learn there will always be a place for you. Software Developers are in demand which means you get to write your future however you see fit. Work your tail off, continue to educate yourself and enjoy what you do. If you do these things, you can wake up every morning with a smile on your face... ",[4534,61104,61105],{},[646,61106,61107],{},"And life is too short not to smile to my friends",[22,61109,50754,61110,61112],{},[646,61111,49733],{}," What advice would you have for aspiring Software Developers? _",{"title":57,"searchDepth":76,"depth":76,"links":61114},[61115,61119,61120,61121],{"id":61044,"depth":76,"text":61045,"children":61116},[61117,61118],{"id":61057,"depth":82,"text":61058},{"id":61064,"depth":82,"text":61065},{"id":61071,"depth":76,"text":61072},{"id":61084,"depth":76,"text":61085},{"id":1498,"depth":76,"text":1499},{"_id":61123,"path":61124,"title":61125,"description":61125,"meta":61126,"body":61130},"content/blog/2017/04/19/what-is-jhipster.md","/blog/2017/04/19/what-is-jhipster","What is JHipster & Why you need to start using it today!",{"slug":61127,"date":61128,"published":13,"tags":61129,"author":-1,"cover":54584,"excerpt":-1},"what-is-jhipster","2017-04-19T09:38:31-04:00",[56,11002],{"type":19,"value":61131,"toc":61298},[61132,61154,61157,61168,61171,61174,61185,61189,61201,61221,61224,61230,61234,61237,61243,61247,61250,61256,61260,61263,61268,61275,61277,61279,61283,61288,61290,61293],[22,61133,61134,61135,61138,61139,61144,61145,61149,61150,61153],{},"In today's post, I want to talk to you about one of my favorite open source projects around, ",[677,61136,54600],{"href":54598,"rel":61137},[681],". In short, JHipster is a ",[677,61140,61143],{"href":61141,"rel":61142},"http://yeoman.io/",[681],"Yeoman Generator"," used to create a ",[677,61146,2925],{"href":61147,"rel":61148},"http://projects.spring.io/spring-boot/",[681]," and ",[677,61151,49752],{"href":50782,"rel":61152},[681]," project. When you get past building sample and demo projects there is a lot of different technologies and stacks that go into building a scalable, complete and modern web application.",[22,61155,61156],{},"The goal of the project is to generate for you a complete and modern Web app, unifying:",[915,61158,61159,61162,61165],{},[37,61160,61161],{},"A high-performance and robust Java stack on the server side with Spring Boot",[37,61163,61164],{},"A sleek, modern, mobile-first front-end with Angular and Bootstrap",[37,61166,61167],{},"A powerful workflow to build your application with Yeoman, Webpack/Gulp and Maven/Gradle",[26,61169,61170],{"id":61127},"What is JHipster",[22,61172,61173],{},"JHipster in simple terms is a way to generate a project around some of my favorite technologies. There are generally 3 sides of a story in every modern web application.",[915,61175,61176,61179,61182],{},[37,61177,61178],{},"What does the server side stack look like?",[37,61180,61181],{},"What does the front end stack look like? ",[37,61183,61184],{},"How do we deploy our application? ",[636,61186,61188],{"id":61187},"server-side","Server Side",[22,61190,61191,61192,61195,61196,61200],{},"If you follow me at all you know that I am a huge fan of ",[677,61193,2925],{"href":61147,"rel":61194},[681],", so much so that ",[677,61197,61199],{"href":55343,"rel":61198},[681],"I teach a course on it",". It isn't enough to just understand Spring Boot though when it comes to building complete and modern web applications. When we begin building the back end we start asking questions like:",[915,61202,61203,61206,61209,61212,61215,61218],{},[37,61204,61205],{},"What does the security model look like?",[37,61207,61208],{},"What does the data layer look like?",[37,61210,61211],{},"Will we need a distributed caching system?",[37,61213,61214],{},"Is this application scalable? ",[37,61216,61217],{},"How can we provide API documentation?",[37,61219,61220],{},"How can we make sure that we are testing our application and that we have sufficient code coverage?",[22,61222,61223],{},"When we start looking at the Server Side Options we are talking about technologies like these. ",[22,61225,61226],{},[653,61227],{"alt":61228,"src":61229},"Jhipster Server Side","./2017-04-19_09-04-28.png",[636,61231,61233],{"id":61232},"front-end","Front End",[22,61235,61236],{},"We spent a lot of time thinking about the back end of the project but now we need to make sure it's functional. Angular is one of the most popular front-end technologies around but it doesn't come without some confusion. There are all these different versions and they seem drastically different. Lucky for you JHipster is going to support all of them. We also want to create a web application that looks good using the latest technologies in HTML 5, CSS 3 & Bootstrap. There are also build tools on the front end to think about. How are we going to test that front end code? When we think about all of these technologies these are our options on the front end. ",[22,61238,61239],{},[653,61240],{"alt":61241,"src":61242},"Jhipster Frontend","./2017-04-19_09-08-48.png",[636,61244,61246],{"id":61245},"deployment-options","Deployment Options",[22,61248,61249],{},"Now that we have our application built we need to deploy it. JHipster gives us a bunch of options for easily deploying our applications. ",[22,61251,61252],{},[653,61253],{"alt":61254,"src":61255},"Jhipster Deployment","./2017-04-19_09-07-05.png",[26,61257,61259],{"id":61258},"why-jhipster","Why JHipster",[22,61261,61262],{},"I hope you are starting to see why JHipster is needed. Sure you could go out and piece together all of these technologies but why on earth would you want to do that. The number of hours it would take to make all of those play together nicely would blow you away. That doesn't sound like fun at all so I will stick to using JHipster. I fire up a command line and type \"yo jhipster\" and I am presented with a list of options of how my application is going to look. ",[22,61264,61265],{},[653,61266],{"alt":54600,"src":61267},"./2017-04-19_09-22-57-1024x567.png",[22,61269,61270,61271,2755],{},"If you want to see what a sample JHipster application looks like without installing it you can ",[677,61272,36806],{"href":61273,"rel":61274},"https://github.com/jhipster/jhipster-sample-app",[681],[26,61276,58447],{"id":54657},[22,61278,54661],{},[22,61280,61281],{},[653,61282],{"alt":54666,"src":54667},[22,61284,61285],{},[677,61286,54674],{"href":54672,"rel":61287},[681],[26,61289,1499],{"id":1498},[22,61291,61292],{},"We will get into how to install JHipster in future posts and how to use it but I wanted to start off here with a gentle introduction to the project. If you are using JHipster I want to hear from you. Please let me know what questions you have in building Spring & Angular applications. ",[22,61294,50754,61295,61297],{},[646,61296,49733],{}," What are some of your favorite open source projects? _",{"title":57,"searchDepth":76,"depth":76,"links":61299},[61300,61305,61306,61307],{"id":61127,"depth":76,"text":61170,"children":61301},[61302,61303,61304],{"id":61187,"depth":82,"text":61188},{"id":61232,"depth":82,"text":61233},{"id":61245,"depth":82,"text":61246},{"id":61258,"depth":76,"text":61259},{"id":54657,"depth":76,"text":58447},{"id":1498,"depth":76,"text":1499},{"_id":61309,"path":61310,"title":61311,"description":61311,"meta":61312,"body":61317},"content/blog/2017/04/17/java-9.md","/blog/2017/04/17/java-9","Getting Started with Java 9",{"slug":61313,"date":61314,"published":13,"tags":61315,"author":-1,"cover":61316,"excerpt":-1},"java-9","2017-04-17T11:05:58-04:00",[56],"./Java9-760x428.png",{"type":19,"value":61318,"toc":61460},[61319,61322,61326,61346,61358,61365,61372,61376,61379,61402,61411,61415,61418,61424,61428,61431,61437,61439,61453],[22,61320,61321],{},"I don't know about you but I love to play around with pre-releases of software. There is something really fun being one of the first to use a new feature and learn how the new release is going to make our lives as developers a little bit easier. Java 9 is coming soon and I am really looking forward to some of the new features. Java 9 has had a couple of delays to date but as I am writing this article (April 2017), it is set to be released in July 2017. There are so many great features in Java 9 and you don't have to wait until July to start checking them out today. In this article, we will go through a quick tutorial on where to get Java 9 and how to get yourself setup. ",[26,61323,61325],{"id":61324},"installing-java-9","Installing Java 9",[22,61327,61328,61329,61333,61334,61339,61340,61345],{},"The first thing we need to do if we want to use Java 9 is to install it. You will head over to ",[677,61330,61331],{"href":61331,"rel":61332},"https://jdk9.java.net",[681]," and bookmark this site as it contains a bunch of great information and links about Java 9. From there you are going to click on ",[646,61335,61336],{},[4534,61337,61338],{},"Downloads"," in the upper left-hand menu. The build may be a different one if you're reading this at a later date but that is OK. Accept the user agreement and then download the correct version of the ",[4534,61341,61342],{},[646,61343,61344],{},"JDK"," for your platform. The Installation is pretty straight forward so just follow the instructions. ",[22,61347,61348,61352,61353,61357],{},[653,61349],{"alt":61350,"src":61351},"Java 9 Downloads","./2017-04-17_09-02-12-1024x448.png"," If you follow me at all you might know what a huge fan of ",[677,61354,61356],{"href":61355},"./http://sdkman.io/","SDKMan"," I am. What you might not know is that you can use SDKMan to manage your Java installs as well now. It is now easier than ever to install and switch between versions of Java. If we run the command \"sdk java list\" we can see that there is a new version for me to install. ",[22,61359,61360,61364],{},[653,61361],{"alt":61362,"src":61363},"SDK Install Java 9","./2017-04-17_09-10-58-1024x498.png"," Now I can run the install command and SDK will pull down that version, make it my default and add the appropriate environment variables. ",[22,61366,61367,61371],{},[653,61368],{"alt":61369,"src":61370},"Installing Java 9 from SDKMan","./2017-04-17_09-16-10-1024x498.png"," It seriously couldn't be any easier! ",[26,61373,61375],{"id":61374},"java-9-new-features","Java 9 New Features",[22,61377,61378],{},"Now that you have Java 9 installed what are the new features we can start to play with? If you have heard any news yet about Java 9 you have probably heard about Modularity. The most significant JDK 9 enhancement is the Java Platform Module System, which divides the JDK into a set of modules. This isn't the only new feature though, here are a list of my favorites. ",[915,61380,61381,61384,61387,61390,61393,61396,61399],{},[37,61382,61383],{},"Module System",[37,61385,61386],{},"Unified JVM Logging",[37,61388,61389],{},"Java 9 REPL (JShell)",[37,61391,61392],{},"HTTP 2 Client",[37,61394,61395],{},"Stream API Improvements",[37,61397,61398],{},"Reactive Streams",[37,61400,61401],{},"Optional Class Improvements",[22,61403,61404,61405,61410],{},"You can read all about these new features on the ",[677,61406,61409],{"href":61407,"rel":61408},"http://openjdk.java.net/projects/jdk9/",[681],"JDK 9 Project Page",". You can also subscribe to this blog as I will be writing up articles and creating video tutorials for YouTube about some of my favorite new features. ",[26,61412,61414],{"id":61413},"java-9-intellij","Java 9 & IntelliJ ",[22,61416,61417],{},"IntelliJ 2017 brings up Java 9 support. When you open an existing project or create a brand new you just want to make sure you are using the correct version of Java. In your project go to File > Project Settings. Make sure that you are using the Java 9-ea (Early Access) SDK and that the language level is set to Java 9 as well. ",[22,61419,61420],{},[653,61421],{"alt":61422,"src":61423},"IntelliJ Java 9 Support","./2017-04-17_10-02-29-1024x602.png",[26,61425,61427],{"id":61426},"getting-started-with-java-9-screencast","Getting Started with Java 9 Screencast",[22,61429,61430],{},"I created a screencast that goes through the information we mentioned in this article. If you're not subscribed to my YouTube channel please do so now so that you can get these videos before they hit my blog. ",[22,61432,61433],{},[677,61434,61435],{"href":61435,"rel":61436},"https://www.youtube.com/watch?v=D-O7eDbdcKg",[681],[26,61438,1499],{"id":1498},[22,61440,61441,61442,61447,61448,57647],{},"This is a great time to start playing with the new features in Java 9. I am working on a curriculum for a ",[646,61443,61444],{},[4534,61445,61446],{},"What's new in Java 9 Course",". If you would be interested in learning more about that please ",[677,61449,61452],{"href":61450,"rel":61451},"https://danvega.dev/java-9",[681],"head over to the course landing page",[22,61454,61455],{},[4534,61456,61457,61459],{},[646,61458,49733],{}," What are you most looking forward to in Java 9?",{"title":57,"searchDepth":76,"depth":76,"links":61461},[61462,61463,61464,61465,61466],{"id":61324,"depth":76,"text":61325},{"id":61374,"depth":76,"text":61375},{"id":61413,"depth":76,"text":61414},{"id":61426,"depth":76,"text":61427},{"id":1498,"depth":76,"text":1499},{"_id":61468,"path":61469,"title":61470,"description":61471,"meta":61472,"body":61477},"content/blog/2017/04/10/inserting-a-groovy-date-into-a-time-stamp-column.md","/blog/2017/04/10/inserting-a-groovy-date-into-a-time-stamp-column","Inserting a Groovy Date into a Time Stamp Column","Inserting a Groovy Date into a Timestamp Column",{"slug":61473,"date":61474,"published":13,"tags":61475,"author":-1,"cover":61476,"excerpt":-1},"inserting-a-groovy-date-into-a-time-stamp-column","2017-04-10T08:00:06-04:00",[53536],"./GroovyDate-760x428.png",{"type":19,"value":61478,"toc":61654},[61479,61482,61501,61504,61571,61574,61643,61646,61651],[22,61480,61481],{},"Working with Dates in any language is one of those core fundamentals you need to know right away. Lucky for us, Groovy makes it super simple to work with dates. I am working on a project where I am using straight SQL to insert a record into a database using Groovy. It's pretty darn easy in most languages to grab the current date/time and in Java, you can do so just by creating a new instance of the Date class.",[52,61483,61485],{"className":54,"code":61484,"language":56,"meta":57,"style":57},"Date now = new Date();\n",[59,61486,61487],{"__ignoreMap":57},[62,61488,61489,61492,61494,61496,61499],{"class":64,"line":65},[62,61490,61491],{"class":72},"Date now ",[62,61493,146],{"class":68},[62,61495,466],{"class":68},[62,61497,61498],{"class":122}," Date",[62,61500,822],{"class":72},[22,61502,61503],{},"The problem with this (and the same goes for other languages) is that you can't insert that value into a timestamp column. I need to stick this date/time into a timestamp column and to do so in most languages you need to format this so it matches up to what a timestamp column expects. With this simple Java example, we need to bring in another package to do formatting, create a formatting object with the correct pattern and then format our date. Not the hardest thing in the world to do but certainly something I don't care to do.",[52,61505,61507],{"className":54,"code":61506,"language":56,"meta":57,"style":57},"import java.text.SimpleDateFormat;\n\nDate now = new Date()\nSimpleDateFormat timestamp = new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\");\n\n// 2017-04-10 08:00:00\nprintln timestamp.format(now)\n",[59,61508,61509,61516,61520,61532,61551,61555,61560],{"__ignoreMap":57},[62,61510,61511,61513],{"class":64,"line":65},[62,61512,27875],{"class":68},[62,61514,61515],{"class":72}," java.text.SimpleDateFormat;\n",[62,61517,61518],{"class":64,"line":76},[62,61519,79],{"emptyLinePlaceholder":13},[62,61521,61522,61524,61526,61528,61530],{"class":64,"line":82},[62,61523,61491],{"class":72},[62,61525,146],{"class":68},[62,61527,466],{"class":68},[62,61529,61498],{"class":122},[62,61531,2223],{"class":72},[62,61533,61534,61537,61539,61541,61544,61546,61549],{"class":64,"line":89},[62,61535,61536],{"class":72},"SimpleDateFormat timestamp ",[62,61538,146],{"class":68},[62,61540,466],{"class":68},[62,61542,61543],{"class":122}," SimpleDateFormat",[62,61545,2109],{"class":72},[62,61547,61548],{"class":1675},"\"yyyy-MM-dd HH:mm:ss\"",[62,61550,1133],{"class":72},[62,61552,61553],{"class":64,"line":95},[62,61554,79],{"emptyLinePlaceholder":13},[62,61556,61557],{"class":64,"line":101},[62,61558,61559],{"class":85},"// 2017-04-10 08:00:00\n",[62,61561,61562,61565,61568],{"class":64,"line":107},[62,61563,61564],{"class":72},"println timestamp.",[62,61566,61567],{"class":122},"format",[62,61569,61570],{"class":72},"(now)\n",[22,61572,61573],{},"Luckily I am using Groovy on this project. If you haven't already played around with Groovy it seems to remove the annoyances of Java by adding on to the API. Groovy adds a convenient method to all Date objects for converting a date to a timestamp. If you print out the class names you will also see that it's not just converting it into a formatted string but an actual timestamp object. No extra libraries needed and I don't have to remember what a timestamp format looks like.",[52,61575,61577],{"className":54,"code":61576,"language":56,"meta":57,"style":57},"def now = new Date()\ndef timestamp = now.toTimestamp()\n\nprintln now // Mond Apr 10 08:00:00 EST 2017\nprintln now.class.name // java.util.Date\nprintln timestamp // 2017-04-10 08:00:00.00\nprintln timestamp.class.name // java.sql.Timestamp\n",[59,61578,61579,61592,61607,61611,61619,61627,61635],{"__ignoreMap":57},[62,61580,61581,61584,61586,61588,61590],{"class":64,"line":65},[62,61582,61583],{"class":72},"def now ",[62,61585,146],{"class":68},[62,61587,466],{"class":68},[62,61589,61498],{"class":122},[62,61591,2223],{"class":72},[62,61593,61594,61597,61599,61602,61605],{"class":64,"line":76},[62,61595,61596],{"class":72},"def timestamp ",[62,61598,146],{"class":68},[62,61600,61601],{"class":72}," now.",[62,61603,61604],{"class":122},"toTimestamp",[62,61606,2223],{"class":72},[62,61608,61609],{"class":64,"line":82},[62,61610,79],{"emptyLinePlaceholder":13},[62,61612,61613,61616],{"class":64,"line":89},[62,61614,61615],{"class":72},"println now ",[62,61617,61618],{"class":85},"// Mond Apr 10 08:00:00 EST 2017\n",[62,61620,61621,61624],{"class":64,"line":95},[62,61622,61623],{"class":72},"println now.class.name ",[62,61625,61626],{"class":85},"// java.util.Date\n",[62,61628,61629,61632],{"class":64,"line":101},[62,61630,61631],{"class":72},"println timestamp ",[62,61633,61634],{"class":85},"// 2017-04-10 08:00:00.00\n",[62,61636,61637,61640],{"class":64,"line":107},[62,61638,61639],{"class":72},"println timestamp.class.name ",[62,61641,61642],{"class":85},"// java.sql.Timestamp\n",[22,61644,61645],{},"To me, this is just one in the land of many examples of how Groovy makes programming in Java fun.",[22,61647,50754,61648,61650],{},[646,61649,49733],{}," What are some of the other ways Groovy makes your life easier? _",[1527,61652,61653],{},"html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}html pre.shiki code .s4-Ip, html code.shiki .s4-Ip{--shiki-default:#6A737D;--shiki-dark:#6A737D;--shiki-sepia:#6A737D}",{"title":57,"searchDepth":76,"depth":76,"links":61655},[],{"_id":61657,"path":61658,"title":61659,"description":61659,"meta":61660,"body":61665},"content/blog/2017/04/07/spring-boot-command-line-runner.md","/blog/2017/04/07/spring-boot-command-line-runner","Spring Boot Command Line Runner",{"slug":61661,"date":61662,"published":13,"tags":61663,"author":-1,"cover":61664,"excerpt":-1},"spring-boot-command-line-runner","2017-04-07T08:00:43-04:00",[11002],"./spring-boot-command-line-runner-thumbnail.png",{"type":19,"value":61666,"toc":62906},[61667,61670,61678,61681,61685,61692,61695,61701,61704,61708,61714,61720,61727,61822,61833,61870,61883,61982,61989,62182,62190,62196,62200,62212,62301,62307,62470,62474,62477,62679,62877,62880,62886,62888,62896,62903],[22,61668,61669],{},"A student asked me the other day what a Command Line Runner was and when can we use one. If you watch demos of Spring Boot features or applications you have probably seen this and said to yourself \"What the heck is that?\". If you take a look a look at the API Documentation for Command Line Runner you will get this elegant explanation.",[29685,61671,61672],{},[22,61673,61674,61675,61677],{},"Interface used to indicate that a bean should ",[4534,61676,2127],{}," when it is contained within a SpringApplication. Multiple CommandLineRunner beans can be defined within the same application context and can be ordered using the Ordered interface or @Order annotation.",[22,61679,61680],{},"In this article, I will explain what a Command Line Runner is and what you might use it for in your next Spring Boot application.",[26,61682,61684],{"id":61683},"command-line-runner","Command Line Runner",[22,61686,61687,61688,61691],{},"A command-line runner is an interface that you can implement to execute some code before the application starts up. If we define an implementation of the Command Line Runner interface you will need to override the ",[59,61689,61690],{},"run()"," method. This method will be executed after the application context is loaded and right before the Spring Application run method is completed.",[22,61693,61694],{},"Start by creating a new application with the Spring Initalizr and select the following dependencies:",[22,61696,61697],{},[653,61698],{"alt":61699,"src":61700},"Spring Boot Init","./spring-boot-init.png",[22,61702,61703],{},"Before we dive in and create one we should talk about some of the things you might want to use this for. Remember that this going to execute after the application context is loaded so you could use it to check if certain beans exist or what values of certain properties are. Another reason to use it is to load some data right before your application fires up. I will say that I have used a Command Line Runner for these purposes before but they were both in development or demo environments.",[636,61705,61707],{"id":61706},"command-line-runner-demo","Command Line Runner Demo",[22,61709,61710,61711,61713],{},"In this demo, you are going to create a DataLoader class that is going to load some initial demo data into your application. For demo purposes you are creating a web application with JPA & H2. The first thing you ned to do is open up ",[59,61712,1265],{}," and add the following to establish a connection to the H2 database:",[52,61715,61718],{"className":61716,"code":61717,"language":1727},[1725],"spring.h2.console.enabled=true\nspring.datasource.generate-unique-name=false\nspring.datasource.name=images\n",[59,61719,61717],{"__ignoreMap":57},[22,61721,61722,61723,61726],{},"Next you will need to create a simple ",[59,61724,61725],{},"@Entity"," class named Image. This class will represent an image but the constructor simply takes a String for the image name. You aren't working any real images in this demo.",[52,61728,61730],{"className":54,"code":61729,"language":56,"meta":57,"style":57},"@Entity\n@Data\n@NoArgsConstructor\npublic class Image {\n\n @Id @GeneratedValue\n private Long id;\n private String name;\n\n public Image(String name) {\n this.name = name;\n }\n}\n",[59,61731,61732,61738,61744,61751,61762,61766,61776,61782,61788,61792,61804,61814,61818],{"__ignoreMap":57},[62,61733,61734,61736],{"class":64,"line":65},[62,61735,942],{"class":72},[62,61737,8999],{"class":68},[62,61739,61740,61742],{"class":64,"line":76},[62,61741,942],{"class":72},[62,61743,9388],{"class":68},[62,61745,61746,61748],{"class":64,"line":82},[62,61747,942],{"class":72},[62,61749,61750],{"class":68},"NoArgsConstructor\n",[62,61752,61753,61755,61757,61760],{"class":64,"line":89},[62,61754,116],{"class":68},[62,61756,119],{"class":68},[62,61758,61759],{"class":122}," Image",[62,61761,126],{"class":72},[62,61763,61764],{"class":64,"line":95},[62,61765,79],{"emptyLinePlaceholder":13},[62,61767,61768,61770,61772,61774],{"class":64,"line":101},[62,61769,2143],{"class":72},[62,61771,9016],{"class":68},[62,61773,9019],{"class":72},[62,61775,9022],{"class":68},[62,61777,61778,61780],{"class":64,"line":107},[62,61779,137],{"class":68},[62,61781,9029],{"class":72},[62,61783,61784,61786],{"class":64,"line":113},[62,61785,137],{"class":68},[62,61787,9406],{"class":72},[62,61789,61790],{"class":64,"line":129},[62,61791,79],{"emptyLinePlaceholder":13},[62,61793,61794,61796,61798,61800,61802],{"class":64,"line":134},[62,61795,194],{"class":68},[62,61797,61759],{"class":122},[62,61799,1049],{"class":72},[62,61801,3107],{"class":889},[62,61803,768],{"class":72},[62,61805,61806,61808,61810,61812],{"class":64,"line":156},[62,61807,2405],{"class":149},[62,61809,58832],{"class":72},[62,61811,146],{"class":68},[62,61813,58837],{"class":72},[62,61815,61816],{"class":64,"line":161},[62,61817,223],{"class":72},[62,61819,61820],{"class":64,"line":167},[62,61821,379],{"class":72},[22,61823,61824,61825,61828,61829,61832],{},"When your application starts up you want to load a couple of images into your database. You could place a SQL script in the resources folder, but who wants to write SQL from scratch? 🤦♂️ To persist data to the database create a new interface called ",[59,61826,61827],{},"ImageRepository"," and extend the ",[59,61830,61831],{},"CrudRepository"," interface which will provide you with CRUD methods.",[52,61834,61836],{"className":54,"code":61835,"language":56,"meta":57,"style":57},"public interface ImageRepository extends CrudRepository\u003CImage,Long> {\n\n}\n",[59,61837,61838,61862,61866],{"__ignoreMap":57},[62,61839,61840,61842,61844,61847,61849,61851,61853,61856,61858,61860],{"class":64,"line":65},[62,61841,116],{"class":68},[62,61843,8531],{"class":68},[62,61845,61846],{"class":122}," ImageRepository",[62,61848,8537],{"class":68},[62,61850,22395],{"class":122},[62,61852,760],{"class":72},[62,61854,61855],{"class":68},"Image",[62,61857,32225],{"class":72},[62,61859,6850],{"class":68},[62,61861,8552],{"class":72},[62,61863,61864],{"class":64,"line":76},[62,61865,79],{"emptyLinePlaceholder":13},[62,61867,61868],{"class":64,"line":82},[62,61869,379],{"class":72},[22,61871,61872,61873,61876,61877,61879,61880,61882],{},"Now that you have a way to persist data you can create a new class called ",[59,61874,61875],{},"DataLoader"," that will be responsible for loading the data. This class is going to implement the ",[59,61878,2066],{}," interface and override the run method. The other important thing to note here is that you need to annotate the class with ",[59,61881,55433],{}," or Spring will never find it and call the run method.",[52,61884,61886],{"className":54,"code":61885,"language":56,"meta":57,"style":57},"@Component\npublic class DataLoader implements CommandLineRunner {\n\n private final Logger logger = LoggerFactory.getLogger(DataLoader.class);\n\n @Override\n public void run(String... strings) throws Exception {\n logger.info(\"Loading data...\");\n }\n}\n",[59,61887,61888,61894,61909,61913,61931,61935,61941,61960,61974,61978],{"__ignoreMap":57},[62,61889,61890,61892],{"class":64,"line":65},[62,61891,942],{"class":72},[62,61893,12585],{"class":68},[62,61895,61896,61898,61900,61903,61905,61907],{"class":64,"line":76},[62,61897,116],{"class":68},[62,61899,119],{"class":68},[62,61901,61902],{"class":122}," DataLoader",[62,61904,13520],{"class":68},[62,61906,17592],{"class":122},[62,61908,126],{"class":72},[62,61910,61911],{"class":64,"line":82},[62,61912,79],{"emptyLinePlaceholder":13},[62,61914,61915,61917,61919,61922,61924,61926,61928],{"class":64,"line":89},[62,61916,137],{"class":68},[62,61918,458],{"class":68},[62,61920,61921],{"class":72}," Logger logger ",[62,61923,146],{"class":68},[62,61925,3066],{"class":72},[62,61927,3069],{"class":122},[62,61929,61930],{"class":72},"(DataLoader.class);\n",[62,61932,61933],{"class":64,"line":95},[62,61934,79],{"emptyLinePlaceholder":13},[62,61936,61937,61939],{"class":64,"line":101},[62,61938,2143],{"class":72},[62,61940,13555],{"class":68},[62,61942,61943,61945,61947,61949,61951,61954,61956,61958],{"class":64,"line":107},[62,61944,194],{"class":68},[62,61946,200],{"class":68},[62,61948,1716],{"class":122},[62,61950,17699],{"class":72},[62,61952,61953],{"class":889},"strings",[62,61955,5024],{"class":72},[62,61957,11501],{"class":68},[62,61959,11504],{"class":72},[62,61961,61962,61965,61967,61969,61972],{"class":64,"line":113},[62,61963,61964],{"class":72}," logger.",[62,61966,12688],{"class":122},[62,61968,2109],{"class":72},[62,61970,61971],{"class":1675},"\"Loading data...\"",[62,61973,1133],{"class":72},[62,61975,61976],{"class":64,"line":129},[62,61977,223],{"class":72},[62,61979,61980],{"class":64,"line":134},[62,61981,379],{"class":72},[22,61983,61984,61985,61988],{},"Now let's work on persisting some data into a database. In the run method you will create a list of 3 images with simple names. With a list of images you can call the repositories ",[59,61986,61987],{},"saveAll()"," method which takes an Iterable of entities.",[52,61990,61992],{"className":54,"code":61991,"language":56,"meta":57,"style":57},"@Component\npublic class DataLoader implements CommandLineRunner {\n\n private final Logger logger = LoggerFactory.getLogger(DataLoader.class);\n private final ImageRepository repository;\n\n public DataLoader(ImageRepository repository) {\n this.repository = repository;\n }\n\n @Override\n public void run(String... args) throws Exception {\n logger.info(\"Loading data...\");\n List\u003CImage> images = List.of(new Image(\"Image 1\"), new Image(\"Image 2\"), new Image(\"Image 3\"));\n repository.saveAll(images);\n }\n}\n",[59,61993,61994,62000,62014,62018,62034,62043,62047,62060,62070,62074,62078,62084,62102,62114,62164,62174,62178],{"__ignoreMap":57},[62,61995,61996,61998],{"class":64,"line":65},[62,61997,942],{"class":72},[62,61999,12585],{"class":68},[62,62001,62002,62004,62006,62008,62010,62012],{"class":64,"line":76},[62,62003,116],{"class":68},[62,62005,119],{"class":68},[62,62007,61902],{"class":122},[62,62009,13520],{"class":68},[62,62011,17592],{"class":122},[62,62013,126],{"class":72},[62,62015,62016],{"class":64,"line":82},[62,62017,79],{"emptyLinePlaceholder":13},[62,62019,62020,62022,62024,62026,62028,62030,62032],{"class":64,"line":89},[62,62021,137],{"class":68},[62,62023,458],{"class":68},[62,62025,61921],{"class":72},[62,62027,146],{"class":68},[62,62029,3066],{"class":72},[62,62031,3069],{"class":122},[62,62033,61930],{"class":72},[62,62035,62036,62038,62040],{"class":64,"line":95},[62,62037,137],{"class":68},[62,62039,458],{"class":68},[62,62041,62042],{"class":72}," ImageRepository repository;\n",[62,62044,62045],{"class":64,"line":101},[62,62046,79],{"emptyLinePlaceholder":13},[62,62048,62049,62051,62053,62056,62058],{"class":64,"line":107},[62,62050,194],{"class":68},[62,62052,61902],{"class":122},[62,62054,62055],{"class":72},"(ImageRepository ",[62,62057,23540],{"class":889},[62,62059,768],{"class":72},[62,62061,62062,62064,62066,62068],{"class":64,"line":113},[62,62063,2405],{"class":149},[62,62065,23549],{"class":72},[62,62067,146],{"class":68},[62,62069,23554],{"class":72},[62,62071,62072],{"class":64,"line":129},[62,62073,223],{"class":72},[62,62075,62076],{"class":64,"line":134},[62,62077,79],{"emptyLinePlaceholder":13},[62,62079,62080,62082],{"class":64,"line":156},[62,62081,2143],{"class":72},[62,62083,13555],{"class":68},[62,62085,62086,62088,62090,62092,62094,62096,62098,62100],{"class":64,"line":161},[62,62087,194],{"class":68},[62,62089,200],{"class":68},[62,62091,1716],{"class":122},[62,62093,17699],{"class":72},[62,62095,2117],{"class":889},[62,62097,5024],{"class":72},[62,62099,11501],{"class":68},[62,62101,11504],{"class":72},[62,62103,62104,62106,62108,62110,62112],{"class":64,"line":167},[62,62105,61964],{"class":72},[62,62107,12688],{"class":122},[62,62109,2109],{"class":72},[62,62111,61971],{"class":1675},[62,62113,1133],{"class":72},[62,62115,62116,62118,62120,62123,62125,62127,62129,62131,62133,62135,62137,62140,62142,62144,62146,62148,62151,62153,62155,62157,62159,62162],{"class":64,"line":173},[62,62117,25147],{"class":72},[62,62119,61855],{"class":68},[62,62121,62122],{"class":72},"> images ",[62,62124,146],{"class":68},[62,62126,3499],{"class":72},[62,62128,3298],{"class":122},[62,62130,2109],{"class":72},[62,62132,2426],{"class":68},[62,62134,61759],{"class":122},[62,62136,2109],{"class":72},[62,62138,62139],{"class":1675},"\"Image 1\"",[62,62141,29203],{"class":72},[62,62143,2426],{"class":68},[62,62145,61759],{"class":122},[62,62147,2109],{"class":72},[62,62149,62150],{"class":1675},"\"Image 2\"",[62,62152,29203],{"class":72},[62,62154,2426],{"class":68},[62,62156,61759],{"class":122},[62,62158,2109],{"class":72},[62,62160,62161],{"class":1675},"\"Image 3\"",[62,62163,6979],{"class":72},[62,62165,62166,62168,62171],{"class":64,"line":179},[62,62167,22559],{"class":72},[62,62169,62170],{"class":122},"saveAll",[62,62172,62173],{"class":72},"(images);\n",[62,62175,62176],{"class":64,"line":185},[62,62177,223],{"class":72},[62,62179,62180],{"class":64,"line":191},[62,62181,379],{"class":72},[22,62183,62184,62185,62189],{},"If you start the application and look at the ",[677,62186,62188],{"href":52672,"rel":62187},[681],"H2 console"," you will see the new database rows.",[22,62191,62192],{},[653,62193],{"alt":62194,"src":62195},"H2 Console","./h2-console.png",[636,62197,62199],{"id":62198},"command-line-runner-functional-interface","Command Line Runner Functional Interface",[22,62201,62202,62203,62205,62206,62208,62209,2755],{},"If you look at the source code for the ",[59,62204,2066],{}," interface you will see that it has a single method called ",[59,62207,61690],{}," and is marked as a ",[59,62210,62211],{},"@FunctionalInterface",[52,62213,62215],{"className":54,"code":62214,"language":56,"meta":57,"style":57},"@FunctionalInterface\npublic interface CommandLineRunner {\n\n /**\n * Callback used to run the bean.\n * @param args incoming main method arguments\n * @throws Exception on error\n */\n void run(String... args) throws Exception;\n\n}\n",[59,62216,62217,62224,62234,62238,62242,62247,62259,62272,62276,62293,62297],{"__ignoreMap":57},[62,62218,62219,62221],{"class":64,"line":65},[62,62220,942],{"class":72},[62,62222,62223],{"class":68},"FunctionalInterface\n",[62,62225,62226,62228,62230,62232],{"class":64,"line":76},[62,62227,116],{"class":68},[62,62229,8531],{"class":68},[62,62231,17592],{"class":122},[62,62233,126],{"class":72},[62,62235,62236],{"class":64,"line":82},[62,62237,79],{"emptyLinePlaceholder":13},[62,62239,62240],{"class":64,"line":89},[62,62241,164],{"class":85},[62,62243,62244],{"class":64,"line":95},[62,62245,62246],{"class":85}," * Callback used to run the bean.\n",[62,62248,62249,62251,62253,62256],{"class":64,"line":101},[62,62250,329],{"class":85},[62,62252,25823],{"class":68},[62,62254,62255],{"class":889}," args",[62,62257,62258],{"class":85}," incoming main method arguments\n",[62,62260,62261,62263,62266,62269],{"class":64,"line":107},[62,62262,329],{"class":85},[62,62264,62265],{"class":68},"@throws",[62,62267,62268],{"class":122}," Exception",[62,62270,62271],{"class":85}," on error\n",[62,62273,62274],{"class":64,"line":113},[62,62275,188],{"class":85},[62,62277,62278,62280,62282,62284,62286,62288,62290],{"class":64,"line":129},[62,62279,11710],{"class":68},[62,62281,1716],{"class":122},[62,62283,17699],{"class":72},[62,62285,2117],{"class":889},[62,62287,5024],{"class":72},[62,62289,11501],{"class":68},[62,62291,62292],{"class":72}," Exception;\n",[62,62294,62295],{"class":64,"line":134},[62,62296,79],{"emptyLinePlaceholder":13},[62,62298,62299],{"class":64,"line":156},[62,62300,379],{"class":72},[22,62302,62303,62304,62306],{},"This means that this is a candidate for a lambda expression. This means that instead of creating a whole new class you can just create a method and annotate it with ",[59,62305,55429],{}," so that Spring will find it. The following Command Line Runner is in the main application class and does the same thing as the class you created before.",[52,62308,62310],{"className":54,"code":62309,"language":56,"meta":57,"style":57},"@SpringBootApplication\npublic class Application {\n\n public static void main(String[] args) {\n SpringApplication.run(Application.class, args);\n }\n\n @Bean\n public CommandLineRunner runner(ImageRepository repository) {\n return args -> {\n List\u003CImage> images = List.of(new Image(\"Image 1\"), new Image(\"Image 2\"), new Image(\"Image 3\"));\n repository.saveAll(images);\n };\n }\n\n}\n",[59,62311,62312,62318,62328,62332,62352,62360,62364,62368,62374,62389,62399,62446,62454,62458,62462,62466],{"__ignoreMap":57},[62,62313,62314,62316],{"class":64,"line":65},[62,62315,942],{"class":72},[62,62317,2079],{"class":68},[62,62319,62320,62322,62324,62326],{"class":64,"line":76},[62,62321,116],{"class":68},[62,62323,119],{"class":68},[62,62325,2088],{"class":122},[62,62327,126],{"class":72},[62,62329,62330],{"class":64,"line":82},[62,62331,79],{"emptyLinePlaceholder":13},[62,62333,62334,62336,62338,62340,62342,62344,62346,62348,62350],{"class":64,"line":89},[62,62335,194],{"class":68},[62,62337,2101],{"class":68},[62,62339,200],{"class":68},[62,62341,2106],{"class":122},[62,62343,2109],{"class":72},[62,62345,973],{"class":68},[62,62347,2114],{"class":72},[62,62349,2117],{"class":889},[62,62351,768],{"class":72},[62,62353,62354,62356,62358],{"class":64,"line":95},[62,62355,2124],{"class":72},[62,62357,2127],{"class":122},[62,62359,2130],{"class":72},[62,62361,62362],{"class":64,"line":101},[62,62363,223],{"class":72},[62,62365,62366],{"class":64,"line":107},[62,62367,79],{"emptyLinePlaceholder":13},[62,62369,62370,62372],{"class":64,"line":113},[62,62371,2143],{"class":72},[62,62373,2146],{"class":68},[62,62375,62376,62378,62381,62383,62385,62387],{"class":64,"line":129},[62,62377,194],{"class":68},[62,62379,62380],{"class":72}," CommandLineRunner ",[62,62382,52603],{"class":122},[62,62384,62055],{"class":72},[62,62386,23540],{"class":889},[62,62388,768],{"class":72},[62,62390,62391,62393,62395,62397],{"class":64,"line":134},[62,62392,360],{"class":68},[62,62394,2169],{"class":72},[62,62396,800],{"class":68},[62,62398,126],{"class":72},[62,62400,62401,62404,62406,62408,62410,62412,62414,62416,62418,62420,62422,62424,62426,62428,62430,62432,62434,62436,62438,62440,62442,62444],{"class":64,"line":156},[62,62402,62403],{"class":72}," List\u003C",[62,62405,61855],{"class":68},[62,62407,62122],{"class":72},[62,62409,146],{"class":68},[62,62411,3499],{"class":72},[62,62413,3298],{"class":122},[62,62415,2109],{"class":72},[62,62417,2426],{"class":68},[62,62419,61759],{"class":122},[62,62421,2109],{"class":72},[62,62423,62139],{"class":1675},[62,62425,29203],{"class":72},[62,62427,2426],{"class":68},[62,62429,61759],{"class":122},[62,62431,2109],{"class":72},[62,62433,62150],{"class":1675},[62,62435,29203],{"class":72},[62,62437,2426],{"class":68},[62,62439,61759],{"class":122},[62,62441,2109],{"class":72},[62,62443,62161],{"class":1675},[62,62445,6979],{"class":72},[62,62447,62448,62450,62452],{"class":64,"line":161},[62,62449,52625],{"class":72},[62,62451,62170],{"class":122},[62,62453,62173],{"class":72},[62,62455,62456],{"class":64,"line":167},[62,62457,2252],{"class":72},[62,62459,62460],{"class":64,"line":173},[62,62461,223],{"class":72},[62,62463,62464],{"class":64,"line":179},[62,62465,79],{"emptyLinePlaceholder":13},[62,62467,62468],{"class":64,"line":185},[62,62469,379],{"class":72},[636,62471,62473],{"id":62472},"multiple-command-line-runners","Multiple Command Line Runners",[22,62475,62476],{},"What if you wanted to create 2 Command Line Runners that did contained completely separate logic? No problem, just use the annotation Order to specify the order in which they should run.",[52,62478,62480],{"className":54,"code":62479,"language":56,"meta":57,"style":57},"@Component\n@Order(1)\npublic class AnotherDataLoader implements CommandLineRunner {\n\n private final Logger logger = LoggerFactory.getLogger(DataLoader.class);\n private final ImageRepository repository;\n\n public AnotherDataLoader(ImageRepository repository) {\n this.repository = repository;\n }\n\n @Override\n public void run(String... args) throws Exception {\n logger.info(\"Loading data from AnotherDataLoader...\");\n List\u003CImage> images = List.of(new Image(\"Image 4\"), new Image(\"Image 5\"), new Image(\"Image 6\"));\n repository.saveAll(images);\n }\n}\n",[59,62481,62482,62488,62500,62515,62519,62535,62543,62547,62559,62569,62573,62577,62583,62601,62614,62663,62671,62675],{"__ignoreMap":57},[62,62483,62484,62486],{"class":64,"line":65},[62,62485,942],{"class":72},[62,62487,12585],{"class":68},[62,62489,62490,62492,62494,62496,62498],{"class":64,"line":76},[62,62491,942],{"class":72},[62,62493,9415],{"class":68},[62,62495,2109],{"class":72},[62,62497,6689],{"class":149},[62,62499,2212],{"class":72},[62,62501,62502,62504,62506,62509,62511,62513],{"class":64,"line":82},[62,62503,116],{"class":68},[62,62505,119],{"class":68},[62,62507,62508],{"class":122}," AnotherDataLoader",[62,62510,13520],{"class":68},[62,62512,17592],{"class":122},[62,62514,126],{"class":72},[62,62516,62517],{"class":64,"line":89},[62,62518,79],{"emptyLinePlaceholder":13},[62,62520,62521,62523,62525,62527,62529,62531,62533],{"class":64,"line":95},[62,62522,137],{"class":68},[62,62524,458],{"class":68},[62,62526,61921],{"class":72},[62,62528,146],{"class":68},[62,62530,3066],{"class":72},[62,62532,3069],{"class":122},[62,62534,61930],{"class":72},[62,62536,62537,62539,62541],{"class":64,"line":101},[62,62538,137],{"class":68},[62,62540,458],{"class":68},[62,62542,62042],{"class":72},[62,62544,62545],{"class":64,"line":107},[62,62546,79],{"emptyLinePlaceholder":13},[62,62548,62549,62551,62553,62555,62557],{"class":64,"line":113},[62,62550,194],{"class":68},[62,62552,62508],{"class":122},[62,62554,62055],{"class":72},[62,62556,23540],{"class":889},[62,62558,768],{"class":72},[62,62560,62561,62563,62565,62567],{"class":64,"line":129},[62,62562,2405],{"class":149},[62,62564,23549],{"class":72},[62,62566,146],{"class":68},[62,62568,23554],{"class":72},[62,62570,62571],{"class":64,"line":134},[62,62572,223],{"class":72},[62,62574,62575],{"class":64,"line":156},[62,62576,79],{"emptyLinePlaceholder":13},[62,62578,62579,62581],{"class":64,"line":161},[62,62580,2143],{"class":72},[62,62582,13555],{"class":68},[62,62584,62585,62587,62589,62591,62593,62595,62597,62599],{"class":64,"line":167},[62,62586,194],{"class":68},[62,62588,200],{"class":68},[62,62590,1716],{"class":122},[62,62592,17699],{"class":72},[62,62594,2117],{"class":889},[62,62596,5024],{"class":72},[62,62598,11501],{"class":68},[62,62600,11504],{"class":72},[62,62602,62603,62605,62607,62609,62612],{"class":64,"line":173},[62,62604,61964],{"class":72},[62,62606,12688],{"class":122},[62,62608,2109],{"class":72},[62,62610,62611],{"class":1675},"\"Loading data from AnotherDataLoader...\"",[62,62613,1133],{"class":72},[62,62615,62616,62618,62620,62622,62624,62626,62628,62630,62632,62634,62636,62639,62641,62643,62645,62647,62650,62652,62654,62656,62658,62661],{"class":64,"line":179},[62,62617,25147],{"class":72},[62,62619,61855],{"class":68},[62,62621,62122],{"class":72},[62,62623,146],{"class":68},[62,62625,3499],{"class":72},[62,62627,3298],{"class":122},[62,62629,2109],{"class":72},[62,62631,2426],{"class":68},[62,62633,61759],{"class":122},[62,62635,2109],{"class":72},[62,62637,62638],{"class":1675},"\"Image 4\"",[62,62640,29203],{"class":72},[62,62642,2426],{"class":68},[62,62644,61759],{"class":122},[62,62646,2109],{"class":72},[62,62648,62649],{"class":1675},"\"Image 5\"",[62,62651,29203],{"class":72},[62,62653,2426],{"class":68},[62,62655,61759],{"class":122},[62,62657,2109],{"class":72},[62,62659,62660],{"class":1675},"\"Image 6\"",[62,62662,6979],{"class":72},[62,62664,62665,62667,62669],{"class":64,"line":185},[62,62666,22559],{"class":72},[62,62668,62170],{"class":122},[62,62670,62173],{"class":72},[62,62672,62673],{"class":64,"line":191},[62,62674,223],{"class":72},[62,62676,62677],{"class":64,"line":209},[62,62678,379],{"class":72},[52,62680,62682],{"className":54,"code":62681,"language":56,"meta":57,"style":57},"@Component\n@Order(2)\npublic class DataLoader implements CommandLineRunner {\n\n private final Logger logger = LoggerFactory.getLogger(DataLoader.class);\n private final ImageRepository repository;\n\n public DataLoader(ImageRepository repository) {\n this.repository = repository;\n }\n\n @Override\n public void run(String... args) throws Exception {\n logger.info(\"Loading data from DataLoader...\");\n List\u003CImage> images = List.of(new Image(\"Image 1\"), new Image(\"Image 2\"), new Image(\"Image 3\"));\n repository.saveAll(images);\n }\n}\n",[59,62683,62684,62690,62702,62716,62720,62736,62744,62748,62760,62770,62774,62778,62784,62802,62815,62861,62869,62873],{"__ignoreMap":57},[62,62685,62686,62688],{"class":64,"line":65},[62,62687,942],{"class":72},[62,62689,12585],{"class":68},[62,62691,62692,62694,62696,62698,62700],{"class":64,"line":76},[62,62693,942],{"class":72},[62,62695,9415],{"class":68},[62,62697,2109],{"class":72},[62,62699,5219],{"class":149},[62,62701,2212],{"class":72},[62,62703,62704,62706,62708,62710,62712,62714],{"class":64,"line":82},[62,62705,116],{"class":68},[62,62707,119],{"class":68},[62,62709,61902],{"class":122},[62,62711,13520],{"class":68},[62,62713,17592],{"class":122},[62,62715,126],{"class":72},[62,62717,62718],{"class":64,"line":89},[62,62719,79],{"emptyLinePlaceholder":13},[62,62721,62722,62724,62726,62728,62730,62732,62734],{"class":64,"line":95},[62,62723,137],{"class":68},[62,62725,458],{"class":68},[62,62727,61921],{"class":72},[62,62729,146],{"class":68},[62,62731,3066],{"class":72},[62,62733,3069],{"class":122},[62,62735,61930],{"class":72},[62,62737,62738,62740,62742],{"class":64,"line":101},[62,62739,137],{"class":68},[62,62741,458],{"class":68},[62,62743,62042],{"class":72},[62,62745,62746],{"class":64,"line":107},[62,62747,79],{"emptyLinePlaceholder":13},[62,62749,62750,62752,62754,62756,62758],{"class":64,"line":113},[62,62751,194],{"class":68},[62,62753,61902],{"class":122},[62,62755,62055],{"class":72},[62,62757,23540],{"class":889},[62,62759,768],{"class":72},[62,62761,62762,62764,62766,62768],{"class":64,"line":129},[62,62763,2405],{"class":149},[62,62765,23549],{"class":72},[62,62767,146],{"class":68},[62,62769,23554],{"class":72},[62,62771,62772],{"class":64,"line":134},[62,62773,223],{"class":72},[62,62775,62776],{"class":64,"line":156},[62,62777,79],{"emptyLinePlaceholder":13},[62,62779,62780,62782],{"class":64,"line":161},[62,62781,2143],{"class":72},[62,62783,13555],{"class":68},[62,62785,62786,62788,62790,62792,62794,62796,62798,62800],{"class":64,"line":167},[62,62787,194],{"class":68},[62,62789,200],{"class":68},[62,62791,1716],{"class":122},[62,62793,17699],{"class":72},[62,62795,2117],{"class":889},[62,62797,5024],{"class":72},[62,62799,11501],{"class":68},[62,62801,11504],{"class":72},[62,62803,62804,62806,62808,62810,62813],{"class":64,"line":173},[62,62805,61964],{"class":72},[62,62807,12688],{"class":122},[62,62809,2109],{"class":72},[62,62811,62812],{"class":1675},"\"Loading data from DataLoader...\"",[62,62814,1133],{"class":72},[62,62816,62817,62819,62821,62823,62825,62827,62829,62831,62833,62835,62837,62839,62841,62843,62845,62847,62849,62851,62853,62855,62857,62859],{"class":64,"line":179},[62,62818,25147],{"class":72},[62,62820,61855],{"class":68},[62,62822,62122],{"class":72},[62,62824,146],{"class":68},[62,62826,3499],{"class":72},[62,62828,3298],{"class":122},[62,62830,2109],{"class":72},[62,62832,2426],{"class":68},[62,62834,61759],{"class":122},[62,62836,2109],{"class":72},[62,62838,62139],{"class":1675},[62,62840,29203],{"class":72},[62,62842,2426],{"class":68},[62,62844,61759],{"class":122},[62,62846,2109],{"class":72},[62,62848,62150],{"class":1675},[62,62850,29203],{"class":72},[62,62852,2426],{"class":68},[62,62854,61759],{"class":122},[62,62856,2109],{"class":72},[62,62858,62161],{"class":1675},[62,62860,6979],{"class":72},[62,62862,62863,62865,62867],{"class":64,"line":185},[62,62864,22559],{"class":72},[62,62866,62170],{"class":122},[62,62868,62173],{"class":72},[62,62870,62871],{"class":64,"line":191},[62,62872,223],{"class":72},[62,62874,62875],{"class":64,"line":209},[62,62876,379],{"class":72},[22,62878,62879],{},"If we look in the console we can see that Another Database Loader ran first.",[22,62881,62882],{},[653,62883],{"alt":62884,"src":62885},"Command Line Runner @Order Annotation","./command-line-runner-order.png",[26,62887,1499],{"id":1498},[22,62889,62890,62891,62895],{},"I hope this helped clear up some confusion on what a Command Line Runner is and how to use it. If you want to checkout the code for this demo you will find it over on ",[677,62892,50830],{"href":62893,"rel":62894},"https://github.com/danvega/command-line-runner",[681],". If you're already using a Command Line Runner in your applications I would like to leave you with this question to kick start a discussion.",[22,62897,62898],{},[4534,62899,62900,62902],{},[646,62901,49733],{}," What are some other uses for a Command Line Runner?",[1527,62904,62905],{},"html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .spoOn, html code.shiki .spoOn{--shiki-default:#E36209;--shiki-dark:#FFAB70;--shiki-sepia:#E36209}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}html pre.shiki code .s4-Ip, html code.shiki .s4-Ip{--shiki-default:#6A737D;--shiki-dark:#6A737D;--shiki-sepia:#6A737D}",{"title":57,"searchDepth":76,"depth":76,"links":62907},[62908,62913],{"id":61683,"depth":76,"text":61684,"children":62909},[62910,62911,62912],{"id":61706,"depth":82,"text":61707},{"id":62198,"depth":82,"text":62199},{"id":62472,"depth":82,"text":62473},{"id":1498,"depth":76,"text":1499},{"_id":62915,"path":62916,"title":62917,"description":62917,"meta":62918,"body":62923},"content/blog/2017/04/05/java-equals.md","/blog/2017/04/05/java-equals","Java: What is the difference between equals and ==",{"slug":62919,"date":62920,"published":13,"tags":62921,"author":-1,"cover":62922,"excerpt":-1},"java-equals","2017-04-05T08:00:50-04:00",[56],"./java_equals-760x428.png",{"type":19,"value":62924,"toc":63662},[62925,62928,62932,62935,63196,63199,63205,63208,63262,63265,63269,63272,63385,63389,63392,63638,63641,63643,63652,63659],[22,62926,62927],{},"This is going to be a fun and personal post for me. The problem that we are going to look at in this article is something we are taught as young software developers. I saw this question on a facebook group that I am a part of and I made the same mistake at work debugging a critical ticket. If you want to find out what trips up newbies and veterans alike, continue reading this article. ",[26,62929,62931],{"id":62930},"the-problem","The Problem",[22,62933,62934],{},"The problem we are going to look at today has to do with comparing values in Java. This is language agnostic issue though as every language has its own way to deal with object values and references. We are going to start by looking at a question I saw in the facebook group I am in. Given the title and subject of this article, you might be able to easily find the problem. What is the output of this code and why is that output returned to us? ",[52,62936,62938],{"className":54,"code":62937,"language":56,"meta":57,"style":57},"import java.util.Scanner;\n\npublic class Main {\n\n public static void main(String\\[\\] args) {\n\n Scanner input = new Scanner(System.in);\n\n String answer;\n\n System.out.println(\"Canada is a country located in which continent?\");\n System.out.println(\"A - South America\");\n System.out.println(\"B - Asia\");\n System.out.println(\"C - North America\");\n System.out.println(\"D - Oceania\");\n System.out.println(\"F - Africa\");\n System.out.println(\"Please enter the letter of the correct answer.\");\n answer = input.nextLine();\n\n if (answer == \"C\") {\n System.out.print(\"You are correct\");\n } else {\n System.out.print(\"You are incorrect\");\n }\n\n }\n\n}\n",[59,62939,62940,62947,62951,62961,62965,62985,62989,63004,63008,63013,63017,63030,63043,63056,63069,63082,63095,63108,63123,63127,63141,63155,63163,63176,63180,63184,63188,63192],{"__ignoreMap":57},[62,62941,62942,62944],{"class":64,"line":65},[62,62943,27875],{"class":68},[62,62945,62946],{"class":72}," java.util.Scanner;\n",[62,62948,62949],{"class":64,"line":76},[62,62950,79],{"emptyLinePlaceholder":13},[62,62952,62953,62955,62957,62959],{"class":64,"line":82},[62,62954,116],{"class":68},[62,62956,119],{"class":68},[62,62958,27335],{"class":122},[62,62960,126],{"class":72},[62,62962,62963],{"class":64,"line":89},[62,62964,79],{"emptyLinePlaceholder":13},[62,62966,62967,62969,62971,62973,62975,62977,62979,62981,62983],{"class":64,"line":95},[62,62968,194],{"class":68},[62,62970,2101],{"class":68},[62,62972,200],{"class":68},[62,62974,2106],{"class":122},[62,62976,2109],{"class":72},[62,62978,973],{"class":889},[62,62980,52569],{"class":72},[62,62982,2117],{"class":889},[62,62984,768],{"class":72},[62,62986,62987],{"class":64,"line":101},[62,62988,79],{"emptyLinePlaceholder":13},[62,62990,62991,62994,62996,62998,63001],{"class":64,"line":107},[62,62992,62993],{"class":72}," Scanner input ",[62,62995,146],{"class":68},[62,62997,466],{"class":68},[62,62999,63000],{"class":122}," Scanner",[62,63002,63003],{"class":72},"(System.in);\n",[62,63005,63006],{"class":64,"line":113},[62,63007,79],{"emptyLinePlaceholder":13},[62,63009,63010],{"class":64,"line":129},[62,63011,63012],{"class":72}," String answer;\n",[62,63014,63015],{"class":64,"line":134},[62,63016,79],{"emptyLinePlaceholder":13},[62,63018,63019,63021,63023,63025,63028],{"class":64,"line":156},[62,63020,27297],{"class":72},[62,63022,2244],{"class":122},[62,63024,2109],{"class":72},[62,63026,63027],{"class":1675},"\"Canada is a country located in which continent?\"",[62,63029,1133],{"class":72},[62,63031,63032,63034,63036,63038,63041],{"class":64,"line":161},[62,63033,27297],{"class":72},[62,63035,2244],{"class":122},[62,63037,2109],{"class":72},[62,63039,63040],{"class":1675},"\"A - South America\"",[62,63042,1133],{"class":72},[62,63044,63045,63047,63049,63051,63054],{"class":64,"line":167},[62,63046,27297],{"class":72},[62,63048,2244],{"class":122},[62,63050,2109],{"class":72},[62,63052,63053],{"class":1675},"\"B - Asia\"",[62,63055,1133],{"class":72},[62,63057,63058,63060,63062,63064,63067],{"class":64,"line":173},[62,63059,27297],{"class":72},[62,63061,2244],{"class":122},[62,63063,2109],{"class":72},[62,63065,63066],{"class":1675},"\"C - North America\"",[62,63068,1133],{"class":72},[62,63070,63071,63073,63075,63077,63080],{"class":64,"line":179},[62,63072,27297],{"class":72},[62,63074,2244],{"class":122},[62,63076,2109],{"class":72},[62,63078,63079],{"class":1675},"\"D - Oceania\"",[62,63081,1133],{"class":72},[62,63083,63084,63086,63088,63090,63093],{"class":64,"line":185},[62,63085,27297],{"class":72},[62,63087,2244],{"class":122},[62,63089,2109],{"class":72},[62,63091,63092],{"class":1675},"\"F - Africa\"",[62,63094,1133],{"class":72},[62,63096,63097,63099,63101,63103,63106],{"class":64,"line":191},[62,63098,27297],{"class":72},[62,63100,2244],{"class":122},[62,63102,2109],{"class":72},[62,63104,63105],{"class":1675},"\"Please enter the letter of the correct answer.\"",[62,63107,1133],{"class":72},[62,63109,63110,63113,63115,63118,63121],{"class":64,"line":209},[62,63111,63112],{"class":72}," answer ",[62,63114,146],{"class":68},[62,63116,63117],{"class":72}," input.",[62,63119,63120],{"class":122},"nextLine",[62,63122,822],{"class":72},[62,63124,63125],{"class":64,"line":220},[62,63126,79],{"emptyLinePlaceholder":13},[62,63128,63129,63131,63134,63136,63139],{"class":64,"line":226},[62,63130,12741],{"class":68},[62,63132,63133],{"class":72}," (answer ",[62,63135,28328],{"class":68},[62,63137,63138],{"class":1675}," \"C\"",[62,63140,768],{"class":72},[62,63142,63143,63145,63148,63150,63153],{"class":64,"line":231},[62,63144,2241],{"class":72},[62,63146,63147],{"class":122},"print",[62,63149,2109],{"class":72},[62,63151,63152],{"class":1675},"\"You are correct\"",[62,63154,1133],{"class":72},[62,63156,63157,63159,63161],{"class":64,"line":236},[62,63158,880],{"class":72},[62,63160,12783],{"class":68},[62,63162,126],{"class":72},[62,63164,63165,63167,63169,63171,63174],{"class":64,"line":242},[62,63166,2241],{"class":72},[62,63168,63147],{"class":122},[62,63170,2109],{"class":72},[62,63172,63173],{"class":1675},"\"You are incorrect\"",[62,63175,1133],{"class":72},[62,63177,63178],{"class":64,"line":247},[62,63179,533],{"class":72},[62,63181,63182],{"class":64,"line":252},[62,63183,79],{"emptyLinePlaceholder":13},[62,63185,63186],{"class":64,"line":257},[62,63187,223],{"class":72},[62,63189,63190],{"class":64,"line":271},[62,63191,79],{"emptyLinePlaceholder":13},[62,63193,63194],{"class":64,"line":281},[62,63195,379],{"class":72},[22,63197,63198],{},"If you run this code and enter \"C\" as your answer, the program will print out \"You are incorrect\". ",[22,63200,63201],{},[653,63202],{"alt":63203,"src":63204},"Java Equals Example","./java_equals-1.png",[22,63206,63207],{},"So what happened here? Why didn't the program print out \"You are correct\"? The problem with this program is with the following code. ",[52,63209,63211],{"className":54,"code":63210,"language":56,"meta":57,"style":57},"if (answer == \"C\") {\n System.out.print(\"You are correct\");\n} else {\n System.out.print(\"You are incorrect\");\n}\n",[59,63212,63213,63225,63237,63246,63258],{"__ignoreMap":57},[62,63214,63215,63217,63219,63221,63223],{"class":64,"line":65},[62,63216,34116],{"class":68},[62,63218,63133],{"class":72},[62,63220,28328],{"class":68},[62,63222,63138],{"class":1675},[62,63224,768],{"class":72},[62,63226,63227,63229,63231,63233,63235],{"class":64,"line":76},[62,63228,4491],{"class":72},[62,63230,63147],{"class":122},[62,63232,2109],{"class":72},[62,63234,63152],{"class":1675},[62,63236,1133],{"class":72},[62,63238,63239,63242,63244],{"class":64,"line":82},[62,63240,63241],{"class":72},"} ",[62,63243,12783],{"class":68},[62,63245,126],{"class":72},[62,63247,63248,63250,63252,63254,63256],{"class":64,"line":89},[62,63249,4491],{"class":72},[62,63251,63147],{"class":122},[62,63253,2109],{"class":72},[62,63255,63173],{"class":1675},[62,63257,1133],{"class":72},[62,63259,63260],{"class":64,"line":95},[62,63261,379],{"class":72},[22,63263,63264],{},"The variable answer is a String and we are using == to compare the value to \"C\". We know that if we compare the value \"C\" to \"C\" that they should be equal. In fact, they are equal but the problem lies with the operator. ",[636,63266,63268],{"id":63267},"the-operator","The \"==\" Operator",[22,63270,63271],{},"In Java, we use the \"==\" operator to compare 2 objects to see if the refer to the same place in memory. When we create the variable answer and assign it a value that is a reference to that object. When we compare that object in memory to the value \"C\" those are not the same so \"==\" will return false. The following is an example of where 2 variables reference the same object. ",[52,63273,63275],{"className":54,"code":63274,"language":56,"meta":57,"style":57},"public class Main {\n\n public static void main(String\\[\\] args) {\n\n String name = \"Dan\";\n String myName = name;\n\n if( name == myName ){\n System.out.println(\"name is equal to myName\");\n }\n\n }\n\n}\n",[59,63276,63277,63287,63291,63311,63315,63327,63336,63340,63352,63365,63369,63373,63377,63381],{"__ignoreMap":57},[62,63278,63279,63281,63283,63285],{"class":64,"line":65},[62,63280,116],{"class":68},[62,63282,119],{"class":68},[62,63284,27335],{"class":122},[62,63286,126],{"class":72},[62,63288,63289],{"class":64,"line":76},[62,63290,79],{"emptyLinePlaceholder":13},[62,63292,63293,63295,63297,63299,63301,63303,63305,63307,63309],{"class":64,"line":82},[62,63294,194],{"class":68},[62,63296,2101],{"class":68},[62,63298,200],{"class":68},[62,63300,2106],{"class":122},[62,63302,2109],{"class":72},[62,63304,973],{"class":889},[62,63306,52569],{"class":72},[62,63308,2117],{"class":889},[62,63310,768],{"class":72},[62,63312,63313],{"class":64,"line":89},[62,63314,79],{"emptyLinePlaceholder":13},[62,63316,63317,63320,63322,63325],{"class":64,"line":95},[62,63318,63319],{"class":72}," String name ",[62,63321,146],{"class":68},[62,63323,63324],{"class":1675}," \"Dan\"",[62,63326,153],{"class":72},[62,63328,63329,63332,63334],{"class":64,"line":101},[62,63330,63331],{"class":72}," String myName ",[62,63333,146],{"class":68},[62,63335,58837],{"class":72},[62,63337,63338],{"class":64,"line":107},[62,63339,79],{"emptyLinePlaceholder":13},[62,63341,63342,63344,63347,63349],{"class":64,"line":113},[62,63343,12741],{"class":68},[62,63345,63346],{"class":72},"( name ",[62,63348,28328],{"class":68},[62,63350,63351],{"class":72}," myName ){\n",[62,63353,63354,63356,63358,63360,63363],{"class":64,"line":129},[62,63355,2241],{"class":72},[62,63357,2244],{"class":122},[62,63359,2109],{"class":72},[62,63361,63362],{"class":1675},"\"name is equal to myName\"",[62,63364,1133],{"class":72},[62,63366,63367],{"class":64,"line":134},[62,63368,533],{"class":72},[62,63370,63371],{"class":64,"line":156},[62,63372,79],{"emptyLinePlaceholder":13},[62,63374,63375],{"class":64,"line":161},[62,63376,223],{"class":72},[62,63378,63379],{"class":64,"line":167},[62,63380,79],{"emptyLinePlaceholder":13},[62,63382,63383],{"class":64,"line":173},[62,63384,379],{"class":72},[26,63386,63388],{"id":63387},"the-solution","The Solution",[22,63390,63391],{},"If that is the case how can we fix our program? The String class actually contains a method called .equals. With that knowledge, we can fix our application by replacing that \"==\" with .equals. ",[52,63393,63395],{"className":54,"code":63394,"language":56,"meta":57,"style":57},"import java.util.Scanner;\n\npublic class Main {\n\n public static void main(String\\[\\] args) {\n\n Scanner input = new Scanner(System.in);\n\n String answer;\n\n System.out.println(\"Canada is a country located in which continent?\");\n System.out.println(\"A - South America\");\n System.out.println(\"B - Asia\");\n System.out.println(\"C - North America\");\n System.out.println(\"D - Oceania\");\n System.out.println(\"F - Africa\");\n System.out.println(\"Please enter the letter of the correct answer.\");\n answer = input.nextLine();\n\n if( answer.equals(\"C\") ) {\n System.out.print(\"You are correct\");\n } else {\n System.out.print(\"You are incorrect\");\n }\n\n }\n\n}\n",[59,63396,63397,63403,63407,63417,63421,63441,63445,63457,63461,63465,63469,63481,63493,63505,63517,63529,63541,63553,63565,63569,63586,63598,63606,63618,63622,63626,63630,63634],{"__ignoreMap":57},[62,63398,63399,63401],{"class":64,"line":65},[62,63400,27875],{"class":68},[62,63402,62946],{"class":72},[62,63404,63405],{"class":64,"line":76},[62,63406,79],{"emptyLinePlaceholder":13},[62,63408,63409,63411,63413,63415],{"class":64,"line":82},[62,63410,116],{"class":68},[62,63412,119],{"class":68},[62,63414,27335],{"class":122},[62,63416,126],{"class":72},[62,63418,63419],{"class":64,"line":89},[62,63420,79],{"emptyLinePlaceholder":13},[62,63422,63423,63425,63427,63429,63431,63433,63435,63437,63439],{"class":64,"line":95},[62,63424,194],{"class":68},[62,63426,2101],{"class":68},[62,63428,200],{"class":68},[62,63430,2106],{"class":122},[62,63432,2109],{"class":72},[62,63434,973],{"class":889},[62,63436,52569],{"class":72},[62,63438,2117],{"class":889},[62,63440,768],{"class":72},[62,63442,63443],{"class":64,"line":101},[62,63444,79],{"emptyLinePlaceholder":13},[62,63446,63447,63449,63451,63453,63455],{"class":64,"line":107},[62,63448,62993],{"class":72},[62,63450,146],{"class":68},[62,63452,466],{"class":68},[62,63454,63000],{"class":122},[62,63456,63003],{"class":72},[62,63458,63459],{"class":64,"line":113},[62,63460,79],{"emptyLinePlaceholder":13},[62,63462,63463],{"class":64,"line":129},[62,63464,63012],{"class":72},[62,63466,63467],{"class":64,"line":134},[62,63468,79],{"emptyLinePlaceholder":13},[62,63470,63471,63473,63475,63477,63479],{"class":64,"line":156},[62,63472,27297],{"class":72},[62,63474,2244],{"class":122},[62,63476,2109],{"class":72},[62,63478,63027],{"class":1675},[62,63480,1133],{"class":72},[62,63482,63483,63485,63487,63489,63491],{"class":64,"line":161},[62,63484,27297],{"class":72},[62,63486,2244],{"class":122},[62,63488,2109],{"class":72},[62,63490,63040],{"class":1675},[62,63492,1133],{"class":72},[62,63494,63495,63497,63499,63501,63503],{"class":64,"line":167},[62,63496,27297],{"class":72},[62,63498,2244],{"class":122},[62,63500,2109],{"class":72},[62,63502,63053],{"class":1675},[62,63504,1133],{"class":72},[62,63506,63507,63509,63511,63513,63515],{"class":64,"line":173},[62,63508,27297],{"class":72},[62,63510,2244],{"class":122},[62,63512,2109],{"class":72},[62,63514,63066],{"class":1675},[62,63516,1133],{"class":72},[62,63518,63519,63521,63523,63525,63527],{"class":64,"line":179},[62,63520,27297],{"class":72},[62,63522,2244],{"class":122},[62,63524,2109],{"class":72},[62,63526,63079],{"class":1675},[62,63528,1133],{"class":72},[62,63530,63531,63533,63535,63537,63539],{"class":64,"line":185},[62,63532,27297],{"class":72},[62,63534,2244],{"class":122},[62,63536,2109],{"class":72},[62,63538,63092],{"class":1675},[62,63540,1133],{"class":72},[62,63542,63543,63545,63547,63549,63551],{"class":64,"line":191},[62,63544,27297],{"class":72},[62,63546,2244],{"class":122},[62,63548,2109],{"class":72},[62,63550,63105],{"class":1675},[62,63552,1133],{"class":72},[62,63554,63555,63557,63559,63561,63563],{"class":64,"line":209},[62,63556,63112],{"class":72},[62,63558,146],{"class":68},[62,63560,63117],{"class":72},[62,63562,63120],{"class":122},[62,63564,822],{"class":72},[62,63566,63567],{"class":64,"line":220},[62,63568,79],{"emptyLinePlaceholder":13},[62,63570,63571,63573,63576,63578,63580,63583],{"class":64,"line":226},[62,63572,12741],{"class":68},[62,63574,63575],{"class":72},"( answer.",[62,63577,3232],{"class":122},[62,63579,2109],{"class":72},[62,63581,63582],{"class":1675},"\"C\"",[62,63584,63585],{"class":72},") ) {\n",[62,63587,63588,63590,63592,63594,63596],{"class":64,"line":231},[62,63589,2241],{"class":72},[62,63591,63147],{"class":122},[62,63593,2109],{"class":72},[62,63595,63152],{"class":1675},[62,63597,1133],{"class":72},[62,63599,63600,63602,63604],{"class":64,"line":236},[62,63601,880],{"class":72},[62,63603,12783],{"class":68},[62,63605,126],{"class":72},[62,63607,63608,63610,63612,63614,63616],{"class":64,"line":242},[62,63609,2241],{"class":72},[62,63611,63147],{"class":122},[62,63613,2109],{"class":72},[62,63615,63173],{"class":1675},[62,63617,1133],{"class":72},[62,63619,63620],{"class":64,"line":247},[62,63621,533],{"class":72},[62,63623,63624],{"class":64,"line":252},[62,63625,79],{"emptyLinePlaceholder":13},[62,63627,63628],{"class":64,"line":257},[62,63629,223],{"class":72},[62,63631,63632],{"class":64,"line":271},[62,63633,79],{"emptyLinePlaceholder":13},[62,63635,63636],{"class":64,"line":281},[62,63637,379],{"class":72},[22,63639,63640],{},"Now instead of comparing objects, we are simply comparing values. That is why you will often hear programmers say \"compare by value, not reference\". We probably fix a couple other issues with this program but that is neither here nor there. ",[26,63642,1499],{"id":1498},[22,63644,63645,63646,63651],{},"Remember when I told you that I made this same mistake at work? There is actually a good reason for this. I was working in a Java program and this gives me some problems from time to time because of my love for the ",[677,63647,63650],{"href":63648,"rel":63649},"http://bit.ly/2n5SBn6",[681],"Groovy Programming language",". I would say I am writing Groovy 90% of the time and because of that, I become used to the syntax. In Groovy we actually overload the \"==\" operator so that comparison would actually work. ",[22,63653,63654],{},[4534,63655,63656,63658],{},[646,63657,49733],{}," What are some of the fundamental programming concepts that still trip you up once in awhile?",[1527,63660,63661],{},"html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .spoOn, html code.shiki .spoOn{--shiki-default:#E36209;--shiki-dark:#FFAB70;--shiki-sepia:#E36209}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}",{"title":57,"searchDepth":76,"depth":76,"links":63663},[63664,63667,63668],{"id":62930,"depth":76,"text":62931,"children":63665},[63666],{"id":63267,"depth":82,"text":63268},{"id":63387,"depth":76,"text":63388},{"id":1498,"depth":76,"text":1499},{"_id":63670,"path":63671,"title":63672,"description":63672,"meta":63673,"body":63678},"content/blog/2017/04/03/2000-subscribers-youtube.md","/blog/2017/04/03/2000-subscribers-youtube","2,000 Subscribers on YouTube!!",{"slug":63674,"date":63675,"published":13,"tags":63676,"author":-1,"cover":63677,"excerpt":-1},"2000-subscribers-youtube","2017-04-03T08:30:34-04:00",[37949],"./200-subscribers.jpg",{"type":19,"value":63679,"toc":63688},[63680],[22,63681,63682,63683,63687],{},"I was so excited to find out that I crossed 2,000 YouTube Subscribers last week! ",[677,63684,63685],{"href":63685,"rel":63686},"https://www.youtube.com/watch?v=40kw1-fSAbo",[681]," Just like my blog I am going to make a few changes on the YouTube channel. I am going to focus the content on mostly programming related topics. I also am going to work on a consistent schedule. With a consistent schedule between my blog and YouTube channel, I think the I can do some big things in 2017 and beyond. Thank you to everyone for the support over of the years. I very much appreciate it.",{"title":57,"searchDepth":76,"depth":76,"links":63689},[],{"_id":63691,"path":63692,"title":63693,"description":63693,"meta":63694,"body":63699},"content/blog/2017/03/31/using-project-lombok-spring-boot.md","/blog/2017/03/31/using-project-lombok-spring-boot","Using Project Lombok in your Spring Boot Project",{"slug":63695,"date":63696,"published":13,"tags":63697,"author":-1,"cover":63698,"excerpt":-1},"using-project-lombok-spring-boot","2017-03-31T08:00:38-04:00",[56,11002],"./2350-760x507.jpg",{"type":19,"value":63700,"toc":65589},[63701,63725,63729,63732,63740,63749,63753,63757,63766,63771,63776,63779,63880,63883,63889,63892,65447,65451,65459,65566,65569,65575,65577,65580,65586],[22,63702,63703,63704,63709,63710,63715,63716,63720,63721,63724],{},"In this article, we get to take a look at an awesome little project called ",[677,63705,63708],{"href":63706,"rel":63707},"https://projectlombok.org/",[681],"Project Lombok",". If you have been following me for awhile now then you already know ",[677,63711,63714],{"href":63712,"rel":63713},"http://courses.danvega.dev/p/the-complete-apache-groovy-developer-course",[681],"I am a huge fan"," of the ",[677,63717,63719],{"href":53545,"rel":63718},[681],"Groovy Programming Language",". It takes a lot of the things I don't like in Java and makes them better. Like, way better! Groovy is also really ",[4534,63722,63723],{},"GOOD"," at cutting down the amount of ceremonious code you have to write. If you are working on a Java project and are looking for a way to cut down some of the noise in your code I have a great solution for you. In this article, we are going to look at Project Lombok and dive into what it is and how to use it in your next Spring Boot Application. ",[26,63726,63728],{"id":63727},"what-is-project-lombok","What is Project Lombok?",[22,63730,63731],{},"As I said in the introduction to this article, one of the biggest complaints against Java is how much noise can be found in a single class. Project Lombok saw this as a problem and aims to reduce the noise of some of the worst offenders by replacing them with a simple set of annotations. One of the worst offenders in Java are classes that have a ton of fields in them and consist of a getter & setter for each property. I will refer to these \"types\" of classes as data classes going forward. IDEs have a come a long way and help us generate this code but every single time you make a change to that data class you will need to go back and regenerate that code or even worse, write it yourself. In this example below, straight from the documentation, all of the getters, setters and a to string method were created for us just by adding the @Data annotation to our Data class.",[22,63733,63734,60103,63738],{},[653,63735],{"alt":63736,"src":63737},"Project Lombok in Action","./lombok.png",[4534,63739,63736],{},[22,63741,63742,63743,63748],{},"Pretty awesome right? Project Lombok consists of more than just this single annotation. If you want to learn about them please ",[677,63744,63747],{"href":63745,"rel":63746},"http://jnb.ociweb.com/jnb/jnbJan2010.html#intro",[681],"read through the documentation",". For now, we are just going to run through a basic demo. ",[26,63750,63752],{"id":63751},"project-lombok-demo","Project Lombok Demo",[636,63754,63756],{"id":63755},"before-project-lombok","Before Project Lombok",[22,63758,63759,63760,63765],{},"We are going to create a new Spring Boot application. If you want to grab the ",[677,63761,63764],{"href":63762,"rel":63763},"https://github.com/cfaddict/lombok",[681],"project source code you can do so here",". You can select Lombok right from the core section of the Spring Initilizr. ",[22,63767,63768],{},[653,63769],{"alt":63708,"src":63770},"./lombok_demo_1-300x189.png",[22,63772,63773],{},[653,63774],{"alt":63708,"src":63775},"./lombok_demo_2-300x189.png",[22,63777,63778],{},"I am going to create a new user object with a bunch of fields. ",[52,63780,63782],{"className":54,"code":63781,"language":56,"meta":57,"style":57},"package com.therealdanvega;\n\nimport java.util.Date;\n\npublic class User {\n\n private String first;\n private String middle;\n private String last;\n private String email;\n private Date dob;\n private String foo;\n private String bar;\n private String a,b,c;\n\n}\n",[59,63783,63784,63790,63794,63801,63805,63815,63819,63825,63832,63838,63844,63851,63858,63865,63872,63876],{"__ignoreMap":57},[62,63785,63786,63788],{"class":64,"line":65},[62,63787,69],{"class":68},[62,63789,52481],{"class":72},[62,63791,63792],{"class":64,"line":76},[62,63793,79],{"emptyLinePlaceholder":13},[62,63795,63796,63798],{"class":64,"line":82},[62,63797,27875],{"class":68},[62,63799,63800],{"class":72}," java.util.Date;\n",[62,63802,63803],{"class":64,"line":89},[62,63804,79],{"emptyLinePlaceholder":13},[62,63806,63807,63809,63811,63813],{"class":64,"line":95},[62,63808,116],{"class":68},[62,63810,119],{"class":68},[62,63812,22289],{"class":122},[62,63814,126],{"class":72},[62,63816,63817],{"class":64,"line":101},[62,63818,79],{"emptyLinePlaceholder":13},[62,63820,63821,63823],{"class":64,"line":107},[62,63822,137],{"class":68},[62,63824,52021],{"class":72},[62,63826,63827,63829],{"class":64,"line":113},[62,63828,137],{"class":68},[62,63830,63831],{"class":72}," String middle;\n",[62,63833,63834,63836],{"class":64,"line":129},[62,63835,137],{"class":68},[62,63837,52028],{"class":72},[62,63839,63840,63842],{"class":64,"line":134},[62,63841,137],{"class":68},[62,63843,22360],{"class":72},[62,63845,63846,63848],{"class":64,"line":156},[62,63847,137],{"class":68},[62,63849,63850],{"class":72}," Date dob;\n",[62,63852,63853,63855],{"class":64,"line":161},[62,63854,137],{"class":68},[62,63856,63857],{"class":72}," String foo;\n",[62,63859,63860,63862],{"class":64,"line":167},[62,63861,137],{"class":68},[62,63863,63864],{"class":72}," String bar;\n",[62,63866,63867,63869],{"class":64,"line":173},[62,63868,137],{"class":68},[62,63870,63871],{"class":72}," String a,b,c;\n",[62,63873,63874],{"class":64,"line":179},[62,63875,79],{"emptyLinePlaceholder":13},[62,63877,63878],{"class":64,"line":185},[62,63879,379],{"class":72},[22,63881,63882],{},"That looks like a pretty clean class, but we aren't done yet. Now we need to create a getter & setter for each of those properties. We also would probably want to add a toString, equals and hashCode method to our class. If you didn't know this already, you can generate these using cmd + n on the mac (cntrl+n on a PC).",[22,63884,63885],{},[653,63886],{"alt":63887,"src":63888},"Project lombok","./lombok_demo_3-193x300.png",[22,63890,63891],{},"Now our class is correct but that is a whole bunch of unwanted code. Not to mention what a maintenance nightmare this is. Every single time this class changes we need to update the getters, setters, and methods by generating them over or even worse, actually writing code. ",[52,63893,63895],{"className":54,"code":63894,"language":56,"meta":57,"style":57},"package com.therealdanvega;\n\nimport java.util.Date;\n\npublic class User {\n\n private String first;\n private String middle;\n private String last;\n private String email;\n private Date dob;\n private String foo;\n private String bar;\n private String a,b,c;\n\n public String getFirst() {\n return first;\n }\n\n public void setFirst(String first) {\n this.first = first;\n }\n\n public String getMiddle() {\n return middle;\n }\n\n public void setMiddle(String middle) {\n this.middle = middle;\n }\n\n public String getLast() {\n return last;\n }\n\n public void setLast(String last) {\n this.last = last;\n }\n\n public String getEmail() {\n return email;\n }\n\n public void setEmail(String email) {\n this.email = email;\n }\n\n public Date getDob() {\n return dob;\n }\n\n public void setDob(Date dob) {\n this.dob = dob;\n }\n\n public String getFoo() {\n return foo;\n }\n\n public void setFoo(String foo) {\n this.foo = foo;\n }\n\n public String getBar() {\n return bar;\n }\n\n public void setBar(String bar) {\n this.bar = bar;\n }\n\n public String getA() {\n return a;\n }\n\n public void setA(String a) {\n this.a = a;\n }\n\n public String getB() {\n return b;\n }\n\n public void setB(String b) {\n this.b = b;\n }\n\n public String getC() {\n return c;\n }\n\n public void setC(String c) {\n this.c = c;\n }\n\n @Override\n public String toString() {\n return \"User{\" +\n \"first='\" + first + '\\'' +\n \", last='\" + last + '\\'' +\n \", email='\" + email + '\\'' +\n '}';\n }\n\n @Override\n public boolean equals(Object o) {\n if (this == o) return true;\n if (o == null || getClass() != o.getClass()) return false;\n\n User user = (User) o;\n\n if (!first.equals(user.first)) return false;\n if (middle != null ? !middle.equals(user.middle) : user.middle != null) return false;\n if (!last.equals(user.last)) return false;\n if (!email.equals(user.email)) return false;\n if (!dob.equals(user.dob)) return false;\n if (foo != null ? !foo.equals(user.foo) : user.foo != null) return false;\n if (bar != null ? !bar.equals(user.bar) : user.bar != null) return false;\n if (a != null ? !a.equals(user.a) : user.a != null) return false;\n if (b != null ? !b.equals(user.b) : user.b != null) return false;\n return c != null ? c.equals(user.c) : user.c == null;\n }\n\n @Override\n public int hashCode() {\n int result = first.hashCode();\n result = 31 * result + (middle != null ? middle.hashCode() : 0);\n result = 31 * result + last.hashCode();\n result = 31 * result + email.hashCode();\n result = 31 * result + dob.hashCode();\n result = 31 * result + (foo != null ? foo.hashCode() : 0);\n result = 31 * result + (bar != null ? bar.hashCode() : 0);\n result = 31 * result + (a != null ? a.hashCode() : 0);\n result = 31 * result + (b != null ? b.hashCode() : 0);\n result = 31 * result + (c != null ? c.hashCode() : 0);\n return result;\n }\n}\n",[59,63896,63897,63903,63907,63913,63917,63927,63931,63937,63943,63949,63955,63961,63967,63973,63979,63983,63993,63999,64003,64007,64021,64031,64035,64039,64050,64057,64061,64065,64081,64092,64096,64100,64110,64116,64120,64124,64138,64148,64152,64156,64166,64172,64176,64180,64194,64204,64208,64212,64224,64231,64235,64239,64256,64267,64271,64275,64286,64293,64297,64301,64317,64328,64332,64336,64347,64354,64358,64362,64378,64389,64393,64397,64408,64415,64419,64423,64438,64449,64453,64457,64468,64474,64478,64482,64498,64509,64513,64517,64528,64535,64539,64543,64558,64569,64573,64577,64583,64593,64602,64621,64641,64661,64668,64672,64676,64682,64699,64718,64754,64758,64768,64772,64794,64836,64858,64881,64904,64945,64986,65027,65067,65100,65105,65110,65117,65129,65146,65185,65207,65229,65251,65287,65323,65358,65394,65430,65437,65442],{"__ignoreMap":57},[62,63898,63899,63901],{"class":64,"line":65},[62,63900,69],{"class":68},[62,63902,52481],{"class":72},[62,63904,63905],{"class":64,"line":76},[62,63906,79],{"emptyLinePlaceholder":13},[62,63908,63909,63911],{"class":64,"line":82},[62,63910,27875],{"class":68},[62,63912,63800],{"class":72},[62,63914,63915],{"class":64,"line":89},[62,63916,79],{"emptyLinePlaceholder":13},[62,63918,63919,63921,63923,63925],{"class":64,"line":95},[62,63920,116],{"class":68},[62,63922,119],{"class":68},[62,63924,22289],{"class":122},[62,63926,126],{"class":72},[62,63928,63929],{"class":64,"line":101},[62,63930,79],{"emptyLinePlaceholder":13},[62,63932,63933,63935],{"class":64,"line":107},[62,63934,137],{"class":68},[62,63936,52021],{"class":72},[62,63938,63939,63941],{"class":64,"line":113},[62,63940,137],{"class":68},[62,63942,63831],{"class":72},[62,63944,63945,63947],{"class":64,"line":129},[62,63946,137],{"class":68},[62,63948,52028],{"class":72},[62,63950,63951,63953],{"class":64,"line":134},[62,63952,137],{"class":68},[62,63954,22360],{"class":72},[62,63956,63957,63959],{"class":64,"line":156},[62,63958,137],{"class":68},[62,63960,63850],{"class":72},[62,63962,63963,63965],{"class":64,"line":161},[62,63964,137],{"class":68},[62,63966,63857],{"class":72},[62,63968,63969,63971],{"class":64,"line":167},[62,63970,137],{"class":68},[62,63972,63864],{"class":72},[62,63974,63975,63977],{"class":64,"line":173},[62,63976,137],{"class":68},[62,63978,63871],{"class":72},[62,63980,63981],{"class":64,"line":179},[62,63982,79],{"emptyLinePlaceholder":13},[62,63984,63985,63987,63989,63991],{"class":64,"line":185},[62,63986,194],{"class":68},[62,63988,2469],{"class":72},[62,63990,52172],{"class":122},[62,63992,206],{"class":72},[62,63994,63995,63997],{"class":64,"line":191},[62,63996,360],{"class":68},[62,63998,52073],{"class":72},[62,64000,64001],{"class":64,"line":209},[62,64002,223],{"class":72},[62,64004,64005],{"class":64,"line":220},[62,64006,79],{"emptyLinePlaceholder":13},[62,64008,64009,64011,64013,64015,64017,64019],{"class":64,"line":226},[62,64010,194],{"class":68},[62,64012,200],{"class":68},[62,64014,52197],{"class":122},[62,64016,1049],{"class":72},[62,64018,52049],{"class":889},[62,64020,768],{"class":72},[62,64022,64023,64025,64027,64029],{"class":64,"line":231},[62,64024,2405],{"class":149},[62,64026,52068],{"class":72},[62,64028,146],{"class":68},[62,64030,52073],{"class":72},[62,64032,64033],{"class":64,"line":236},[62,64034,223],{"class":72},[62,64036,64037],{"class":64,"line":242},[62,64038,79],{"emptyLinePlaceholder":13},[62,64040,64041,64043,64045,64048],{"class":64,"line":247},[62,64042,194],{"class":68},[62,64044,2469],{"class":72},[62,64046,64047],{"class":122},"getMiddle",[62,64049,206],{"class":72},[62,64051,64052,64054],{"class":64,"line":252},[62,64053,360],{"class":68},[62,64055,64056],{"class":72}," middle;\n",[62,64058,64059],{"class":64,"line":257},[62,64060,223],{"class":72},[62,64062,64063],{"class":64,"line":271},[62,64064,79],{"emptyLinePlaceholder":13},[62,64066,64067,64069,64071,64074,64076,64079],{"class":64,"line":281},[62,64068,194],{"class":68},[62,64070,200],{"class":68},[62,64072,64073],{"class":122}," setMiddle",[62,64075,1049],{"class":72},[62,64077,64078],{"class":889},"middle",[62,64080,768],{"class":72},[62,64082,64083,64085,64088,64090],{"class":64,"line":286},[62,64084,2405],{"class":149},[62,64086,64087],{"class":72},".middle ",[62,64089,146],{"class":68},[62,64091,64056],{"class":72},[62,64093,64094],{"class":64,"line":291},[62,64095,223],{"class":72},[62,64097,64098],{"class":64,"line":296},[62,64099,79],{"emptyLinePlaceholder":13},[62,64101,64102,64104,64106,64108],{"class":64,"line":302},[62,64103,194],{"class":68},[62,64105,2469],{"class":72},[62,64107,52230],{"class":122},[62,64109,206],{"class":72},[62,64111,64112,64114],{"class":64,"line":308},[62,64113,360],{"class":68},[62,64115,52085],{"class":72},[62,64117,64118],{"class":64,"line":314},[62,64119,223],{"class":72},[62,64121,64122],{"class":64,"line":320},[62,64123,79],{"emptyLinePlaceholder":13},[62,64125,64126,64128,64130,64132,64134,64136],{"class":64,"line":326},[62,64127,194],{"class":68},[62,64129,200],{"class":68},[62,64131,52255],{"class":122},[62,64133,1049],{"class":72},[62,64135,52054],{"class":889},[62,64137,768],{"class":72},[62,64139,64140,64142,64144,64146],{"class":64,"line":338},[62,64141,2405],{"class":149},[62,64143,52080],{"class":72},[62,64145,146],{"class":68},[62,64147,52085],{"class":72},[62,64149,64150],{"class":64,"line":343},[62,64151,223],{"class":72},[62,64153,64154],{"class":64,"line":357},[62,64155,79],{"emptyLinePlaceholder":13},[62,64157,64158,64160,64162,64164],{"class":64,"line":366},[62,64159,194],{"class":68},[62,64161,2469],{"class":72},[62,64163,52288],{"class":122},[62,64165,206],{"class":72},[62,64167,64168,64170],{"class":64,"line":371},[62,64169,360],{"class":68},[62,64171,52097],{"class":72},[62,64173,64174],{"class":64,"line":376},[62,64175,223],{"class":72},[62,64177,64178],{"class":64,"line":16333},[62,64179,79],{"emptyLinePlaceholder":13},[62,64181,64182,64184,64186,64188,64190,64192],{"class":64,"line":16349},[62,64183,194],{"class":68},[62,64185,200],{"class":68},[62,64187,52313],{"class":122},[62,64189,1049],{"class":72},[62,64191,52059],{"class":889},[62,64193,768],{"class":72},[62,64195,64196,64198,64200,64202],{"class":64,"line":16365},[62,64197,2405],{"class":149},[62,64199,52092],{"class":72},[62,64201,146],{"class":68},[62,64203,52097],{"class":72},[62,64205,64206],{"class":64,"line":16381},[62,64207,223],{"class":72},[62,64209,64210],{"class":64,"line":16402},[62,64211,79],{"emptyLinePlaceholder":13},[62,64213,64214,64216,64219,64222],{"class":64,"line":16411},[62,64215,194],{"class":68},[62,64217,64218],{"class":72}," Date ",[62,64220,64221],{"class":122},"getDob",[62,64223,206],{"class":72},[62,64225,64226,64228],{"class":64,"line":16427},[62,64227,360],{"class":68},[62,64229,64230],{"class":72}," dob;\n",[62,64232,64233],{"class":64,"line":16448},[62,64234,223],{"class":72},[62,64236,64237],{"class":64,"line":16457},[62,64238,79],{"emptyLinePlaceholder":13},[62,64240,64241,64243,64245,64248,64251,64254],{"class":64,"line":16466},[62,64242,194],{"class":68},[62,64244,200],{"class":68},[62,64246,64247],{"class":122}," setDob",[62,64249,64250],{"class":72},"(Date ",[62,64252,64253],{"class":889},"dob",[62,64255,768],{"class":72},[62,64257,64258,64260,64263,64265],{"class":64,"line":16471},[62,64259,2405],{"class":149},[62,64261,64262],{"class":72},".dob ",[62,64264,146],{"class":68},[62,64266,64230],{"class":72},[62,64268,64269],{"class":64,"line":16487},[62,64270,223],{"class":72},[62,64272,64273],{"class":64,"line":16504},[62,64274,79],{"emptyLinePlaceholder":13},[62,64276,64277,64279,64281,64284],{"class":64,"line":16517},[62,64278,194],{"class":68},[62,64280,2469],{"class":72},[62,64282,64283],{"class":122},"getFoo",[62,64285,206],{"class":72},[62,64287,64288,64290],{"class":64,"line":16523},[62,64289,360],{"class":68},[62,64291,64292],{"class":72}," foo;\n",[62,64294,64295],{"class":64,"line":16532},[62,64296,223],{"class":72},[62,64298,64299],{"class":64,"line":16546},[62,64300,79],{"emptyLinePlaceholder":13},[62,64302,64303,64305,64307,64310,64312,64315],{"class":64,"line":16557},[62,64304,194],{"class":68},[62,64306,200],{"class":68},[62,64308,64309],{"class":122}," setFoo",[62,64311,1049],{"class":72},[62,64313,64314],{"class":889},"foo",[62,64316,768],{"class":72},[62,64318,64319,64321,64324,64326],{"class":64,"line":16563},[62,64320,2405],{"class":149},[62,64322,64323],{"class":72},".foo ",[62,64325,146],{"class":68},[62,64327,64292],{"class":72},[62,64329,64330],{"class":64,"line":16572},[62,64331,223],{"class":72},[62,64333,64334],{"class":64,"line":16581},[62,64335,79],{"emptyLinePlaceholder":13},[62,64337,64338,64340,64342,64345],{"class":64,"line":16590},[62,64339,194],{"class":68},[62,64341,2469],{"class":72},[62,64343,64344],{"class":122},"getBar",[62,64346,206],{"class":72},[62,64348,64349,64351],{"class":64,"line":16599},[62,64350,360],{"class":68},[62,64352,64353],{"class":72}," bar;\n",[62,64355,64356],{"class":64,"line":22013},[62,64357,223],{"class":72},[62,64359,64360],{"class":64,"line":22023},[62,64361,79],{"emptyLinePlaceholder":13},[62,64363,64364,64366,64368,64371,64373,64376],{"class":64,"line":22049},[62,64365,194],{"class":68},[62,64367,200],{"class":68},[62,64369,64370],{"class":122}," setBar",[62,64372,1049],{"class":72},[62,64374,64375],{"class":889},"bar",[62,64377,768],{"class":72},[62,64379,64380,64382,64385,64387],{"class":64,"line":22054},[62,64381,2405],{"class":149},[62,64383,64384],{"class":72},".bar ",[62,64386,146],{"class":68},[62,64388,64353],{"class":72},[62,64390,64391],{"class":64,"line":22059},[62,64392,223],{"class":72},[62,64394,64395],{"class":64,"line":22064},[62,64396,79],{"emptyLinePlaceholder":13},[62,64398,64399,64401,64403,64406],{"class":64,"line":22073},[62,64400,194],{"class":68},[62,64402,2469],{"class":72},[62,64404,64405],{"class":122},"getA",[62,64407,206],{"class":72},[62,64409,64410,64412],{"class":64,"line":22082},[62,64411,360],{"class":68},[62,64413,64414],{"class":72}," a;\n",[62,64416,64417],{"class":64,"line":30770},[62,64418,223],{"class":72},[62,64420,64421],{"class":64,"line":30775},[62,64422,79],{"emptyLinePlaceholder":13},[62,64424,64425,64427,64429,64432,64434,64436],{"class":64,"line":30791},[62,64426,194],{"class":68},[62,64428,200],{"class":68},[62,64430,64431],{"class":122}," setA",[62,64433,1049],{"class":72},[62,64435,677],{"class":889},[62,64437,768],{"class":72},[62,64439,64440,64442,64445,64447],{"class":64,"line":30808},[62,64441,2405],{"class":149},[62,64443,64444],{"class":72},".a ",[62,64446,146],{"class":68},[62,64448,64414],{"class":72},[62,64450,64451],{"class":64,"line":30822},[62,64452,223],{"class":72},[62,64454,64455],{"class":64,"line":30836},[62,64456,79],{"emptyLinePlaceholder":13},[62,64458,64459,64461,64463,64466],{"class":64,"line":30845},[62,64460,194],{"class":68},[62,64462,2469],{"class":72},[62,64464,64465],{"class":122},"getB",[62,64467,206],{"class":72},[62,64469,64470,64472],{"class":64,"line":30850},[62,64471,360],{"class":68},[62,64473,28892],{"class":72},[62,64475,64476],{"class":64,"line":30866},[62,64477,223],{"class":72},[62,64479,64480],{"class":64,"line":30889},[62,64481,79],{"emptyLinePlaceholder":13},[62,64483,64484,64486,64488,64491,64493,64496],{"class":64,"line":30911},[62,64485,194],{"class":68},[62,64487,200],{"class":68},[62,64489,64490],{"class":122}," setB",[62,64492,1049],{"class":72},[62,64494,64495],{"class":889},"b",[62,64497,768],{"class":72},[62,64499,64500,64502,64505,64507],{"class":64,"line":30933},[62,64501,2405],{"class":149},[62,64503,64504],{"class":72},".b ",[62,64506,146],{"class":68},[62,64508,28892],{"class":72},[62,64510,64511],{"class":64,"line":30955},[62,64512,223],{"class":72},[62,64514,64515],{"class":64,"line":30977},[62,64516,79],{"emptyLinePlaceholder":13},[62,64518,64519,64521,64523,64526],{"class":64,"line":30986},[62,64520,194],{"class":68},[62,64522,2469],{"class":72},[62,64524,64525],{"class":122},"getC",[62,64527,206],{"class":72},[62,64529,64530,64532],{"class":64,"line":30991},[62,64531,360],{"class":68},[62,64533,64534],{"class":72}," c;\n",[62,64536,64537],{"class":64,"line":31007},[62,64538,223],{"class":72},[62,64540,64541],{"class":64,"line":31021},[62,64542,79],{"emptyLinePlaceholder":13},[62,64544,64545,64547,64549,64552,64554,64556],{"class":64,"line":31037},[62,64546,194],{"class":68},[62,64548,200],{"class":68},[62,64550,64551],{"class":122}," setC",[62,64553,1049],{"class":72},[62,64555,40717],{"class":889},[62,64557,768],{"class":72},[62,64559,64560,64562,64565,64567],{"class":64,"line":31051},[62,64561,2405],{"class":149},[62,64563,64564],{"class":72},".c ",[62,64566,146],{"class":68},[62,64568,64534],{"class":72},[62,64570,64571],{"class":64,"line":31065},[62,64572,223],{"class":72},[62,64574,64575],{"class":64,"line":31085},[62,64576,79],{"emptyLinePlaceholder":13},[62,64578,64579,64581],{"class":64,"line":31094},[62,64580,2143],{"class":72},[62,64582,13555],{"class":68},[62,64584,64585,64587,64589,64591],{"class":64,"line":31100},[62,64586,194],{"class":68},[62,64588,2469],{"class":72},[62,64590,23175],{"class":122},[62,64592,206],{"class":72},[62,64594,64595,64597,64600],{"class":64,"line":31109},[62,64596,360],{"class":68},[62,64598,64599],{"class":1675}," \"User{\"",[62,64601,52363],{"class":68},[62,64603,64604,64606,64608,64610,64612,64614,64617,64619],{"class":64,"line":31114},[62,64605,52368],{"class":1675},[62,64607,4507],{"class":68},[62,64609,52373],{"class":72},[62,64611,1148],{"class":68},[62,64613,52378],{"class":1675},[62,64615,64616],{"class":149},"\\'",[62,64618,54412],{"class":1675},[62,64620,52363],{"class":68},[62,64622,64623,64626,64628,64631,64633,64635,64637,64639],{"class":64,"line":31130},[62,64624,64625],{"class":1675}," \", last='\"",[62,64627,4507],{"class":68},[62,64629,64630],{"class":72}," last ",[62,64632,1148],{"class":68},[62,64634,52378],{"class":1675},[62,64636,64616],{"class":149},[62,64638,54412],{"class":1675},[62,64640,52363],{"class":68},[62,64642,64643,64646,64648,64651,64653,64655,64657,64659],{"class":64,"line":31144},[62,64644,64645],{"class":1675}," \", email='\"",[62,64647,4507],{"class":68},[62,64649,64650],{"class":72}," email ",[62,64652,1148],{"class":68},[62,64654,52378],{"class":1675},[62,64656,64616],{"class":149},[62,64658,54412],{"class":1675},[62,64660,52363],{"class":68},[62,64662,64663,64666],{"class":64,"line":31160},[62,64664,64665],{"class":1675}," '}'",[62,64667,153],{"class":72},[62,64669,64670],{"class":64,"line":31174},[62,64671,223],{"class":72},[62,64673,64674],{"class":64,"line":31188},[62,64675,79],{"emptyLinePlaceholder":13},[62,64677,64678,64680],{"class":64,"line":31202},[62,64679,2143],{"class":72},[62,64681,13555],{"class":68},[62,64683,64684,64686,64688,64691,64694,64697],{"class":64,"line":31211},[62,64685,194],{"class":68},[62,64687,1043],{"class":68},[62,64689,64690],{"class":122}," equals",[62,64692,64693],{"class":72},"(Object ",[62,64695,64696],{"class":889},"o",[62,64698,768],{"class":72},[62,64700,64701,64703,64705,64707,64709,64712,64714,64716],{"class":64,"line":31217},[62,64702,12741],{"class":68},[62,64704,744],{"class":72},[62,64706,1295],{"class":149},[62,64708,26800],{"class":68},[62,64710,64711],{"class":72}," o) ",[62,64713,22008],{"class":68},[62,64715,1227],{"class":149},[62,64717,153],{"class":72},[62,64719,64720,64722,64725,64727,64729,64732,64735,64737,64739,64742,64745,64748,64750,64752],{"class":64,"line":31226},[62,64721,12741],{"class":68},[62,64723,64724],{"class":72}," (o ",[62,64726,28328],{"class":68},[62,64728,13324],{"class":149},[62,64730,64731],{"class":68}," ||",[62,64733,64734],{"class":122}," getClass",[62,64736,5398],{"class":72},[62,64738,13321],{"class":68},[62,64740,64741],{"class":72}," o.",[62,64743,64744],{"class":122},"getClass",[62,64746,64747],{"class":72},"()) ",[62,64749,22008],{"class":68},[62,64751,1165],{"class":149},[62,64753,153],{"class":72},[62,64755,64756],{"class":64,"line":31235},[62,64757,79],{"emptyLinePlaceholder":13},[62,64759,64760,64763,64765],{"class":64,"line":31240},[62,64761,64762],{"class":72}," User user ",[62,64764,146],{"class":68},[62,64766,64767],{"class":72}," (User) o;\n",[62,64769,64770],{"class":64,"line":31249},[62,64771,79],{"emptyLinePlaceholder":13},[62,64773,64774,64776,64778,64780,64783,64785,64788,64790,64792],{"class":64,"line":31268},[62,64775,12741],{"class":68},[62,64777,744],{"class":72},[62,64779,6277],{"class":68},[62,64781,64782],{"class":72},"first.",[62,64784,3232],{"class":122},[62,64786,64787],{"class":72},"(user.first)) ",[62,64789,22008],{"class":68},[62,64791,1165],{"class":149},[62,64793,153],{"class":72},[62,64795,64796,64798,64801,64803,64805,64808,64811,64814,64816,64819,64821,64824,64826,64828,64830,64832,64834],{"class":64,"line":31277},[62,64797,12741],{"class":68},[62,64799,64800],{"class":72}," (middle ",[62,64802,13321],{"class":68},[62,64804,13324],{"class":149},[62,64806,64807],{"class":68}," ?",[62,64809,64810],{"class":68}," !",[62,64812,64813],{"class":72},"middle.",[62,64815,3232],{"class":122},[62,64817,64818],{"class":72},"(user.middle) ",[62,64820,1266],{"class":68},[62,64822,64823],{"class":72}," user.middle ",[62,64825,13321],{"class":68},[62,64827,13324],{"class":149},[62,64829,5024],{"class":72},[62,64831,22008],{"class":68},[62,64833,1165],{"class":149},[62,64835,153],{"class":72},[62,64837,64838,64840,64842,64844,64847,64849,64852,64854,64856],{"class":64,"line":31286},[62,64839,12741],{"class":68},[62,64841,744],{"class":72},[62,64843,6277],{"class":68},[62,64845,64846],{"class":72},"last.",[62,64848,3232],{"class":122},[62,64850,64851],{"class":72},"(user.last)) ",[62,64853,22008],{"class":68},[62,64855,1165],{"class":149},[62,64857,153],{"class":72},[62,64859,64861,64863,64865,64867,64870,64872,64875,64877,64879],{"class":64,"line":64860},115,[62,64862,12741],{"class":68},[62,64864,744],{"class":72},[62,64866,6277],{"class":68},[62,64868,64869],{"class":72},"email.",[62,64871,3232],{"class":122},[62,64873,64874],{"class":72},"(user.email)) ",[62,64876,22008],{"class":68},[62,64878,1165],{"class":149},[62,64880,153],{"class":72},[62,64882,64884,64886,64888,64890,64893,64895,64898,64900,64902],{"class":64,"line":64883},116,[62,64885,12741],{"class":68},[62,64887,744],{"class":72},[62,64889,6277],{"class":68},[62,64891,64892],{"class":72},"dob.",[62,64894,3232],{"class":122},[62,64896,64897],{"class":72},"(user.dob)) ",[62,64899,22008],{"class":68},[62,64901,1165],{"class":149},[62,64903,153],{"class":72},[62,64905,64907,64909,64912,64914,64916,64918,64920,64923,64925,64928,64930,64933,64935,64937,64939,64941,64943],{"class":64,"line":64906},117,[62,64908,12741],{"class":68},[62,64910,64911],{"class":72}," (foo ",[62,64913,13321],{"class":68},[62,64915,13324],{"class":149},[62,64917,64807],{"class":68},[62,64919,64810],{"class":68},[62,64921,64922],{"class":72},"foo.",[62,64924,3232],{"class":122},[62,64926,64927],{"class":72},"(user.foo) ",[62,64929,1266],{"class":68},[62,64931,64932],{"class":72}," user.foo ",[62,64934,13321],{"class":68},[62,64936,13324],{"class":149},[62,64938,5024],{"class":72},[62,64940,22008],{"class":68},[62,64942,1165],{"class":149},[62,64944,153],{"class":72},[62,64946,64948,64950,64953,64955,64957,64959,64961,64964,64966,64969,64971,64974,64976,64978,64980,64982,64984],{"class":64,"line":64947},118,[62,64949,12741],{"class":68},[62,64951,64952],{"class":72}," (bar ",[62,64954,13321],{"class":68},[62,64956,13324],{"class":149},[62,64958,64807],{"class":68},[62,64960,64810],{"class":68},[62,64962,64963],{"class":72},"bar.",[62,64965,3232],{"class":122},[62,64967,64968],{"class":72},"(user.bar) ",[62,64970,1266],{"class":68},[62,64972,64973],{"class":72}," user.bar ",[62,64975,13321],{"class":68},[62,64977,13324],{"class":149},[62,64979,5024],{"class":72},[62,64981,22008],{"class":68},[62,64983,1165],{"class":149},[62,64985,153],{"class":72},[62,64987,64989,64991,64994,64996,64998,65000,65002,65005,65007,65010,65012,65015,65017,65019,65021,65023,65025],{"class":64,"line":64988},119,[62,64990,12741],{"class":68},[62,64992,64993],{"class":72}," (a ",[62,64995,13321],{"class":68},[62,64997,13324],{"class":149},[62,64999,64807],{"class":68},[62,65001,64810],{"class":68},[62,65003,65004],{"class":72},"a.",[62,65006,3232],{"class":122},[62,65008,65009],{"class":72},"(user.a) ",[62,65011,1266],{"class":68},[62,65013,65014],{"class":72}," user.a ",[62,65016,13321],{"class":68},[62,65018,13324],{"class":149},[62,65020,5024],{"class":72},[62,65022,22008],{"class":68},[62,65024,1165],{"class":149},[62,65026,153],{"class":72},[62,65028,65030,65032,65034,65036,65038,65040,65042,65045,65047,65050,65052,65055,65057,65059,65061,65063,65065],{"class":64,"line":65029},120,[62,65031,12741],{"class":68},[62,65033,29014],{"class":72},[62,65035,13321],{"class":68},[62,65037,13324],{"class":149},[62,65039,64807],{"class":68},[62,65041,64810],{"class":68},[62,65043,65044],{"class":72},"b.",[62,65046,3232],{"class":122},[62,65048,65049],{"class":72},"(user.b) ",[62,65051,1266],{"class":68},[62,65053,65054],{"class":72}," user.b ",[62,65056,13321],{"class":68},[62,65058,13324],{"class":149},[62,65060,5024],{"class":72},[62,65062,22008],{"class":68},[62,65064,1165],{"class":149},[62,65066,153],{"class":72},[62,65068,65070,65072,65075,65077,65079,65081,65084,65086,65089,65091,65094,65096,65098],{"class":64,"line":65069},121,[62,65071,360],{"class":68},[62,65073,65074],{"class":72}," c ",[62,65076,13321],{"class":68},[62,65078,13324],{"class":149},[62,65080,64807],{"class":68},[62,65082,65083],{"class":72}," c.",[62,65085,3232],{"class":122},[62,65087,65088],{"class":72},"(user.c) ",[62,65090,1266],{"class":68},[62,65092,65093],{"class":72}," user.c ",[62,65095,28328],{"class":68},[62,65097,13324],{"class":149},[62,65099,153],{"class":72},[62,65101,65103],{"class":64,"line":65102},122,[62,65104,223],{"class":72},[62,65106,65108],{"class":64,"line":65107},123,[62,65109,79],{"emptyLinePlaceholder":13},[62,65111,65113,65115],{"class":64,"line":65112},124,[62,65114,2143],{"class":72},[62,65116,13555],{"class":68},[62,65118,65120,65122,65124,65127],{"class":64,"line":65119},125,[62,65121,194],{"class":68},[62,65123,140],{"class":68},[62,65125,65126],{"class":122}," hashCode",[62,65128,206],{"class":72},[62,65130,65132,65134,65136,65138,65141,65144],{"class":64,"line":65131},126,[62,65133,5681],{"class":68},[62,65135,5389],{"class":72},[62,65137,146],{"class":68},[62,65139,65140],{"class":72}," first.",[62,65142,65143],{"class":122},"hashCode",[62,65145,822],{"class":72},[62,65147,65149,65152,65154,65157,65160,65162,65164,65166,65168,65170,65172,65175,65177,65179,65181,65183],{"class":64,"line":65148},127,[62,65150,65151],{"class":72}," result ",[62,65153,146],{"class":68},[62,65155,65156],{"class":149}," 31",[62,65158,65159],{"class":68}," *",[62,65161,5389],{"class":72},[62,65163,1148],{"class":68},[62,65165,64800],{"class":72},[62,65167,13321],{"class":68},[62,65169,13324],{"class":149},[62,65171,64807],{"class":68},[62,65173,65174],{"class":72}," middle.",[62,65176,65143],{"class":122},[62,65178,5398],{"class":72},[62,65180,1266],{"class":68},[62,65182,150],{"class":149},[62,65184,1133],{"class":72},[62,65186,65188,65190,65192,65194,65196,65198,65200,65203,65205],{"class":64,"line":65187},128,[62,65189,65151],{"class":72},[62,65191,146],{"class":68},[62,65193,65156],{"class":149},[62,65195,65159],{"class":68},[62,65197,5389],{"class":72},[62,65199,1148],{"class":68},[62,65201,65202],{"class":72}," last.",[62,65204,65143],{"class":122},[62,65206,822],{"class":72},[62,65208,65210,65212,65214,65216,65218,65220,65222,65225,65227],{"class":64,"line":65209},129,[62,65211,65151],{"class":72},[62,65213,146],{"class":68},[62,65215,65156],{"class":149},[62,65217,65159],{"class":68},[62,65219,5389],{"class":72},[62,65221,1148],{"class":68},[62,65223,65224],{"class":72}," email.",[62,65226,65143],{"class":122},[62,65228,822],{"class":72},[62,65230,65232,65234,65236,65238,65240,65242,65244,65247,65249],{"class":64,"line":65231},130,[62,65233,65151],{"class":72},[62,65235,146],{"class":68},[62,65237,65156],{"class":149},[62,65239,65159],{"class":68},[62,65241,5389],{"class":72},[62,65243,1148],{"class":68},[62,65245,65246],{"class":72}," dob.",[62,65248,65143],{"class":122},[62,65250,822],{"class":72},[62,65252,65254,65256,65258,65260,65262,65264,65266,65268,65270,65272,65274,65277,65279,65281,65283,65285],{"class":64,"line":65253},131,[62,65255,65151],{"class":72},[62,65257,146],{"class":68},[62,65259,65156],{"class":149},[62,65261,65159],{"class":68},[62,65263,5389],{"class":72},[62,65265,1148],{"class":68},[62,65267,64911],{"class":72},[62,65269,13321],{"class":68},[62,65271,13324],{"class":149},[62,65273,64807],{"class":68},[62,65275,65276],{"class":72}," foo.",[62,65278,65143],{"class":122},[62,65280,5398],{"class":72},[62,65282,1266],{"class":68},[62,65284,150],{"class":149},[62,65286,1133],{"class":72},[62,65288,65290,65292,65294,65296,65298,65300,65302,65304,65306,65308,65310,65313,65315,65317,65319,65321],{"class":64,"line":65289},132,[62,65291,65151],{"class":72},[62,65293,146],{"class":68},[62,65295,65156],{"class":149},[62,65297,65159],{"class":68},[62,65299,5389],{"class":72},[62,65301,1148],{"class":68},[62,65303,64952],{"class":72},[62,65305,13321],{"class":68},[62,65307,13324],{"class":149},[62,65309,64807],{"class":68},[62,65311,65312],{"class":72}," bar.",[62,65314,65143],{"class":122},[62,65316,5398],{"class":72},[62,65318,1266],{"class":68},[62,65320,150],{"class":149},[62,65322,1133],{"class":72},[62,65324,65326,65328,65330,65332,65334,65336,65338,65340,65342,65344,65346,65348,65350,65352,65354,65356],{"class":64,"line":65325},133,[62,65327,65151],{"class":72},[62,65329,146],{"class":68},[62,65331,65156],{"class":149},[62,65333,65159],{"class":68},[62,65335,5389],{"class":72},[62,65337,1148],{"class":68},[62,65339,64993],{"class":72},[62,65341,13321],{"class":68},[62,65343,13324],{"class":149},[62,65345,64807],{"class":68},[62,65347,48661],{"class":72},[62,65349,65143],{"class":122},[62,65351,5398],{"class":72},[62,65353,1266],{"class":68},[62,65355,150],{"class":149},[62,65357,1133],{"class":72},[62,65359,65361,65363,65365,65367,65369,65371,65373,65375,65377,65379,65381,65384,65386,65388,65390,65392],{"class":64,"line":65360},134,[62,65362,65151],{"class":72},[62,65364,146],{"class":68},[62,65366,65156],{"class":149},[62,65368,65159],{"class":68},[62,65370,5389],{"class":72},[62,65372,1148],{"class":68},[62,65374,29014],{"class":72},[62,65376,13321],{"class":68},[62,65378,13324],{"class":149},[62,65380,64807],{"class":68},[62,65382,65383],{"class":72}," b.",[62,65385,65143],{"class":122},[62,65387,5398],{"class":72},[62,65389,1266],{"class":68},[62,65391,150],{"class":149},[62,65393,1133],{"class":72},[62,65395,65397,65399,65401,65403,65405,65407,65409,65412,65414,65416,65418,65420,65422,65424,65426,65428],{"class":64,"line":65396},135,[62,65398,65151],{"class":72},[62,65400,146],{"class":68},[62,65402,65156],{"class":149},[62,65404,65159],{"class":68},[62,65406,5389],{"class":72},[62,65408,1148],{"class":68},[62,65410,65411],{"class":72}," (c ",[62,65413,13321],{"class":68},[62,65415,13324],{"class":149},[62,65417,64807],{"class":68},[62,65419,65083],{"class":72},[62,65421,65143],{"class":122},[62,65423,5398],{"class":72},[62,65425,1266],{"class":68},[62,65427,150],{"class":149},[62,65429,1133],{"class":72},[62,65431,65433,65435],{"class":64,"line":65432},136,[62,65434,360],{"class":68},[62,65436,12852],{"class":72},[62,65438,65440],{"class":64,"line":65439},137,[62,65441,223],{"class":72},[62,65443,65445],{"class":64,"line":65444},138,[62,65446,379],{"class":72},[636,65448,65450],{"id":65449},"after-project-lombok","After Project Lombok",[22,65452,65453,65454,57647],{},"Now that we have Lombok in our application we can reduce this noise. Just add that @Data annotation to your class. This will create a getter and setter for each property defined in your class. It will also create a to string, equals and hash code method. You can get pretty granular control with some of the annotations so again I would ",[677,65455,65458],{"href":65456,"rel":65457},"http://jnb.ociweb.com/jnb/jnbJan2010.html",[681],"refer you to the documentation",[52,65460,65462],{"className":54,"code":65461,"language":56,"meta":57,"style":57},"package com.therealdanvega;\n\nimport lombok.Data;\nimport java.util.Date;\n\n@Data\npublic class User {\n\n private String first;\n private String middle;\n private String last;\n private String email;\n private Date dob;\n private String foo;\n private String bar;\n private String a,b,c;\n\n}\n",[59,65463,65464,65470,65474,65480,65486,65490,65496,65506,65510,65516,65522,65528,65534,65540,65546,65552,65558,65562],{"__ignoreMap":57},[62,65465,65466,65468],{"class":64,"line":65},[62,65467,69],{"class":68},[62,65469,52481],{"class":72},[62,65471,65472],{"class":64,"line":76},[62,65473,79],{"emptyLinePlaceholder":13},[62,65475,65476,65478],{"class":64,"line":82},[62,65477,27875],{"class":68},[62,65479,58700],{"class":72},[62,65481,65482,65484],{"class":64,"line":89},[62,65483,27875],{"class":68},[62,65485,63800],{"class":72},[62,65487,65488],{"class":64,"line":95},[62,65489,79],{"emptyLinePlaceholder":13},[62,65491,65492,65494],{"class":64,"line":101},[62,65493,942],{"class":72},[62,65495,9388],{"class":68},[62,65497,65498,65500,65502,65504],{"class":64,"line":107},[62,65499,116],{"class":68},[62,65501,119],{"class":68},[62,65503,22289],{"class":122},[62,65505,126],{"class":72},[62,65507,65508],{"class":64,"line":113},[62,65509,79],{"emptyLinePlaceholder":13},[62,65511,65512,65514],{"class":64,"line":129},[62,65513,137],{"class":68},[62,65515,52021],{"class":72},[62,65517,65518,65520],{"class":64,"line":134},[62,65519,137],{"class":68},[62,65521,63831],{"class":72},[62,65523,65524,65526],{"class":64,"line":156},[62,65525,137],{"class":68},[62,65527,52028],{"class":72},[62,65529,65530,65532],{"class":64,"line":161},[62,65531,137],{"class":68},[62,65533,22360],{"class":72},[62,65535,65536,65538],{"class":64,"line":167},[62,65537,137],{"class":68},[62,65539,63850],{"class":72},[62,65541,65542,65544],{"class":64,"line":173},[62,65543,137],{"class":68},[62,65545,63857],{"class":72},[62,65547,65548,65550],{"class":64,"line":179},[62,65549,137],{"class":68},[62,65551,63864],{"class":72},[62,65553,65554,65556],{"class":64,"line":185},[62,65555,137],{"class":68},[62,65557,63871],{"class":72},[62,65559,65560],{"class":64,"line":191},[62,65561,79],{"emptyLinePlaceholder":13},[62,65563,65564],{"class":64,"line":209},[62,65565,379],{"class":72},[22,65567,65568],{},"The great thing about Lombok is that it integrates well with the IDE. I am using IntelliJ but this will work in Eclipse as well. If you look over at your project structure you can see that all of these methods now exist without us having to write them. We also don't have to guess as to whether it worked or not, we can clearly see that it did. Not to mention, look how clean our class looks! ",[22,65570,65571],{},[653,65572],{"alt":65573,"src":65574},"Lombok Demo","/images/blog/2017/03/31/lombok_demo_4-293x300.png",[26,65576,1499],{"id":1498},[22,65578,65579],{},"There is absolutely no reason you shouldn't be using Project Lombok today. If it's not a brand new project you can simply drop in the dependency and go. I hope you found this tutorial useful and I want to leave you with a question to hopefully kick start a discussion. ",[22,65581,50754,65582,65585],{},[646,65583,65584],{},"Question",": What are some of your favorite tools & libraries to help in the creation of better, cleaner code? _",[1527,65587,65588],{},"html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .spoOn, html code.shiki .spoOn{--shiki-default:#E36209;--shiki-dark:#FFAB70;--shiki-sepia:#E36209}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}",{"title":57,"searchDepth":76,"depth":76,"links":65590},[65591,65592,65596],{"id":63727,"depth":76,"text":63728},{"id":63751,"depth":76,"text":63752,"children":65593},[65594,65595],{"id":63755,"depth":82,"text":63756},{"id":65449,"depth":82,"text":65450},{"id":1498,"depth":76,"text":1499},{"_id":65598,"path":65599,"title":65600,"description":65600,"meta":65601,"body":65606},"content/blog/2017/03/27/spring-stereotype-annotations.md","/blog/2017/03/27/spring-stereotype-annotations","Spring Stereotype Annotations",{"slug":65602,"date":65603,"published":13,"tags":65604,"author":-1,"cover":65605,"excerpt":-1},"spring-stereotype-annotations","2017-03-27T08:00:15-04:00",[11002],"./stereotype_annotations.png",{"type":19,"value":65607,"toc":66829},[65608,65611,65615,65624,65634,65637,65640,65651,65654,65658,65666,65707,65728,65749,65753,65762,65768,65773,65776,65781,65784,65834,65885,65888,65893,65900,66049,66055,66059,66068,66073,66076,66418,66421,66607,66610,66817,66819,66822,66827],[22,65609,65610],{},"When software developers get introduced to the Spring Framework there are some concepts that can be a little confusing. The good news is once we wrap our head around them they can become second nature. Spring has some specialized annotations called Stereotype Annotations. If that concept is new to you don’t worry, we are going to break everything down for you in this article.",[26,65612,65614],{"id":65613},"stereotype-annotation-defined","Stereotype & Annotation Defined",[22,65616,65617,65618,65623],{},"Before we dive into what Spring Stereotype annotations are I think it's important for us to understand what these two words mean. The word stereotype is defined in the ",[677,65619,65622],{"href":65620,"rel":65621},"https://www.merriam-webster.com/dictionary/stereotype",[681],"Merriam-Webster dictionary"," as:",[29685,65625,65626],{},[22,65627,65628],{},[4534,65629,65630,65633],{},[646,65631,65632],{},"stereotype: (noun)"," something conforming to a fixed or general pattern; especially : a standardized mental picture that is held in common by members of a group and that represents an oversimplified opinion, prejudiced attitude, or uncritical judgment",[22,65635,65636],{},"You have probably come across stereotypes in real life. Examples of this could be that all Irish people are drunks or all blondes are unintelligent. We know that this, of course, is not only wrong but also extremely hurtful. In real life, you shouldn’t judge someone based on characteristics or because they belong to a particular group. While this isn’t a good practice in real life it is something we will do this in Spring.",[22,65638,65639],{},"We know that a controller's role in MVC pattern is to direct traffic and route requests. We know that a service is a place where we can place all of our business logic. Finally, we know that a repository is a place for us to implement data access layers for various persistence stores. In a Spring MVC project, it is important for us to group classes based on their role in the application. The word annotation is defined in the Merriam-Webster dictionary as:",[29685,65641,65642],{},[22,65643,65644,65647,65648],{},[646,65645,65646],{},"annotation: (noun)"," ",[4534,65649,65650],{},"a note added (as to a statute) by way of comment or explanation often furnishing summaries of relevant court decisions",[22,65652,65653],{},"In the real world, we use the word annotation as a note or comment to describe something further. In Java, we use them much as the same but there are some technical things going on behind the scenes. We describe a class in our project as being a controller by using the @Controller annotation. We know just by looking at that class that it is a Controller but so does the framework which will do some cool things to this class behind the scenes.",[26,65655,65657],{"id":65656},"spring-stereotypes-and-how-to-use-them","Spring Stereotypes and how to use them",[22,65659,65660,65661,2755],{},"Now that we understand what these words mean it might give us some insight as to what role they play in our Spring projects. When a class is annotated with one of the following Stereotypes Spring will automatically register them in the application context. This makes the class available for dependency injection in other classes and this become vital to building out our applications. These classes can all be found under the ",[677,65662,65665],{"href":65663,"rel":65664},"http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/stereotype/package-summary.html",[681],"org.springframework.stereotype package",[11922,65667,65668,65677],{},[11925,65669,65670],{},[11928,65671,65672,65675],{},[11931,65673,65674],{},"Annotation",[11931,65676,11939],{},[11941,65678,65679,65686,65693,65700],{},[11928,65680,65681,65683],{},[11946,65682,55433],{},[11946,65684,65685],{},"Indicates that an annotated class is a \"component\"",[11928,65687,65688,65690],{},[11946,65689,55837],{},[11946,65691,65692],{},"Indicates that an annotated class is a \"Controller\" (e.g.",[11928,65694,65695,65697],{},[11946,65696,55842],{},[11946,65698,65699],{},"Indicates that an annotated class is a \"Service\", originally defined by Domain-Driven Design (Evans, 2003) as \"an operation offered as an interface that stands alone in the model, with no encapsulated state.\"",[11928,65701,65702,65704],{},[11946,65703,55845],{},[11946,65705,65706],{},"Indicates that an annotated class is a \"Repository\", originally defined by Domain-Driven Design (Evans, 2003) as \"a mechanism for encapsulating storage, retrieval, and search behavior which emulates a collection of objects\".",[29685,65708,65709],{},[22,65710,65711,65712,65716,65717,65722,65723,65727],{},"You might have expected to see ",[677,65713,40266],{"href":65714,"rel":65715},"https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/bind/annotation/RestController.html",[681]," in this list but it was omitted on purpose. @RestController is a convenience annotation that is itself annotated with ",[677,65718,55837],{"href":65719,"rel":65720,"title":65721},"https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/stereotype/Controller.html",[681],"annotation in org.springframework.stereotype"," and [@ResponseBody](",[677,65724,65725],{"href":65725,"rel":65726},"https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/bind/annotation/ResponseBody.html",[681],"\" annotation in org.springframework.web.bind.annotation\").",[22,65729,65730,65731,65733,65734,65736,65737,65739,65740,65742,65743,65745,65746,65748],{},"There is a reason that ",[59,65732,55433],{}," was at the top of this list. ",[59,65735,55433],{}," is a generic stereotype annotation for any Spring-managed component. This means that if you want to register a class in the application context you can use ",[59,65738,55433],{}," to do so. The other stereotypes (Controller, Service, Repository) are simply specializations of the ",[59,65741,55433],{}," class. In fact, if you look at their source code you will see they themselves are annotated with ",[59,65744,55433],{},". If this is true, why wouldn't we just annotate everything with ",[59,65747,55433],{},"? First, it helps us and other developers classify a particular class. We can look at a class with the @Controller annotation and understand right away what its purpose is. Second, as we will see in the demo, it also helps us define well crafted point-cut demarcations when we get into using AOP. This should all make a little more sense as we move through a demo, so let's get to it.",[26,65750,65752],{"id":65751},"spring-stereotype-annotation-demo-project","Spring Stereotype Annotation Demo Project",[22,65754,65755,65756,65761],{},"Now that we know what classes are available to us I want to ",[677,65757,65760],{"href":65758,"rel":65759},"https://github.com/cfaddict/stereotype-annotation-tutorial",[681],"create a project"," that shows this off. In this project, we are going to select the AOP, Web & Actuator dependencies.",[22,65763,65764],{},[653,65765],{"alt":65766,"src":65767},"Spring Stereotype","./new_project_1-300x189.png",[22,65769,65770],{},[653,65771],{"alt":65766,"src":65772},"./new_project_2-300x189.png",[22,65774,65775],{},"I am going to keep this demo intentionally simple so we can focus on the issue at hand. No matter how small or large the project is going to be I like to break my classes up into packages. In this case, I am going to create a controller, service and aop package. I am going to create both a Stereotype Controller and Service in their respective packages but I am not going to annotate these classes just yet.",[22,65777,65778],{},[653,65779],{"alt":65766,"src":65780},"./stereotype_step1-300x203.png",[22,65782,65783],{},"If you try and run the project at this point it will run but it won't do much. If you hit the /beans actuator endpoint you won't see the controller or service registered in the application context. If we were to annotate these 2 classes with the component annotation they would now be registered beans. We could do that but this goes back to us defining (stereotyping) what these classes are going to be used for. I am going to update these classes with the @Controller & @Service annotation respectively.",[52,65785,65787],{"className":54,"code":65786,"language":56,"meta":57,"style":57},"package com.therealdanvega.controller;\n\nimport org.springframework.stereotype.Controller;\n\n@Controller\npublic class StereotypeController {\n\n}\n",[59,65788,65789,65795,65799,65805,65809,65815,65826,65830],{"__ignoreMap":57},[62,65790,65791,65793],{"class":64,"line":65},[62,65792,69],{"class":68},[62,65794,49492],{"class":72},[62,65796,65797],{"class":64,"line":76},[62,65798,79],{"emptyLinePlaceholder":13},[62,65800,65801,65803],{"class":64,"line":82},[62,65802,27875],{"class":68},[62,65804,49510],{"class":72},[62,65806,65807],{"class":64,"line":89},[62,65808,79],{"emptyLinePlaceholder":13},[62,65810,65811,65813],{"class":64,"line":95},[62,65812,942],{"class":72},[62,65814,16624],{"class":68},[62,65816,65817,65819,65821,65824],{"class":64,"line":101},[62,65818,116],{"class":68},[62,65820,119],{"class":68},[62,65822,65823],{"class":122}," StereotypeController",[62,65825,126],{"class":72},[62,65827,65828],{"class":64,"line":107},[62,65829,79],{"emptyLinePlaceholder":13},[62,65831,65832],{"class":64,"line":113},[62,65833,379],{"class":72},[52,65835,65837],{"className":54,"code":65836,"language":56,"meta":57,"style":57},"package com.therealdanvega.service;\n\nimport org.springframework.stereotype.Service;\n\n@Service\npublic class StereotypeService {\n\n}\n",[59,65838,65839,65846,65850,65856,65860,65866,65877,65881],{"__ignoreMap":57},[62,65840,65841,65843],{"class":64,"line":65},[62,65842,69],{"class":68},[62,65844,65845],{"class":72}," com.therealdanvega.service;\n",[62,65847,65848],{"class":64,"line":76},[62,65849,79],{"emptyLinePlaceholder":13},[62,65851,65852,65854],{"class":64,"line":82},[62,65853,27875],{"class":68},[62,65855,28194],{"class":72},[62,65857,65858],{"class":64,"line":89},[62,65859,79],{"emptyLinePlaceholder":13},[62,65861,65862,65864],{"class":64,"line":95},[62,65863,942],{"class":72},[62,65865,945],{"class":68},[62,65867,65868,65870,65872,65875],{"class":64,"line":101},[62,65869,116],{"class":68},[62,65871,119],{"class":68},[62,65873,65874],{"class":122}," StereotypeService",[62,65876,126],{"class":72},[62,65878,65879],{"class":64,"line":107},[62,65880,79],{"emptyLinePlaceholder":13},[62,65882,65883],{"class":64,"line":113},[62,65884,379],{"class":72},[22,65886,65887],{},"If I run this project now and pull up the /beans actuator endpoint I will see these classes registered in the application context.",[22,65889,65890],{},[653,65891],{"alt":65766,"src":65892},"./stereotype_beans-1024x356.png",[22,65894,65895,65896,65899],{},"This means that these classes are now available for dependency injection. If we go into our controller we can auto wire the service into our controller using constructor injection. This is new to Spring 4.3 and we no longer need to annotate this with ",[59,65897,65898],{},"@Autowired"," if there is only a single constructor.",[52,65901,65903],{"className":54,"code":65902,"language":56,"meta":57,"style":57},"package com.therealdanvega.controller;\n\nimport com.therealdanvega.service.StereotypeService;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class StereotypeController {\n\n private StereotypeService stereotypeService;\n\n public StereotypeController(StereotypeService stereotypeService) {\n this.stereotypeService = stereotypeService;\n }\n\n @RequestMapping(\"/\")\n public String home(){\n return stereotypeService.sayHello();\n }\n\n}\n",[59,65904,65905,65911,65915,65922,65928,65934,65938,65944,65954,65958,65965,65969,65983,65995,65999,66003,66015,66025,66037,66041,66045],{"__ignoreMap":57},[62,65906,65907,65909],{"class":64,"line":65},[62,65908,69],{"class":68},[62,65910,49492],{"class":72},[62,65912,65913],{"class":64,"line":76},[62,65914,79],{"emptyLinePlaceholder":13},[62,65916,65917,65919],{"class":64,"line":82},[62,65918,27875],{"class":68},[62,65920,65921],{"class":72}," com.therealdanvega.service.StereotypeService;\n",[62,65923,65924,65926],{"class":64,"line":89},[62,65925,27875],{"class":68},[62,65927,49517],{"class":72},[62,65929,65930,65932],{"class":64,"line":95},[62,65931,27875],{"class":68},[62,65933,27892],{"class":72},[62,65935,65936],{"class":64,"line":101},[62,65937,79],{"emptyLinePlaceholder":13},[62,65939,65940,65942],{"class":64,"line":107},[62,65941,942],{"class":72},[62,65943,2342],{"class":68},[62,65945,65946,65948,65950,65952],{"class":64,"line":113},[62,65947,116],{"class":68},[62,65949,119],{"class":68},[62,65951,65823],{"class":122},[62,65953,126],{"class":72},[62,65955,65956],{"class":64,"line":129},[62,65957,79],{"emptyLinePlaceholder":13},[62,65959,65960,65962],{"class":64,"line":134},[62,65961,137],{"class":68},[62,65963,65964],{"class":72}," StereotypeService stereotypeService;\n",[62,65966,65967],{"class":64,"line":156},[62,65968,79],{"emptyLinePlaceholder":13},[62,65970,65971,65973,65975,65978,65981],{"class":64,"line":161},[62,65972,194],{"class":68},[62,65974,65823],{"class":122},[62,65976,65977],{"class":72},"(StereotypeService ",[62,65979,65980],{"class":889},"stereotypeService",[62,65982,768],{"class":72},[62,65984,65985,65987,65990,65992],{"class":64,"line":167},[62,65986,2405],{"class":149},[62,65988,65989],{"class":72},".stereotypeService ",[62,65991,146],{"class":68},[62,65993,65994],{"class":72}," stereotypeService;\n",[62,65996,65997],{"class":64,"line":173},[62,65998,223],{"class":72},[62,66000,66001],{"class":64,"line":179},[62,66002,79],{"emptyLinePlaceholder":13},[62,66004,66005,66007,66009,66011,66013],{"class":64,"line":185},[62,66006,2143],{"class":72},[62,66008,10592],{"class":68},[62,66010,2109],{"class":72},[62,66012,15635],{"class":1675},[62,66014,2212],{"class":72},[62,66016,66017,66019,66021,66023],{"class":64,"line":191},[62,66018,194],{"class":68},[62,66020,2469],{"class":72},[62,66022,18647],{"class":122},[62,66024,57791],{"class":72},[62,66026,66027,66029,66032,66035],{"class":64,"line":209},[62,66028,360],{"class":68},[62,66030,66031],{"class":72}," stereotypeService.",[62,66033,66034],{"class":122},"sayHello",[62,66036,822],{"class":72},[62,66038,66039],{"class":64,"line":220},[62,66040,223],{"class":72},[62,66042,66043],{"class":64,"line":226},[62,66044,79],{"emptyLinePlaceholder":13},[62,66046,66047],{"class":64,"line":231},[62,66048,379],{"class":72},[22,66050,66051,66052,66054],{},"This works great but one of the questions that constantly comes up is what benefit does marking our classes with the appropriate annotation have? Why not just mark everything with ",[59,66053,55433],{}," and make all of our lives a little easier. This is a great question and I will try to answer in the next section.",[26,66056,66058],{"id":66057},"spring-aop","Spring AOP",[22,66060,66061,66062,66067],{},"If you're not familiar with Aspect Oriented Programming (AOP) don't worry at all. I am going to give you a very quick introduction and hopefully, it will make more sense as to why we are using different stereotypes in our application. If you would like to read up on AOP before we dive in ",[677,66063,66066],{"href":66064,"rel":66065},"https://docs.spring.io/spring/docs/current/spring-framework-reference/html/aop.html",[681],"you can do so here",". AOP is one of those tools that we developers need to have in our toolbox. We might not always have the need to bring it out but like a good wrench, it's really nice to have. I think Wikipedia does a really good job of explaining AOP so let's dive in.",[29685,66069,66070],{},[22,66071,66072],{},"In computing, aspect-oriented programming (AOP) is a programming paradigm that aims to increase modularity by allowing the separation of cross-cutting concerns. It does so by adding additional behavior to existing code (an advice) without modifying the code itself, instead separately specifying which code is modified via a \"pointcut\" specification, such as \"log all function calls when the function's name begins with 'set'\". This allows behaviors that are not central to the business logic (such as logging) to be added to a program without cluttering the code core to the functionality. AOP forms a basis for aspect-oriented software development.",[22,66074,66075],{},"If the business came to us and asked to add logging calls before each of the controller methods in our application were called, we could take the following approach.",[52,66077,66079],{"className":54,"code":66078,"language":56,"meta":57,"style":57},"package com.therealdanvega.controller;\n\nimport com.therealdanvega.service.StereotypeService;\nimport org.apache.log4j.Logger;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class StereotypeController {\n\n private static final Logger logger = Logger.getLogger(StereotypeController.class);\n\n private StereotypeService stereotypeService;\n\n public StereotypeController(StereotypeService stereotypeService) {\n this.stereotypeService = stereotypeService;\n }\n\n @RequestMapping(\"/\")\n public String home(){\n logger.info(\"StereotypeController.home method called\");\n return stereotypeService.sayHello();\n }\n\n @RequestMapping(\"/list\")\n public String list(){\n logger.info(\"StereotypeController.home method called\");\n return \"list\";\n }\n\n @RequestMapping(\"/add\")\n public String add(){\n logger.info(\"StereotypeController.add method called\");\n return \"add\";\n }\n\n @RequestMapping(\"/save\")\n public String save(){\n logger.info(\"StereotypeController.save method called\");\n return \"save\";\n }\n\n}\n",[59,66080,66081,66087,66091,66097,66104,66110,66116,66120,66126,66136,66140,66160,66164,66170,66174,66186,66196,66200,66204,66216,66226,66239,66249,66253,66257,66270,66280,66292,66301,66305,66309,66322,66332,66345,66354,66358,66362,66374,66384,66397,66406,66410,66414],{"__ignoreMap":57},[62,66082,66083,66085],{"class":64,"line":65},[62,66084,69],{"class":68},[62,66086,49492],{"class":72},[62,66088,66089],{"class":64,"line":76},[62,66090,79],{"emptyLinePlaceholder":13},[62,66092,66093,66095],{"class":64,"line":82},[62,66094,27875],{"class":68},[62,66096,65921],{"class":72},[62,66098,66099,66101],{"class":64,"line":89},[62,66100,27875],{"class":68},[62,66102,66103],{"class":72}," org.apache.log4j.Logger;\n",[62,66105,66106,66108],{"class":64,"line":95},[62,66107,27875],{"class":68},[62,66109,49517],{"class":72},[62,66111,66112,66114],{"class":64,"line":101},[62,66113,27875],{"class":68},[62,66115,27892],{"class":72},[62,66117,66118],{"class":64,"line":107},[62,66119,79],{"emptyLinePlaceholder":13},[62,66121,66122,66124],{"class":64,"line":113},[62,66123,942],{"class":72},[62,66125,2342],{"class":68},[62,66127,66128,66130,66132,66134],{"class":64,"line":129},[62,66129,116],{"class":68},[62,66131,119],{"class":68},[62,66133,65823],{"class":122},[62,66135,126],{"class":72},[62,66137,66138],{"class":64,"line":134},[62,66139,79],{"emptyLinePlaceholder":13},[62,66141,66142,66144,66146,66148,66150,66152,66155,66157],{"class":64,"line":156},[62,66143,137],{"class":68},[62,66145,2101],{"class":68},[62,66147,458],{"class":68},[62,66149,61921],{"class":72},[62,66151,146],{"class":68},[62,66153,66154],{"class":72}," Logger.",[62,66156,3069],{"class":122},[62,66158,66159],{"class":72},"(StereotypeController.class);\n",[62,66161,66162],{"class":64,"line":161},[62,66163,79],{"emptyLinePlaceholder":13},[62,66165,66166,66168],{"class":64,"line":167},[62,66167,137],{"class":68},[62,66169,65964],{"class":72},[62,66171,66172],{"class":64,"line":173},[62,66173,79],{"emptyLinePlaceholder":13},[62,66175,66176,66178,66180,66182,66184],{"class":64,"line":179},[62,66177,194],{"class":68},[62,66179,65823],{"class":122},[62,66181,65977],{"class":72},[62,66183,65980],{"class":889},[62,66185,768],{"class":72},[62,66187,66188,66190,66192,66194],{"class":64,"line":185},[62,66189,2405],{"class":149},[62,66191,65989],{"class":72},[62,66193,146],{"class":68},[62,66195,65994],{"class":72},[62,66197,66198],{"class":64,"line":191},[62,66199,223],{"class":72},[62,66201,66202],{"class":64,"line":209},[62,66203,79],{"emptyLinePlaceholder":13},[62,66205,66206,66208,66210,66212,66214],{"class":64,"line":220},[62,66207,2143],{"class":72},[62,66209,10592],{"class":68},[62,66211,2109],{"class":72},[62,66213,15635],{"class":1675},[62,66215,2212],{"class":72},[62,66217,66218,66220,66222,66224],{"class":64,"line":226},[62,66219,194],{"class":68},[62,66221,2469],{"class":72},[62,66223,18647],{"class":122},[62,66225,57791],{"class":72},[62,66227,66228,66230,66232,66234,66237],{"class":64,"line":231},[62,66229,61964],{"class":72},[62,66231,12688],{"class":122},[62,66233,2109],{"class":72},[62,66235,66236],{"class":1675},"\"StereotypeController.home method called\"",[62,66238,1133],{"class":72},[62,66240,66241,66243,66245,66247],{"class":64,"line":236},[62,66242,360],{"class":68},[62,66244,66031],{"class":72},[62,66246,66034],{"class":122},[62,66248,822],{"class":72},[62,66250,66251],{"class":64,"line":242},[62,66252,223],{"class":72},[62,66254,66255],{"class":64,"line":247},[62,66256,79],{"emptyLinePlaceholder":13},[62,66258,66259,66261,66263,66265,66268],{"class":64,"line":252},[62,66260,2143],{"class":72},[62,66262,10592],{"class":68},[62,66264,2109],{"class":72},[62,66266,66267],{"class":1675},"\"/list\"",[62,66269,2212],{"class":72},[62,66271,66272,66274,66276,66278],{"class":64,"line":257},[62,66273,194],{"class":68},[62,66275,2469],{"class":72},[62,66277,38739],{"class":122},[62,66279,57791],{"class":72},[62,66281,66282,66284,66286,66288,66290],{"class":64,"line":271},[62,66283,61964],{"class":72},[62,66285,12688],{"class":122},[62,66287,2109],{"class":72},[62,66289,66236],{"class":1675},[62,66291,1133],{"class":72},[62,66293,66294,66296,66299],{"class":64,"line":281},[62,66295,360],{"class":68},[62,66297,66298],{"class":1675}," \"list\"",[62,66300,153],{"class":72},[62,66302,66303],{"class":64,"line":286},[62,66304,223],{"class":72},[62,66306,66307],{"class":64,"line":291},[62,66308,79],{"emptyLinePlaceholder":13},[62,66310,66311,66313,66315,66317,66320],{"class":64,"line":296},[62,66312,2143],{"class":72},[62,66314,10592],{"class":68},[62,66316,2109],{"class":72},[62,66318,66319],{"class":1675},"\"/add\"",[62,66321,2212],{"class":72},[62,66323,66324,66326,66328,66330],{"class":64,"line":302},[62,66325,194],{"class":68},[62,66327,2469],{"class":72},[62,66329,5806],{"class":122},[62,66331,57791],{"class":72},[62,66333,66334,66336,66338,66340,66343],{"class":64,"line":308},[62,66335,61964],{"class":72},[62,66337,12688],{"class":122},[62,66339,2109],{"class":72},[62,66341,66342],{"class":1675},"\"StereotypeController.add method called\"",[62,66344,1133],{"class":72},[62,66346,66347,66349,66352],{"class":64,"line":314},[62,66348,360],{"class":68},[62,66350,66351],{"class":1675}," \"add\"",[62,66353,153],{"class":72},[62,66355,66356],{"class":64,"line":320},[62,66357,223],{"class":72},[62,66359,66360],{"class":64,"line":326},[62,66361,79],{"emptyLinePlaceholder":13},[62,66363,66364,66366,66368,66370,66372],{"class":64,"line":338},[62,66365,2143],{"class":72},[62,66367,10592],{"class":68},[62,66369,2109],{"class":72},[62,66371,22527],{"class":1675},[62,66373,2212],{"class":72},[62,66375,66376,66378,66380,66382],{"class":64,"line":343},[62,66377,194],{"class":68},[62,66379,2469],{"class":72},[62,66381,22562],{"class":122},[62,66383,57791],{"class":72},[62,66385,66386,66388,66390,66392,66395],{"class":64,"line":357},[62,66387,61964],{"class":72},[62,66389,12688],{"class":122},[62,66391,2109],{"class":72},[62,66393,66394],{"class":1675},"\"StereotypeController.save method called\"",[62,66396,1133],{"class":72},[62,66398,66399,66401,66404],{"class":64,"line":366},[62,66400,360],{"class":68},[62,66402,66403],{"class":1675}," \"save\"",[62,66405,153],{"class":72},[62,66407,66408],{"class":64,"line":371},[62,66409,223],{"class":72},[62,66411,66412],{"class":64,"line":376},[62,66413,79],{"emptyLinePlaceholder":13},[62,66415,66416],{"class":64,"line":16333},[62,66417,379],{"class":72},[22,66419,66420],{},"As you can tell this becomes quite repetitive and this is just a single class. Can you image the complexity and unwanted noise this would add to our application? Thanks to AOP we can remove this type of boilerplate code. In this example, I am creating an aspect that will log before each controller method in our application. Please don't focus too much on the code but rather the solution that this brings.",[52,66422,66424],{"className":54,"code":66423,"language":56,"meta":57,"style":57},"package com.therealdanvega.aop;\n\nimport org.apache.log4j.Logger;\nimport org.aspectj.lang.JoinPoint;\nimport org.aspectj.lang.annotation.Aspect;\nimport org.aspectj.lang.annotation.Before;\nimport org.springframework.stereotype.Component;\n\n@Component\n@Aspect\npublic class ControllerAspect {\n\n private static final Logger logger = Logger.getLogger(ControllerAspect.class);\n\n @Before(\"within(com.therealdanvega.controller..\\*)\")\n private void before(JoinPoint joinPoint){\n String caller = joinPoint.getSignature().toShortString();\n logger.info(caller + \" method called.\");\n }\n\n}\n",[59,66425,66426,66433,66437,66443,66450,66457,66464,66471,66475,66481,66487,66498,66502,66521,66525,66545,66561,66579,66595,66599,66603],{"__ignoreMap":57},[62,66427,66428,66430],{"class":64,"line":65},[62,66429,69],{"class":68},[62,66431,66432],{"class":72}," com.therealdanvega.aop;\n",[62,66434,66435],{"class":64,"line":76},[62,66436,79],{"emptyLinePlaceholder":13},[62,66438,66439,66441],{"class":64,"line":82},[62,66440,27875],{"class":68},[62,66442,66103],{"class":72},[62,66444,66445,66447],{"class":64,"line":89},[62,66446,27875],{"class":68},[62,66448,66449],{"class":72}," org.aspectj.lang.JoinPoint;\n",[62,66451,66452,66454],{"class":64,"line":95},[62,66453,27875],{"class":68},[62,66455,66456],{"class":72}," org.aspectj.lang.annotation.Aspect;\n",[62,66458,66459,66461],{"class":64,"line":101},[62,66460,27875],{"class":68},[62,66462,66463],{"class":72}," org.aspectj.lang.annotation.Before;\n",[62,66465,66466,66468],{"class":64,"line":107},[62,66467,27875],{"class":68},[62,66469,66470],{"class":72}," org.springframework.stereotype.Component;\n",[62,66472,66473],{"class":64,"line":113},[62,66474,79],{"emptyLinePlaceholder":13},[62,66476,66477,66479],{"class":64,"line":129},[62,66478,942],{"class":72},[62,66480,12585],{"class":68},[62,66482,66483,66485],{"class":64,"line":134},[62,66484,942],{"class":72},[62,66486,12578],{"class":68},[62,66488,66489,66491,66493,66496],{"class":64,"line":156},[62,66490,116],{"class":68},[62,66492,119],{"class":68},[62,66494,66495],{"class":122}," ControllerAspect",[62,66497,126],{"class":72},[62,66499,66500],{"class":64,"line":161},[62,66501,79],{"emptyLinePlaceholder":13},[62,66503,66504,66506,66508,66510,66512,66514,66516,66518],{"class":64,"line":167},[62,66505,137],{"class":68},[62,66507,2101],{"class":68},[62,66509,458],{"class":68},[62,66511,61921],{"class":72},[62,66513,146],{"class":68},[62,66515,66154],{"class":72},[62,66517,3069],{"class":122},[62,66519,66520],{"class":72},"(ControllerAspect.class);\n",[62,66522,66523],{"class":64,"line":173},[62,66524,79],{"emptyLinePlaceholder":13},[62,66526,66527,66529,66532,66534,66537,66540,66543],{"class":64,"line":179},[62,66528,2143],{"class":72},[62,66530,66531],{"class":68},"Before",[62,66533,2109],{"class":72},[62,66535,66536],{"class":1675},"\"within(com.therealdanvega.controller..",[62,66538,66539],{"class":149},"\\*",[62,66541,66542],{"class":1675},")\"",[62,66544,2212],{"class":72},[62,66546,66547,66549,66551,66554,66557,66559],{"class":64,"line":185},[62,66548,137],{"class":68},[62,66550,200],{"class":68},[62,66552,66553],{"class":122}," before",[62,66555,66556],{"class":72},"(JoinPoint ",[62,66558,12653],{"class":889},[62,66560,34126],{"class":72},[62,66562,66563,66566,66568,66570,66572,66574,66577],{"class":64,"line":191},[62,66564,66565],{"class":72}," String caller ",[62,66567,146],{"class":68},[62,66569,12670],{"class":72},[62,66571,12673],{"class":122},[62,66573,3229],{"class":72},[62,66575,66576],{"class":122},"toShortString",[62,66578,822],{"class":72},[62,66580,66581,66583,66585,66588,66590,66593],{"class":64,"line":209},[62,66582,61964],{"class":72},[62,66584,12688],{"class":122},[62,66586,66587],{"class":72},"(caller ",[62,66589,1148],{"class":68},[62,66591,66592],{"class":1675}," \" method called.\"",[62,66594,1133],{"class":72},[62,66596,66597],{"class":64,"line":220},[62,66598,223],{"class":72},[62,66600,66601],{"class":64,"line":226},[62,66602,79],{"emptyLinePlaceholder":13},[62,66604,66605],{"class":64,"line":231},[62,66606,379],{"class":72},[22,66608,66609],{},"In the previous example, we are targeting any class in our com.therealdanvega.controller package. What if we had @Controller & @RestControllers in this package. We can also target a particular set of classes or methods via annotations.",[52,66611,66613],{"className":54,"code":66612,"language":56,"meta":57,"style":57},"package com.therealdanvega.aop;\n\nimport org.apache.log4j.Logger;\nimport org.aspectj.lang.JoinPoint;\nimport org.aspectj.lang.annotation.Aspect;\nimport org.aspectj.lang.annotation.Before;\nimport org.aspectj.lang.annotation.Pointcut;\nimport org.springframework.stereotype.Component;\n\n@Component\n@Aspect\npublic class ControllerAspect {\n\n private static final Logger logger = Logger.getLogger(ControllerAspect.class);\n\n @Pointcut(\"@target(org.springframework.web.bind.annotation.RestController)\")\n public void targetRestControllers(){}\n\n @Before(\"within(com.therealdanvega.controller..\\*) && targetRestControllers()\")\n private void before(JoinPoint joinPoint){\n String caller = joinPoint.getSignature().toShortString();\n logger.info(caller + \" method called.\");\n }\n\n}\n",[59,66614,66615,66621,66625,66631,66637,66643,66649,66656,66662,66666,66672,66678,66688,66692,66710,66714,66728,66740,66744,66761,66775,66791,66805,66809,66813],{"__ignoreMap":57},[62,66616,66617,66619],{"class":64,"line":65},[62,66618,69],{"class":68},[62,66620,66432],{"class":72},[62,66622,66623],{"class":64,"line":76},[62,66624,79],{"emptyLinePlaceholder":13},[62,66626,66627,66629],{"class":64,"line":82},[62,66628,27875],{"class":68},[62,66630,66103],{"class":72},[62,66632,66633,66635],{"class":64,"line":89},[62,66634,27875],{"class":68},[62,66636,66449],{"class":72},[62,66638,66639,66641],{"class":64,"line":95},[62,66640,27875],{"class":68},[62,66642,66456],{"class":72},[62,66644,66645,66647],{"class":64,"line":101},[62,66646,27875],{"class":68},[62,66648,66463],{"class":72},[62,66650,66651,66653],{"class":64,"line":107},[62,66652,27875],{"class":68},[62,66654,66655],{"class":72}," org.aspectj.lang.annotation.Pointcut;\n",[62,66657,66658,66660],{"class":64,"line":113},[62,66659,27875],{"class":68},[62,66661,66470],{"class":72},[62,66663,66664],{"class":64,"line":129},[62,66665,79],{"emptyLinePlaceholder":13},[62,66667,66668,66670],{"class":64,"line":134},[62,66669,942],{"class":72},[62,66671,12585],{"class":68},[62,66673,66674,66676],{"class":64,"line":156},[62,66675,942],{"class":72},[62,66677,12578],{"class":68},[62,66679,66680,66682,66684,66686],{"class":64,"line":161},[62,66681,116],{"class":68},[62,66683,119],{"class":68},[62,66685,66495],{"class":122},[62,66687,126],{"class":72},[62,66689,66690],{"class":64,"line":167},[62,66691,79],{"emptyLinePlaceholder":13},[62,66693,66694,66696,66698,66700,66702,66704,66706,66708],{"class":64,"line":173},[62,66695,137],{"class":68},[62,66697,2101],{"class":68},[62,66699,458],{"class":68},[62,66701,61921],{"class":72},[62,66703,146],{"class":68},[62,66705,66154],{"class":72},[62,66707,3069],{"class":122},[62,66709,66520],{"class":72},[62,66711,66712],{"class":64,"line":179},[62,66713,79],{"emptyLinePlaceholder":13},[62,66715,66716,66718,66721,66723,66726],{"class":64,"line":185},[62,66717,2143],{"class":72},[62,66719,66720],{"class":68},"Pointcut",[62,66722,2109],{"class":72},[62,66724,66725],{"class":1675},"\"@target(org.springframework.web.bind.annotation.RestController)\"",[62,66727,2212],{"class":72},[62,66729,66730,66732,66734,66737],{"class":64,"line":191},[62,66731,194],{"class":68},[62,66733,200],{"class":68},[62,66735,66736],{"class":122}," targetRestControllers",[62,66738,66739],{"class":72},"(){}\n",[62,66741,66742],{"class":64,"line":209},[62,66743,79],{"emptyLinePlaceholder":13},[62,66745,66746,66748,66750,66752,66754,66756,66759],{"class":64,"line":220},[62,66747,2143],{"class":72},[62,66749,66531],{"class":68},[62,66751,2109],{"class":72},[62,66753,66536],{"class":1675},[62,66755,66539],{"class":149},[62,66757,66758],{"class":1675},") && targetRestControllers()\"",[62,66760,2212],{"class":72},[62,66762,66763,66765,66767,66769,66771,66773],{"class":64,"line":226},[62,66764,137],{"class":68},[62,66766,200],{"class":68},[62,66768,66553],{"class":122},[62,66770,66556],{"class":72},[62,66772,12653],{"class":889},[62,66774,34126],{"class":72},[62,66776,66777,66779,66781,66783,66785,66787,66789],{"class":64,"line":231},[62,66778,66565],{"class":72},[62,66780,146],{"class":68},[62,66782,12670],{"class":72},[62,66784,12673],{"class":122},[62,66786,3229],{"class":72},[62,66788,66576],{"class":122},[62,66790,822],{"class":72},[62,66792,66793,66795,66797,66799,66801,66803],{"class":64,"line":236},[62,66794,61964],{"class":72},[62,66796,12688],{"class":122},[62,66798,66587],{"class":72},[62,66800,1148],{"class":68},[62,66802,66592],{"class":1675},[62,66804,1133],{"class":72},[62,66806,66807],{"class":64,"line":242},[62,66808,223],{"class":72},[62,66810,66811],{"class":64,"line":247},[62,66812,79],{"emptyLinePlaceholder":13},[62,66814,66815],{"class":64,"line":252},[62,66816,379],{"class":72},[26,66818,1499],{"id":1498},[22,66820,66821],{},"I hope this article was able to help you understand the Spring Stereotype Annotations a little bit more.",[22,66823,50754,66824,66826],{},[646,66825,49733],{}," What concept in Spring do you find most difficult to grasp? _",[1527,66828,65588],{},{"title":57,"searchDepth":76,"depth":76,"links":66830},[66831,66832,66833,66834,66835],{"id":65613,"depth":76,"text":65614},{"id":65656,"depth":76,"text":65657},{"id":65751,"depth":76,"text":65752},{"id":66057,"depth":76,"text":66058},{"id":1498,"depth":76,"text":1499},{"_id":66837,"path":66838,"title":66839,"description":66839,"meta":66840,"body":66845},"content/blog/2017/03/24/spring-boot-convention-based-error-pages.md","/blog/2017/03/24/spring-boot-convention-based-error-pages","Spring Boot Convention Based Error Pages",{"slug":66841,"date":66842,"published":13,"tags":66843,"author":-1,"cover":66844,"excerpt":-1},"spring-boot-convention-based-error-pages","2017-03-24T08:00:33-04:00",[11002],"./custom-error-cover.jpg",{"type":19,"value":66846,"toc":67345},[66847,66850,66853,66857,66870,66876,66880,66888,66891,67044,67050,67054,67057,67326,67332,67334,67337,67342],[22,66848,66849],{},"We have to remember that when we put applications out into the wild that things will go wrong. This can be in the form of a user going to a wrong URL or entering invalid data. As much as we like to always blame the users (it must be user error, right?) there are times when we as developers make mistakes.",[22,66851,66852],{},"What we need to understand is that it is ok for both the users and us to make mistakes as long as we account for them. When a user requests a URI that doesn't exist we need to show them a proper 404 (page not found) error page. We can even take this one step further and provide additional resources on this page to help them find what they are looking for. In this article, we will look at some improvements in Spring Boot 1.4 that help us define convention based error pages. In this article, we will look at some improvements in Spring Boot 1.4 that help us define convention based error pages.",[26,66854,66856],{"id":66855},"custom-error-pages","Custom Error Pages",[22,66858,66859,66860,66865,66866,66869],{},"I have ",[677,66861,66864],{"href":66862,"rel":66863},"https://github.com/cfaddict/custom-errors/tree/master/src/main/java/com/therealdanvega/controller",[681],"created a very simple project"," that has a single home controller with no methods defined. If we launch that application and visit ",[677,66867,16942],{"href":16942,"rel":66868},[681]," we will get this default white label error page. This is of course because we have no mapping defined for \"/\".",[22,66871,66872],{},[653,66873],{"alt":66874,"src":66875},"White Label Error","./whitelabel_error_page.png",[26,66877,66879],{"id":66878},"html-templates","HTML Templates",[22,66881,66882,66883,2755],{},"What you are seeing above is the default, global error handler for the servlet container. As you can see the status code 404 (page not found) is being thrown. In previous versions of Spring Boot if we wanted a custom 404 page we had to create a custom error controller that implemented the ",[677,66884,66887],{"href":66885,"rel":66886},"http://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/autoconfigure/web/ErrorController.html",[681],"error controller interface",[22,66889,66890],{},"Now we can just create the templates and by convention place them in a folder. If we are just using static HTML files we can place them in the /src/main/resources/public/error directory. If we want to catch a specific error code like 404 we can create a 404.html. If we want to map all 4xx errors, simply create a 4xx.html. With that, I am going to drop in my custom 404.html.",[52,66892,66894],{"className":15773,"code":66893,"language":15775,"meta":57,"style":57},"\u003Chtml>\n \u003Chead>\n \u003Ctitle>404: Page Not Found\u003C/title>\n \u003Cstyle>\n body {\n background-color: #2a2627;\n margin: 20px;\n }\n h1 {\n color: #ffffff;\n }\n \u003C/style>\n \u003C/head>\n \u003Cbody>\n \u003Ch1>404: Page Not Found\u003C/h1>\n \u003C/body>\n\u003C/html>\n",[59,66895,66896,66904,66912,66925,66933,66940,66952,66965,66969,66976,66988,66992,67000,67008,67016,67028,67036],{"__ignoreMap":57},[62,66897,66898,66900,66902],{"class":64,"line":65},[62,66899,760],{"class":72},[62,66901,15775],{"class":1780},[62,66903,1784],{"class":72},[62,66905,66906,66908,66910],{"class":64,"line":76},[62,66907,33056],{"class":72},[62,66909,15824],{"class":1780},[62,66911,1784],{"class":72},[62,66913,66914,66916,66918,66921,66923],{"class":64,"line":82},[62,66915,1789],{"class":72},[62,66917,3196],{"class":1780},[62,66919,66920],{"class":72},">404: Page Not Found\u003C/",[62,66922,3196],{"class":1780},[62,66924,1784],{"class":72},[62,66926,66927,66929,66931],{"class":64,"line":89},[62,66928,1789],{"class":72},[62,66930,1527],{"class":1780},[62,66932,1784],{"class":72},[62,66934,66935,66938],{"class":64,"line":95},[62,66936,66937],{"class":1780}," body",[62,66939,126],{"class":72},[62,66941,66942,66945,66947,66950],{"class":64,"line":101},[62,66943,66944],{"class":149}," background-color",[62,66946,3696],{"class":72},[62,66948,66949],{"class":149},"#2a2627",[62,66951,153],{"class":72},[62,66953,66954,66957,66959,66961,66963],{"class":64,"line":107},[62,66955,66956],{"class":149}," margin",[62,66958,3696],{"class":72},[62,66960,30188],{"class":149},[62,66962,30191],{"class":68},[62,66964,153],{"class":72},[62,66966,66967],{"class":64,"line":113},[62,66968,29042],{"class":72},[62,66970,66971,66974],{"class":64,"line":129},[62,66972,66973],{"class":1780}," h1",[62,66975,126],{"class":72},[62,66977,66978,66981,66983,66986],{"class":64,"line":134},[62,66979,66980],{"class":149}," color",[62,66982,3696],{"class":72},[62,66984,66985],{"class":149},"#ffffff",[62,66987,153],{"class":72},[62,66989,66990],{"class":64,"line":156},[62,66991,29042],{"class":72},[62,66993,66994,66996,66998],{"class":64,"line":161},[62,66995,1982],{"class":72},[62,66997,1527],{"class":1780},[62,66999,1784],{"class":72},[62,67001,67002,67004,67006],{"class":64,"line":167},[62,67003,33187],{"class":72},[62,67005,15824],{"class":1780},[62,67007,1784],{"class":72},[62,67009,67010,67012,67014],{"class":64,"line":173},[62,67011,33056],{"class":72},[62,67013,11414],{"class":1780},[62,67015,1784],{"class":72},[62,67017,67018,67020,67022,67024,67026],{"class":64,"line":179},[62,67019,1789],{"class":72},[62,67021,4168],{"class":1780},[62,67023,66920],{"class":72},[62,67025,4168],{"class":1780},[62,67027,1784],{"class":72},[62,67029,67030,67032,67034],{"class":64,"line":185},[62,67031,33187],{"class":72},[62,67033,11414],{"class":1780},[62,67035,1784],{"class":72},[62,67037,67038,67040,67042],{"class":64,"line":191},[62,67039,1818],{"class":72},[62,67041,15775],{"class":1780},[62,67043,1784],{"class":72},[22,67045,67046],{},[653,67047],{"alt":67048,"src":67049},"Custom Error","./custom_error-.png",[26,67051,67053],{"id":67052},"dynamic-templates","Dynamic Templates",[22,67055,67056],{},"If you are using a templating engine in your application we can also take advantage of this convention based error handling. You will need to place your templates in the src/main/resources/templates/error directory. I have almost the same error template but because we are using Thymeleaf I can access the error object and display more information on the page.",[52,67058,67060],{"className":15773,"code":67059,"language":15775,"meta":57,"style":57},"\u003C!DOCTYPE html>\n\u003Chtml\n lang=\"en\"\n xmlns=\"http://www.w3.org/1999/xhtml\"\n xmlns:th=\"http://www.thymleaf.org\"\n>\n \u003Chead>\n \u003Cmeta charset=\"utf-8\" />\n \u003Cmeta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\" />\n \u003Cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n \u003Ctitle>404: Page Not Found\u003C/title>\n \u003Cstyle>\n body {\n background-color: #2a2627;\n margin: 20px;\n }\n h1,\n p {\n color: #ffffff;\n }\n \u003C/style>\n \u003C/head>\n \u003Cbody>\n \u003Ch1>Page Not Found\u003C/h1>\n \u003Cp th:text=\"${error}\">Error Info\u003C/p>\n \u003C/body>\n\u003C/html>\n",[59,67061,67062,67072,67079,67088,67097,67107,67111,67119,67134,67154,67174,67186,67194,67200,67210,67222,67226,67232,67239,67249,67253,67261,67269,67277,67290,67310,67318],{"__ignoreMap":57},[62,67063,67064,67066,67068,67070],{"class":64,"line":65},[62,67065,15801],{"class":72},[62,67067,15804],{"class":1780},[62,67069,15807],{"class":122},[62,67071,1784],{"class":72},[62,67073,67074,67076],{"class":64,"line":76},[62,67075,760],{"class":72},[62,67077,67078],{"class":1780},"html\n",[62,67080,67081,67084,67086],{"class":64,"line":82},[62,67082,67083],{"class":122}," lang",[62,67085,146],{"class":72},[62,67087,60594],{"class":1675},[62,67089,67090,67093,67095],{"class":64,"line":89},[62,67091,67092],{"class":122}," xmlns",[62,67094,146],{"class":72},[62,67096,60604],{"class":1675},[62,67098,67099,67102,67104],{"class":64,"line":95},[62,67100,67101],{"class":122}," xmlns:th",[62,67103,146],{"class":72},[62,67105,67106],{"class":1675},"\"http://www.thymleaf.org\"\n",[62,67108,67109],{"class":64,"line":101},[62,67110,1784],{"class":72},[62,67112,67113,67115,67117],{"class":64,"line":107},[62,67114,33056],{"class":72},[62,67116,15824],{"class":1780},[62,67118,1784],{"class":72},[62,67120,67121,67123,67125,67127,67129,67131],{"class":64,"line":113},[62,67122,1789],{"class":72},[62,67124,20701],{"class":1780},[62,67126,20704],{"class":122},[62,67128,146],{"class":72},[62,67130,60637],{"class":1675},[62,67132,67133],{"class":72}," />\n",[62,67135,67136,67138,67140,67142,67144,67146,67148,67150,67152],{"class":64,"line":129},[62,67137,1789],{"class":72},[62,67139,20701],{"class":1780},[62,67141,60649],{"class":122},[62,67143,146],{"class":72},[62,67145,60654],{"class":1675},[62,67147,20727],{"class":122},[62,67149,146],{"class":72},[62,67151,60661],{"class":1675},[62,67153,67133],{"class":72},[62,67155,67156,67158,67160,67162,67164,67166,67168,67170,67172],{"class":64,"line":134},[62,67157,1789],{"class":72},[62,67159,20701],{"class":1780},[62,67161,16107],{"class":122},[62,67163,146],{"class":72},[62,67165,20724],{"class":1675},[62,67167,20727],{"class":122},[62,67169,146],{"class":72},[62,67171,60682],{"class":1675},[62,67173,67133],{"class":72},[62,67175,67176,67178,67180,67182,67184],{"class":64,"line":156},[62,67177,1789],{"class":72},[62,67179,3196],{"class":1780},[62,67181,66920],{"class":72},[62,67183,3196],{"class":1780},[62,67185,1784],{"class":72},[62,67187,67188,67190,67192],{"class":64,"line":161},[62,67189,1789],{"class":72},[62,67191,1527],{"class":1780},[62,67193,1784],{"class":72},[62,67195,67196,67198],{"class":64,"line":167},[62,67197,66937],{"class":1780},[62,67199,126],{"class":72},[62,67201,67202,67204,67206,67208],{"class":64,"line":173},[62,67203,66944],{"class":149},[62,67205,3696],{"class":72},[62,67207,66949],{"class":149},[62,67209,153],{"class":72},[62,67211,67212,67214,67216,67218,67220],{"class":64,"line":179},[62,67213,66956],{"class":149},[62,67215,3696],{"class":72},[62,67217,30188],{"class":149},[62,67219,30191],{"class":68},[62,67221,153],{"class":72},[62,67223,67224],{"class":64,"line":185},[62,67225,29042],{"class":72},[62,67227,67228,67230],{"class":64,"line":191},[62,67229,66973],{"class":1780},[62,67231,3338],{"class":72},[62,67233,67234,67237],{"class":64,"line":209},[62,67235,67236],{"class":1780}," p",[62,67238,126],{"class":72},[62,67240,67241,67243,67245,67247],{"class":64,"line":220},[62,67242,66980],{"class":149},[62,67244,3696],{"class":72},[62,67246,66985],{"class":149},[62,67248,153],{"class":72},[62,67250,67251],{"class":64,"line":226},[62,67252,29042],{"class":72},[62,67254,67255,67257,67259],{"class":64,"line":231},[62,67256,1982],{"class":72},[62,67258,1527],{"class":1780},[62,67260,1784],{"class":72},[62,67262,67263,67265,67267],{"class":64,"line":236},[62,67264,33187],{"class":72},[62,67266,15824],{"class":1780},[62,67268,1784],{"class":72},[62,67270,67271,67273,67275],{"class":64,"line":242},[62,67272,33056],{"class":72},[62,67274,11414],{"class":1780},[62,67276,1784],{"class":72},[62,67278,67279,67281,67283,67286,67288],{"class":64,"line":247},[62,67280,1789],{"class":72},[62,67282,4168],{"class":1780},[62,67284,67285],{"class":72},">Page Not Found\u003C/",[62,67287,4168],{"class":1780},[62,67289,1784],{"class":72},[62,67291,67292,67294,67296,67298,67300,67303,67306,67308],{"class":64,"line":252},[62,67293,1789],{"class":72},[62,67295,22],{"class":1780},[62,67297,60721],{"class":122},[62,67299,146],{"class":72},[62,67301,67302],{"class":1675},"\"${error}\"",[62,67304,67305],{"class":72},">Error Info\u003C/",[62,67307,22],{"class":1780},[62,67309,1784],{"class":72},[62,67311,67312,67314,67316],{"class":64,"line":257},[62,67313,33187],{"class":72},[62,67315,11414],{"class":1780},[62,67317,1784],{"class":72},[62,67319,67320,67322,67324],{"class":64,"line":271},[62,67321,1818],{"class":72},[62,67323,15775],{"class":1780},[62,67325,1784],{"class":72},[22,67327,67328],{},[653,67329],{"alt":67330,"src":67331},"Error Template","./custom_error_template.png",[26,67333,1499],{"id":1498},[22,67335,67336],{},"If create an error template in both public and templates the dynamic template will win out. As you can see the improvements in Spring Boot 1.4 made it really easy for us to handle errors. As I said when I started this article, errors are going to happen. We just need to make sure that we are handling them and making the user experience a pleasant one.",[22,67338,67339,67341],{},[646,67340,49733],{}," What problems are you facing when trying to handle errors in your Spring applications?",[1527,67343,67344],{},"html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .suaqH, html code.shiki .suaqH{--shiki-default:#22863A;--shiki-dark:#85E89D;--shiki-sepia:#22863A}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}",{"title":57,"searchDepth":76,"depth":76,"links":67346},[67347,67348,67349,67350],{"id":66855,"depth":76,"text":66856},{"id":66878,"depth":76,"text":66879},{"id":67052,"depth":76,"text":67053},{"id":1498,"depth":76,"text":1499},{"_id":67352,"path":67353,"title":67354,"description":67354,"meta":67355,"body":67360},"content/blog/2017/03/22/spring-boot-entity-scan.md","/blog/2017/03/22/spring-boot-entity-scan","Spring Boot Entity Scan",{"slug":67356,"date":67357,"published":13,"tags":67358,"author":-1,"cover":67359,"excerpt":-1},"spring-boot-entity-scan","2017-03-22T08:00:44-04:00",[11002],"./entity-scan.jpg",{"type":19,"value":67361,"toc":67855},[67362,67365,67367,67375,67377,67380,67384,67387,67392,67395,67402,67404,67407,67692,67695,67699,67708,67826,67829,67836,67838,67847,67852],[22,67363,67364],{},"I had a question from a student come up that a lot of new Spring developers come across. It has to do with creating components and in this case more specifically, entities. Let's take a look at the question and then dive into the solution.",[26,67366,62931],{"id":62930},[29685,67368,67369],{},[22,67370,67371,67372,67374],{},"Hey, I'm trying to get JPA to create the post and author tables and view them in the H2 console. I copied every step in the video and tried the suggestions to change the JDBC url to ",[52688,67373],{},". I've also tried changing spring.datasource.name: to both test and testdb, all with no luck. When the console comes up, there are no tables. Any help would be most appreciated!! Here is the code in a repo, if that helps!",[26,67376,63388],{"id":63387},[22,67378,67379],{},"I had to dive into the student's code to figure out what was going on but usually, there is a pretty good explanation as to why something like this is happening. When entities aren't being picked up and database tables aren't being created there are a few places to look. The first thing I do is make sure we are connecting to the correct database and in this case, we were. Next, I like to take a look at the entities and make sure there are no issues with those. We could have issues with the code or it simply could be an issue with Spring not finding the entity based on its location. ",[636,67381,67383],{"id":67382},"create-a-new-spring-boot-project","Create a new Spring Boot Project",[22,67385,67386],{},"To demonstrate the issue at hand we are going to create a brand new Spring Boot project. This will give us a chance to see what the problem is in a simplified version. Create a new project using the Spring Initializr and select the Web, JPA, and H2 dependencies. ",[22,67388,67389],{},[653,67390],{"alt":2925,"src":67391},"./entity-demo-starter-1024x649.png",[22,67393,67394],{},"When you are creating the project you will be asked to give your main package a name. I named mine com.therealdanvega and as you can see from the image below it created the main application class under that package. ",[22,67396,67397,67401],{},[653,67398],{"alt":67399,"src":67400},"Entry Scan Spring Boot","./entity-scan-project.png"," ",[636,67403,51933],{"id":51932},[22,67405,67406],{},"In the project, I asked the student to create a package named domain and create a new entity there. The student did just that and here is what that class look likes. ",[52,67408,67410],{"className":54,"code":67409,"language":56,"meta":57,"style":57},"package domain;\n\nimport javax.persistence.Entity;\nimport javax.persistence.GeneratedValue;\nimport javax.persistence.Id;\n\n@Entity\npublic class Tweet {\n\n @Id @GeneratedValue\n private Long id;\n private String username;\n private String text;\n\n private Tweet() {}\n\n public Long getId() {\n return id;\n }\n\n public void setId(Long id) {\n this.id = id;\n }\n\n public String getUsername() {\n return username;\n }\n\n public void setUsername(String username) {\n this.username = username;\n }\n\n public String getText() {\n return text;\n }\n\n public void setText(String text) {\n this.text = text;\n }\n\n}\n",[59,67411,67412,67419,67423,67429,67435,67441,67445,67451,67462,67466,67476,67482,67489,67496,67500,67508,67512,67522,67528,67532,67536,67550,67560,67564,67568,67579,67586,67590,67594,67609,67620,67624,67628,67639,67646,67650,67654,67669,67680,67684,67688],{"__ignoreMap":57},[62,67413,67414,67416],{"class":64,"line":65},[62,67415,69],{"class":68},[62,67417,67418],{"class":72}," domain;\n",[62,67420,67421],{"class":64,"line":76},[62,67422,79],{"emptyLinePlaceholder":13},[62,67424,67425,67427],{"class":64,"line":82},[62,67426,27875],{"class":68},[62,67428,51959],{"class":72},[62,67430,67431,67433],{"class":64,"line":89},[62,67432,27875],{"class":68},[62,67434,51966],{"class":72},[62,67436,67437,67439],{"class":64,"line":95},[62,67438,27875],{"class":68},[62,67440,51973],{"class":72},[62,67442,67443],{"class":64,"line":101},[62,67444,79],{"emptyLinePlaceholder":13},[62,67446,67447,67449],{"class":64,"line":107},[62,67448,942],{"class":72},[62,67450,8999],{"class":68},[62,67452,67453,67455,67457,67460],{"class":64,"line":113},[62,67454,116],{"class":68},[62,67456,119],{"class":68},[62,67458,67459],{"class":122}," Tweet",[62,67461,126],{"class":72},[62,67463,67464],{"class":64,"line":129},[62,67465,79],{"emptyLinePlaceholder":13},[62,67467,67468,67470,67472,67474],{"class":64,"line":134},[62,67469,2143],{"class":72},[62,67471,9016],{"class":68},[62,67473,9019],{"class":72},[62,67475,9022],{"class":68},[62,67477,67478,67480],{"class":64,"line":156},[62,67479,137],{"class":68},[62,67481,9029],{"class":72},[62,67483,67484,67486],{"class":64,"line":161},[62,67485,137],{"class":68},[62,67487,67488],{"class":72}," String username;\n",[62,67490,67491,67493],{"class":64,"line":167},[62,67492,137],{"class":68},[62,67494,67495],{"class":72}," String text;\n",[62,67497,67498],{"class":64,"line":173},[62,67499,79],{"emptyLinePlaceholder":13},[62,67501,67502,67504,67506],{"class":64,"line":179},[62,67503,137],{"class":68},[62,67505,67459],{"class":122},[62,67507,58809],{"class":72},[62,67509,67510],{"class":64,"line":185},[62,67511,79],{"emptyLinePlaceholder":13},[62,67513,67514,67516,67518,67520],{"class":64,"line":191},[62,67515,194],{"class":68},[62,67517,52112],{"class":72},[62,67519,23363],{"class":122},[62,67521,206],{"class":72},[62,67523,67524,67526],{"class":64,"line":209},[62,67525,360],{"class":68},[62,67527,9954],{"class":72},[62,67529,67530],{"class":64,"line":220},[62,67531,223],{"class":72},[62,67533,67534],{"class":64,"line":226},[62,67535,79],{"emptyLinePlaceholder":13},[62,67537,67538,67540,67542,67544,67546,67548],{"class":64,"line":231},[62,67539,194],{"class":68},[62,67541,200],{"class":68},[62,67543,52139],{"class":122},[62,67545,39340],{"class":72},[62,67547,6283],{"class":889},[62,67549,768],{"class":72},[62,67551,67552,67554,67556,67558],{"class":64,"line":236},[62,67553,2405],{"class":149},[62,67555,9802],{"class":72},[62,67557,146],{"class":68},[62,67559,9954],{"class":72},[62,67561,67562],{"class":64,"line":242},[62,67563,223],{"class":72},[62,67565,67566],{"class":64,"line":247},[62,67567,79],{"emptyLinePlaceholder":13},[62,67569,67570,67572,67574,67577],{"class":64,"line":252},[62,67571,194],{"class":68},[62,67573,2469],{"class":72},[62,67575,67576],{"class":122},"getUsername",[62,67578,206],{"class":72},[62,67580,67581,67583],{"class":64,"line":257},[62,67582,360],{"class":68},[62,67584,67585],{"class":72}," username;\n",[62,67587,67588],{"class":64,"line":271},[62,67589,223],{"class":72},[62,67591,67592],{"class":64,"line":281},[62,67593,79],{"emptyLinePlaceholder":13},[62,67595,67596,67598,67600,67603,67605,67607],{"class":64,"line":286},[62,67597,194],{"class":68},[62,67599,200],{"class":68},[62,67601,67602],{"class":122}," setUsername",[62,67604,1049],{"class":72},[62,67606,54963],{"class":889},[62,67608,768],{"class":72},[62,67610,67611,67613,67616,67618],{"class":64,"line":291},[62,67612,2405],{"class":149},[62,67614,67615],{"class":72},".username ",[62,67617,146],{"class":68},[62,67619,67585],{"class":72},[62,67621,67622],{"class":64,"line":296},[62,67623,223],{"class":72},[62,67625,67626],{"class":64,"line":302},[62,67627,79],{"emptyLinePlaceholder":13},[62,67629,67630,67632,67634,67637],{"class":64,"line":308},[62,67631,194],{"class":68},[62,67633,2469],{"class":72},[62,67635,67636],{"class":122},"getText",[62,67638,206],{"class":72},[62,67640,67641,67643],{"class":64,"line":314},[62,67642,360],{"class":68},[62,67644,67645],{"class":72}," text;\n",[62,67647,67648],{"class":64,"line":320},[62,67649,223],{"class":72},[62,67651,67652],{"class":64,"line":326},[62,67653,79],{"emptyLinePlaceholder":13},[62,67655,67656,67658,67660,67663,67665,67667],{"class":64,"line":338},[62,67657,194],{"class":68},[62,67659,200],{"class":68},[62,67661,67662],{"class":122}," setText",[62,67664,1049],{"class":72},[62,67666,1727],{"class":889},[62,67668,768],{"class":72},[62,67670,67671,67673,67676,67678],{"class":64,"line":343},[62,67672,2405],{"class":149},[62,67674,67675],{"class":72},".text ",[62,67677,146],{"class":68},[62,67679,67645],{"class":72},[62,67681,67682],{"class":64,"line":357},[62,67683,223],{"class":72},[62,67685,67686],{"class":64,"line":366},[62,67687,79],{"emptyLinePlaceholder":13},[62,67689,67690],{"class":64,"line":371},[62,67691,379],{"class":72},[22,67693,67694],{},"If you know what you're looking for you can probably see the issue on line 1 of that code. The student created the domain package under the java folder and not inside of the main com.therealdanvega package. The student started the application and the table wasn't being created and as we will see this is expected behavior. ",[636,67696,67698],{"id":67697},"entityscan-annotation","@EntityScan Annotation",[22,67700,67701,67702,67707],{},"The easy fix is to move the package and class under the main package and this will work as expected. Another way we can solve this problem is by telling Spring where to find our entities. We can do so by using the ",[677,67703,67706],{"href":67704,"rel":67705},"http://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/autoconfigure/domain/EntityScan.html",[681],"@EntityScan annotation",". We can use the base packages attribute and point to one or many packages to include when scanning for entities. We will add this annotation to our main application class. ",[52,67709,67711],{"className":54,"code":67710,"language":56,"meta":57,"style":57},"package com.therealdanvega;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.boot.autoconfigure.domain.EntityScan;\n\n@SpringBootApplication\n@EntityScan( basePackages = {\"domain\"} )\npublic class EntityDemoApplication {\n\n public static void main(String\\[\\] args) {\n SpringApplication.run(EntityDemoApplication.class, args);\n }\n}\n",[59,67712,67713,67719,67723,67729,67735,67742,67746,67752,67774,67785,67789,67809,67818,67822],{"__ignoreMap":57},[62,67714,67715,67717],{"class":64,"line":65},[62,67716,69],{"class":68},[62,67718,52481],{"class":72},[62,67720,67721],{"class":64,"line":76},[62,67722,79],{"emptyLinePlaceholder":13},[62,67724,67725,67727],{"class":64,"line":82},[62,67726,27875],{"class":68},[62,67728,52513],{"class":72},[62,67730,67731,67733],{"class":64,"line":89},[62,67732,27875],{"class":68},[62,67734,52520],{"class":72},[62,67736,67737,67739],{"class":64,"line":95},[62,67738,27875],{"class":68},[62,67740,67741],{"class":72}," org.springframework.boot.autoconfigure.domain.EntityScan;\n",[62,67743,67744],{"class":64,"line":101},[62,67745,79],{"emptyLinePlaceholder":13},[62,67747,67748,67750],{"class":64,"line":107},[62,67749,942],{"class":72},[62,67751,2079],{"class":68},[62,67753,67754,67756,67759,67761,67764,67766,67768,67771],{"class":64,"line":113},[62,67755,942],{"class":72},[62,67757,67758],{"class":68},"EntityScan",[62,67760,52630],{"class":72},[62,67762,67763],{"class":149},"basePackages",[62,67765,2556],{"class":68},[62,67767,57766],{"class":72},[62,67769,67770],{"class":1675},"\"domain\"",[62,67772,67773],{"class":72},"} )\n",[62,67775,67776,67778,67780,67783],{"class":64,"line":129},[62,67777,116],{"class":68},[62,67779,119],{"class":68},[62,67781,67782],{"class":122}," EntityDemoApplication",[62,67784,126],{"class":72},[62,67786,67787],{"class":64,"line":134},[62,67788,79],{"emptyLinePlaceholder":13},[62,67790,67791,67793,67795,67797,67799,67801,67803,67805,67807],{"class":64,"line":156},[62,67792,194],{"class":68},[62,67794,2101],{"class":68},[62,67796,200],{"class":68},[62,67798,2106],{"class":122},[62,67800,2109],{"class":72},[62,67802,973],{"class":889},[62,67804,52569],{"class":72},[62,67806,2117],{"class":889},[62,67808,768],{"class":72},[62,67810,67811,67813,67815],{"class":64,"line":161},[62,67812,2124],{"class":72},[62,67814,2127],{"class":122},[62,67816,67817],{"class":72},"(EntityDemoApplication.class, args);\n",[62,67819,67820],{"class":64,"line":167},[62,67821,223],{"class":72},[62,67823,67824],{"class":64,"line":173},[62,67825,379],{"class":72},[22,67827,67828],{},"Now if you start up the application and go to your H2 console you should see that the tweet table was created for you. ",[22,67830,67831,67835],{},[653,67832],{"alt":67833,"src":67834},"Tweet Class","./tweet_class.png"," ",[26,67837,1499],{"id":1498},[22,67839,67840,67841,67846],{},"As you can see this is a pretty easy mistake to make. If you would like to check out this project you can find the ",[677,67842,67845],{"href":67843,"rel":67844},"https://github.com/cfaddict/entity-scan-demo",[681],"source code for it on Github",". I hope you found this tutorial helpful and with that, I will leave you with a question. ",[22,67848,67849,67851],{},[646,67850,49733],{}," _What is a common mistake you made in your first few Spring Boot projects? _",[1527,67853,67854],{},"html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .spoOn, html code.shiki .spoOn{--shiki-default:#E36209;--shiki-dark:#FFAB70;--shiki-sepia:#E36209}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}",{"title":57,"searchDepth":76,"depth":76,"links":67856},[67857,67858,67863],{"id":62930,"depth":76,"text":62931},{"id":63387,"depth":76,"text":63388,"children":67859},[67860,67861,67862],{"id":67382,"depth":82,"text":67383},{"id":51932,"depth":82,"text":51933},{"id":67697,"depth":82,"text":67698},{"id":1498,"depth":76,"text":1499},{"_id":67865,"path":67866,"title":67867,"description":67868,"meta":67869,"body":67875},"content/blog/2016/10/04/made-20000-sleep.md","/blog/2016/10/04/made-20000-sleep","How I Made $20,000 in my Sleep","In this article I hope to inspire you to take action now!",{"slug":67870,"date":67871,"published":13,"tags":67872,"author":-1,"cover":67874,"excerpt":-1},"made-20000-sleep","2016-10-04T20:26:59-04:00",[67873],"udemy","./udemy_milestone_20k_2-1024x688.png",{"type":19,"value":67876,"toc":67958},[67877,67880,67883,67892,67895,67898,67901,67904,67911,67917,67920,67927,67933,67936,67946,67949,67952,67955],[22,67878,67879],{},"About a year ago, I set out on a journey that has forever changed my life. The title of this article might be a little bit misleading but I had to get your attention so that I could share my story with you. My hope is that anyone in a similar situation will be inspired by this and take action now.",[22,67881,67882],{},"This story actually started about 2 years ago in November. I have always been someone who enjoys learning from a variety of sources. I enjoy reading books and articles on the web, listening to podcasts, going to conferences and even watching videos online.",[22,67884,67885,67886,67891],{},"I loved watching videos online even if I knew the subject because I felt like I would always pick up one little tip or trick that would always make it all worth it. I can remember using ",[677,67887,67890],{"href":67888,"rel":67889},"http://www.lynda.com",[681],"www.lynda.com"," in the past but the idea of an online course was still very new to me. While you can find individual videos on YouTube for free it is very hard to stitch all of these videos together to teach you an entire subject.",[22,67893,67894],{},"In November of 2014 Udemy was running a Black Friday promotion where courses were only $10 each. I ended up watching the promo videos, reading reviews and purchased 5 courses that day. I think over the next year I bought a few more courses and probably went through half of them. I would compare this to purchasing books in the sense that I get really excited to read a new one but sometimes they sit on the shelf for awhile.",[22,67896,67897],{},"When October 2015 rolled around Udemy started running a promotion for all new instructors. Anyone who wanted to be a first time instructor could get involved. If you followed their guidelines and created a new course they would select a few winners and promote you to all of the Facebook & Twitter followers. I can't quite remember the numbers but I do remember that it was a lot. So I started to think about and asked myself \"Why not me?\". I was a blogger, YouTuber and conference speaker. I really enjoy helping others and I always believed that my background made me a great instructor. I was never the smartest guy in the room and I know what problems beginners were facing.",[22,67899,67900],{},"Just coming back inspired from the Spring One Conference I decided I was going to create my first course on Spring Boot. I spent the next 45 days or so working my tail off. I didn't know a whole lot about course creation or video production but I was willing to learn. All of the videos for my first course were shot in my home office on my iPhone. It wasn't the easiest thing to do but it ended up turning out ok. I also knew that the promo video was going to be important so I spent some time on that as well. At the end of the day though students want to learn Spring Boot and they are purchasing this course for the content.",[22,67902,67903],{},"I didn't end up being selected by Udemy to be in their promotions but I was included in the Black Friday sale and that was the most important thing. With 2,300 students at the time of this writing and 528 ratings I couldn't be happier with the way things turned out. So many students leave positive reviews on a regular basis and it just makes me so happy to be helping others.",[22,67905,67906],{},[677,67907,67910],{"href":67908,"rel":67909},"https://www.udemy.com/spring-boot-intro/?couponCode=TRDV20K",[681],"https://www.udemy.com/spring-boot-intro/",[22,67912,67913],{},[677,67914,67915],{"href":67915,"rel":67916},"https://www.youtube.com/watch?v=sEvZf70qyv4",[681],[22,67918,67919],{},"When that course was complete I already started working on my next course. One of the first things I did was upgrade my camera to a Canon 70D. This camera shot amazing video but one of the main features was a flip out screen so I could actually see what I was recording. With some experience on my hands I felt like it was less about the production this time around and now I could focus on the content. I choose to teach a course on the Groovy Programming Language. I have been using this language for years and it was just something I was really passionate about. I ended up launching this course towards the end of May 2016 and at the current time of this article it has almost 420 students and 75 reviews. This course has gotten nothing but positive feedback and I really don't feel like I would change anything about it at this time.",[22,67921,67922],{},[677,67923,67926],{"href":67924,"rel":67925},"https://www.udemy.com/apache-groovy/?couponCode=TRDV20K",[681],"https://www.udemy.com/apache-groovy/",[22,67928,67929],{},[677,67930,67931],{"href":67931,"rel":67932},"https://www.youtube.com/watch?v=NBe0NrSqPrU",[681],[22,67934,67935],{},"So here we are on October 4th which is 1 year from when I decided to commit to this and about 10 1/2 months from the time my first course launched. As you can see from the image below I have made about $20,000 over that time period. There are instructors making a lot more than this but I wanted to post these very real numbers to prove a point to you. If you have a passion for helping others this can be you. It is not going to be easy and there will be times where you might want to give up but I promise you it is all worth it. I have a full time job that I love doing and all of this can be done on nights and weekends.",[22,67937,67938,67945],{},[677,67939,67942],{"href":67940,"rel":67941},"https://therealdanvega.com/wp-content/uploads/2016/10/udemy_milestone_20k_2.png",[681],[653,67943],{"alt":67944,"src":67874},"Udemy Milestone 20K"," ",[22,67947,67948],{},"The reason for the title is where the passive income lifestyle comes into play. I won't lie there is a lot of up front work but the idea of waking up and seeing that you made money overnight is very cool. I also took a couple vacations this year and making money while you're lying on the beach is equally as rewarding.",[22,67950,67951],{},"You will have some work to do keeping the course updated and answering student questions but to be honest if you do the work up front there won't be a ton of work post launch. The time to take action is right now though. With the big Black Friday sale around the corner you will want to be a part of that.",[22,67953,67954],{},"I am currently working on course #3 with my friend John Thompson and we are really excited about this one. I am also branching out and already have course outlines completed for my next 2 courses which are going to be a little bit of a pivot for me, but an exciting one. I would love to hear your comments and questions so please leave them below or contact me.",[22,67956,67957],{},"This is just the start of my story and I have so much more to tell you about my experience. I have learned so much on my journey and I can't wait to share it with you. If you're interested in learning more about teaching online or anything else I am up to please sign up for my newsletter below.",{"title":57,"searchDepth":76,"depth":76,"links":67959},[],{"_id":67961,"path":67962,"title":67963,"description":67964,"meta":67965,"body":67970},"content/blog/2016/09/20/tech-elevator-meetup-summary.md","/blog/2016/09/20/tech-elevator-meetup-summary","Tech Elevator Meetup Summary","A summary of the panel I was asked to be on at Tech Elevator.",{"slug":67966,"date":67967,"published":13,"tags":67968,"author":-1,"cover":67969,"excerpt":-1},"tech-elevator-meetup-summary","2016-09-20T08:00:53-04:00",[61035],"./highres_454339592.jpeg",{"type":19,"value":67971,"toc":68052},[67972,67981,67987,67990],[22,67973,67974,67975,67980],{},"Last night I was lucky enough to be asked to ",[677,67976,67979],{"href":67977,"rel":67978},"http://www.meetup.com/Cleveland-learntocode/events/233766635/",[681],"speak on a panel at Tech Elevator",". The panel was made up of Senior Developers from the area and it was to a group of Jr. developers. The presentation was Lessons I've Learned in my programming career. It was great to talk to developers that are in the same spot I was in 15+ years ago and I really hope my advice was good for them to hear.",[22,67982,67983,67986],{},[653,67984],{"alt":57,"src":67985},"./14316849_10153742055740493_5473490718914398535_n.jpg"," ",[22,67988,67989],{},"We had a list of questions that the moderator was going to ask us as well as questions from the audience. These are the questions and I thought some of them were really good. I am actually going to try and create some you YouTube videos with my answers to these and get them posted. Thanks again to everyone who came out.",[915,67991,67992,67995,67998,68001,68004,68007,68010,68013,68016,68019,68022,68025,68028,68031,68034,68037,68040,68043,68046,68049],{},[37,67993,67994],{},"Why did you decide to be a developer?",[37,67996,67997],{},"What was your first role as a developer? Talk about the career path and/or phases (junior dev to senior dev).",[37,67999,68000],{},"Types of industries and how they compliment strengths students may have (they all have completed the StrengthsFinder assessment)",[37,68002,68003],{},"Job titles and what they mean.",[37,68005,68006],{},"What’s it like working for a startup vs corporate vs mid size?",[37,68008,68009],{},"What’s the biggest misconception about being a developer?",[37,68011,68012],{},"What’s one thing you would have done differently?",[37,68014,68015],{},"What other dev-related roles within a company should students be considering?",[37,68017,68018],{},"How can students get involved in the local tech community? Meetups, etc.",[37,68020,68021],{},"How can students continue to learn and grow their skills?",[37,68023,68024],{},"Where can a student be embraced by senior developers as a learner?",[37,68026,68027],{},"What is the hardest thing about being a developer?",[37,68029,68030],{},"What type of person does it take to be a developer?",[37,68032,68033],{},"What’s the difference between a really great developer and an average developer?",[37,68035,68036],{},"What advice would you give to someone just entering the field?",[37,68038,68039],{},"What kind of things do you look for in a team/organization that you are considering joining?",[37,68041,68042],{},"What are your favorite tools / technologies / platforms to work with and why?",[37,68044,68045],{},"What are your favorite kinds of projects to work on?",[37,68047,68048],{},"What are your least favorite kinds of projects to work on?",[37,68050,68051],{},"What was the most important lesson that you have learned as a developer? How did you learn it?",{"title":57,"searchDepth":76,"depth":76,"links":68053},[],{"_id":68055,"path":68056,"title":68057,"description":68058,"meta":68059,"body":68064},"content/blog/2016/01/14/spring-mvc-get-controller-method-name.md","/blog/2016/01/14/spring-mvc-get-controller-method-name","Spring MVC Get Controller & Method Name","How to get the current controller and method name Spring MVC Get Controller & Method Name",{"slug":68060,"date":68061,"published":13,"tags":68062,"author":-1,"cover":68063,"excerpt":-1},"spring-mvc-get-controller-method-name","2016-01-14T16:27:22-05:00",[56,11002],"./controller-cover.jpg",{"type":19,"value":68065,"toc":69071},[68066,68075,68080,68083,68098,68101,68291,68294,68330,68333,68497,68500,68504,68507,68776,68779,68818,68821,68830,68833,68909,68912,69061,69068],[22,68067,68068,68069,68074],{},"The one thing I really love about my ",[677,68070,68073],{"href":68071,"rel":68072},"https://www.udemy.com/spring-boot-intro/?couponCode=NEW_YEAR_29",[681],"Spring Boot Introductory Course"," is that my students are always asking me questions. I had this question come in recently and I thought we should take this chance to discuss it.",[29685,68076,68077],{},[22,68078,68079],{},"Dan, Suppose you have header, footer and content templates. The header has the navigation bootstrap code. For example you have multiple tabs in the navigation i.e. \"Admin\" and \"Customers\". How do you make the tab visually active if you are on Customer page or vice versa when using templates? Thanks, AK",[22,68081,68082],{},"I have done a lot of Grails web development and If you know anything about that you will know that it's built on Spring MVC. Grails makes 2 very important variables available to your views",[52,68084,68086],{"className":15773,"code":68085,"language":15775,"meta":57,"style":57},"${controllerName}\n${actionName}\n",[59,68087,68088,68093],{"__ignoreMap":57},[62,68089,68090],{"class":64,"line":65},[62,68091,68092],{"class":72},"${controllerName}\n",[62,68094,68095],{"class":64,"line":76},[62,68096,68097],{"class":72},"${actionName}\n",[22,68099,68100],{},"If these two variables were available to us in Thymeleaf that would make our job really easy right? Well sadly they are not so we need to look at a few alternatives. First we could manually set these variables in every method that sends content to a view.",[52,68102,68104],{"className":54,"code":68103,"language":56,"meta":57,"style":57},"@Controller\n@RequestMapping(\"/admin/posts\")\npublic class AdminPostController {\n\n private PostService postService;\n\n @Autowired\n public AdminPostController(PostService postService) {\n this.postService = postService;\n }\n\n @RequestMapping(\"/\")\n public String list(Model model, HttpServletRequest request) {\n model.addAttribute(\"posts\", postService.list());\n model.addAttribute(\"controllerName\", \"AdminPost\");\n model.addAttribute(\"actionName\", \"list\");\n return \"admin/post/list\";\n }\n\n}\n",[59,68105,68106,68112,68125,68136,68140,68146,68150,68156,68168,68178,68182,68186,68198,68216,68234,68252,68270,68279,68283,68287],{"__ignoreMap":57},[62,68107,68108,68110],{"class":64,"line":65},[62,68109,942],{"class":72},[62,68111,16624],{"class":68},[62,68113,68114,68116,68118,68120,68123],{"class":64,"line":76},[62,68115,942],{"class":72},[62,68117,10592],{"class":68},[62,68119,2109],{"class":72},[62,68121,68122],{"class":1675},"\"/admin/posts\"",[62,68124,2212],{"class":72},[62,68126,68127,68129,68131,68134],{"class":64,"line":82},[62,68128,116],{"class":68},[62,68130,119],{"class":68},[62,68132,68133],{"class":122}," AdminPostController",[62,68135,126],{"class":72},[62,68137,68138],{"class":64,"line":89},[62,68139,79],{"emptyLinePlaceholder":13},[62,68141,68142,68144],{"class":64,"line":95},[62,68143,137],{"class":68},[62,68145,41394],{"class":72},[62,68147,68148],{"class":64,"line":101},[62,68149,79],{"emptyLinePlaceholder":13},[62,68151,68152,68154],{"class":64,"line":107},[62,68153,2143],{"class":72},[62,68155,11687],{"class":68},[62,68157,68158,68160,68162,68164,68166],{"class":64,"line":113},[62,68159,194],{"class":68},[62,68161,68133],{"class":122},[62,68163,41407],{"class":72},[62,68165,41410],{"class":889},[62,68167,768],{"class":72},[62,68169,68170,68172,68174,68176],{"class":64,"line":129},[62,68171,2405],{"class":149},[62,68173,41419],{"class":72},[62,68175,146],{"class":68},[62,68177,41424],{"class":72},[62,68179,68180],{"class":64,"line":134},[62,68181,223],{"class":72},[62,68183,68184],{"class":64,"line":156},[62,68185,79],{"emptyLinePlaceholder":13},[62,68187,68188,68190,68192,68194,68196],{"class":64,"line":161},[62,68189,2143],{"class":72},[62,68191,10592],{"class":68},[62,68193,2109],{"class":72},[62,68195,15635],{"class":1675},[62,68197,2212],{"class":72},[62,68199,68200,68202,68204,68206,68208,68210,68212,68214],{"class":64,"line":167},[62,68201,194],{"class":68},[62,68203,2469],{"class":72},[62,68205,38739],{"class":122},[62,68207,22475],{"class":72},[62,68209,16671],{"class":889},[62,68211,49408],{"class":72},[62,68213,13262],{"class":889},[62,68215,768],{"class":72},[62,68217,68218,68220,68222,68224,68227,68230,68232],{"class":64,"line":173},[62,68219,22484],{"class":72},[62,68221,16736],{"class":122},[62,68223,2109],{"class":72},[62,68225,68226],{"class":1675},"\"posts\"",[62,68228,68229],{"class":72},", postService.",[62,68231,38739],{"class":122},[62,68233,1091],{"class":72},[62,68235,68236,68238,68240,68242,68245,68247,68250],{"class":64,"line":179},[62,68237,22484],{"class":72},[62,68239,16736],{"class":122},[62,68241,2109],{"class":72},[62,68243,68244],{"class":1675},"\"controllerName\"",[62,68246,976],{"class":72},[62,68248,68249],{"class":1675},"\"AdminPost\"",[62,68251,1133],{"class":72},[62,68253,68254,68256,68258,68260,68263,68265,68268],{"class":64,"line":185},[62,68255,22484],{"class":72},[62,68257,16736],{"class":122},[62,68259,2109],{"class":72},[62,68261,68262],{"class":1675},"\"actionName\"",[62,68264,976],{"class":72},[62,68266,68267],{"class":1675},"\"list\"",[62,68269,1133],{"class":72},[62,68271,68272,68274,68277],{"class":64,"line":191},[62,68273,360],{"class":68},[62,68275,68276],{"class":1675}," \"admin/post/list\"",[62,68278,153],{"class":72},[62,68280,68281],{"class":64,"line":209},[62,68282,223],{"class":72},[62,68284,68285],{"class":64,"line":220},[62,68286,79],{"emptyLinePlaceholder":13},[62,68288,68289],{"class":64,"line":226},[62,68290,379],{"class":72},[22,68292,68293],{},"And we can check the controller name and if it's that \"section\" we can add the active class.",[52,68295,68297],{"className":15773,"code":68296,"language":15775,"meta":57,"style":57},"\u003Cli class=\"dropdown\" th:class=\"${controllerName == 'AdminPost'} ? 'dropdown active' : 'dropdown'\" sec:authorize=\"hasRole('ROLE\\_ADMIN')\">\n",[59,68298,68299],{"__ignoreMap":57},[62,68300,68301,68303,68305,68307,68309,68312,68315,68317,68320,68323,68325,68328],{"class":64,"line":65},[62,68302,760],{"class":72},[62,68304,37],{"class":1780},[62,68306,119],{"class":122},[62,68308,146],{"class":72},[62,68310,68311],{"class":1675},"\"dropdown\"",[62,68313,68314],{"class":122}," th:class",[62,68316,146],{"class":72},[62,68318,68319],{"class":1675},"\"${controllerName == 'AdminPost'} ? 'dropdown active' : 'dropdown'\"",[62,68321,68322],{"class":122}," sec:authorize",[62,68324,146],{"class":72},[62,68326,68327],{"class":1675},"\"hasRole('ROLE\\_ADMIN')\"",[62,68329,1784],{"class":72},[22,68331,68332],{},"While this works it seems like a lot of work to have to set those variables in every single method. The http servlet request is made available in the view template so we can also do something like this.",[52,68334,68336],{"className":15773,"code":68335,"language":15775,"meta":57,"style":57},"\u003Cli class=\"dropdown\" th:class=\"${#httpServletRequest.requestURI.contains('/admin/posts/')} ? 'dropdown active' : 'dropdown'\" sec:authorize=\"hasRole('ROLE\\_ADMIN')\">\n \u003Ca href=\"#\" class=\"dropdown-toggle\" data-toggle=\"dropdown\" role=\"button\" aria-haspopup=\"true\" aria-expanded=\"false\">Admin \u003Cspan class=\"caret\">\u003C/span>\u003C/a>\n \u003Cul class=\"dropdown-menu\">\n \u003Cli>\u003Ca href=\"/admin/posts/\">Posts\u003C/a>\u003C/li>\n \u003C/ul>\n\u003C/li>\n",[59,68337,68338,68365,68437,68452,68481,68489],{"__ignoreMap":57},[62,68339,68340,68342,68344,68346,68348,68350,68352,68354,68357,68359,68361,68363],{"class":64,"line":65},[62,68341,760],{"class":72},[62,68343,37],{"class":1780},[62,68345,119],{"class":122},[62,68347,146],{"class":72},[62,68349,68311],{"class":1675},[62,68351,68314],{"class":122},[62,68353,146],{"class":72},[62,68355,68356],{"class":1675},"\"${#httpServletRequest.requestURI.contains('/admin/posts/')} ? 'dropdown active' : 'dropdown'\"",[62,68358,68322],{"class":122},[62,68360,146],{"class":72},[62,68362,68327],{"class":1675},[62,68364,1784],{"class":72},[62,68366,68367,68369,68371,68373,68375,68377,68379,68381,68384,68387,68389,68391,68394,68396,68399,68402,68404,68407,68410,68412,68415,68418,68420,68422,68424,68427,68429,68431,68433,68435],{"class":64,"line":76},[62,68368,1789],{"class":72},[62,68370,677],{"class":1780},[62,68372,16494],{"class":122},[62,68374,146],{"class":72},[62,68376,30687],{"class":1675},[62,68378,119],{"class":122},[62,68380,146],{"class":72},[62,68382,68383],{"class":1675},"\"dropdown-toggle\"",[62,68385,68386],{"class":122}," data-toggle",[62,68388,146],{"class":72},[62,68390,68311],{"class":1675},[62,68392,68393],{"class":122}," role",[62,68395,146],{"class":72},[62,68397,68398],{"class":1675},"\"button\"",[62,68400,68401],{"class":122}," aria-haspopup",[62,68403,146],{"class":72},[62,68405,68406],{"class":1675},"\"true\"",[62,68408,68409],{"class":122}," aria-expanded",[62,68411,146],{"class":72},[62,68413,68414],{"class":1675},"\"false\"",[62,68416,68417],{"class":72},">Admin \u003C",[62,68419,62],{"class":1780},[62,68421,119],{"class":122},[62,68423,146],{"class":72},[62,68425,68426],{"class":1675},"\"caret\"",[62,68428,15857],{"class":72},[62,68430,62],{"class":1780},[62,68432,15857],{"class":72},[62,68434,677],{"class":1780},[62,68436,1784],{"class":72},[62,68438,68439,68441,68443,68445,68447,68450],{"class":64,"line":82},[62,68440,1789],{"class":72},[62,68442,915],{"class":1780},[62,68444,119],{"class":122},[62,68446,146],{"class":72},[62,68448,68449],{"class":1675},"\"dropdown-menu\"",[62,68451,1784],{"class":72},[62,68453,68454,68456,68458,68461,68463,68465,68467,68470,68473,68475,68477,68479],{"class":64,"line":89},[62,68455,1896],{"class":72},[62,68457,37],{"class":1780},[62,68459,68460],{"class":72},">\u003C",[62,68462,677],{"class":1780},[62,68464,16494],{"class":122},[62,68466,146],{"class":72},[62,68468,68469],{"class":1675},"\"/admin/posts/\"",[62,68471,68472],{"class":72},">Posts\u003C/",[62,68474,677],{"class":1780},[62,68476,15857],{"class":72},[62,68478,37],{"class":1780},[62,68480,1784],{"class":72},[62,68482,68483,68485,68487],{"class":64,"line":95},[62,68484,1982],{"class":72},[62,68486,915],{"class":1780},[62,68488,1784],{"class":72},[62,68490,68491,68493,68495],{"class":64,"line":101},[62,68492,1818],{"class":72},[62,68494,37],{"class":1780},[62,68496,1784],{"class":72},[22,68498,68499],{},"While this will work some of the times trying to match on the URI isn't always a good choice and to me this just seems like a hacky solution.",[636,68501,68503],{"id":68502},"my-solution","My Solution",[22,68505,68506],{},"There might be other solutions to this puzzle but this is what I came up with and I think it works out pretty well. I like the idea of the controller and action names being available in my view but I don't want to set them on every request manually. Thankfully we can create an interceptor to do this for us. We are extending the HandlerInterceptorAdapter which gives us the ability to override the post handle method. In this method I am going to set the controller and action(method) name and send it to the view using ModelAndView.",[52,68508,68510],{"className":54,"code":68509,"language":56,"meta":57,"style":57},"package com.therealdanvega.interceptor;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.springframework.web.method.HandlerMethod;\nimport org.springframework.web.servlet.ModelAndView;\nimport org.springframework.web.servlet.handler.HandlerInterceptorAdapter;\n\npublic class BaseInterceptor extends HandlerInterceptorAdapter {\n\n public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {\n String controllerName = \"\";\n String actionName = \"\";\n\n if( handler instanceof HandlerMethod ) {\n // there are cases where this handler isn't an instance of HandlerMethod, so the cast fails.\n HandlerMethod handlerMethod = (HandlerMethod) handler;\n //controllerName = handlerMethod.getBean().getClass().getSimpleName().replace(\"Controller\", \"\");\n controllerName = handlerMethod.getBeanType().getSimpleName().replace(\"Controller\", \"\");\n actionName = handlerMethod.getMethod().getName();\n }\n\n modelAndView.addObject(\"controllerName\", controllerName );\n modelAndView.addObject(\"actionName\", actionName );\n }\n\n}\n",[59,68511,68512,68519,68523,68529,68535,68539,68546,68553,68560,68564,68580,68584,68619,68630,68641,68645,68657,68662,68672,68677,68711,68728,68732,68736,68751,68764,68768,68772],{"__ignoreMap":57},[62,68513,68514,68516],{"class":64,"line":65},[62,68515,69],{"class":68},[62,68517,68518],{"class":72}," com.therealdanvega.interceptor;\n",[62,68520,68521],{"class":64,"line":76},[62,68522,79],{"emptyLinePlaceholder":13},[62,68524,68525,68527],{"class":64,"line":82},[62,68526,27875],{"class":68},[62,68528,49528],{"class":72},[62,68530,68531,68533],{"class":64,"line":89},[62,68532,27875],{"class":68},[62,68534,49535],{"class":72},[62,68536,68537],{"class":64,"line":95},[62,68538,79],{"emptyLinePlaceholder":13},[62,68540,68541,68543],{"class":64,"line":101},[62,68542,27875],{"class":68},[62,68544,68545],{"class":72}," org.springframework.web.method.HandlerMethod;\n",[62,68547,68548,68550],{"class":64,"line":107},[62,68549,27875],{"class":68},[62,68551,68552],{"class":72}," org.springframework.web.servlet.ModelAndView;\n",[62,68554,68555,68557],{"class":64,"line":113},[62,68556,27875],{"class":68},[62,68558,68559],{"class":72}," org.springframework.web.servlet.handler.HandlerInterceptorAdapter;\n",[62,68561,68562],{"class":64,"line":129},[62,68563,79],{"emptyLinePlaceholder":13},[62,68565,68566,68568,68570,68573,68575,68578],{"class":64,"line":134},[62,68567,116],{"class":68},[62,68569,119],{"class":68},[62,68571,68572],{"class":122}," BaseInterceptor",[62,68574,8537],{"class":68},[62,68576,68577],{"class":122}," HandlerInterceptorAdapter",[62,68579,126],{"class":72},[62,68581,68582],{"class":64,"line":156},[62,68583,79],{"emptyLinePlaceholder":13},[62,68585,68586,68588,68590,68593,68595,68597,68599,68601,68604,68607,68610,68613,68615,68617],{"class":64,"line":161},[62,68587,194],{"class":68},[62,68589,200],{"class":68},[62,68591,68592],{"class":122}," postHandle",[62,68594,16663],{"class":72},[62,68596,13262],{"class":889},[62,68598,49632],{"class":72},[62,68600,13389],{"class":889},[62,68602,68603],{"class":72},", Object ",[62,68605,68606],{"class":889},"handler",[62,68608,68609],{"class":72},", ModelAndView ",[62,68611,68612],{"class":889},"modelAndView",[62,68614,5024],{"class":72},[62,68616,11501],{"class":68},[62,68618,11504],{"class":72},[62,68620,68621,68624,68626,68628],{"class":64,"line":167},[62,68622,68623],{"class":72}," String controllerName ",[62,68625,146],{"class":68},[62,68627,51048],{"class":1675},[62,68629,153],{"class":72},[62,68631,68632,68635,68637,68639],{"class":64,"line":173},[62,68633,68634],{"class":72}," String actionName ",[62,68636,146],{"class":68},[62,68638,51048],{"class":1675},[62,68640,153],{"class":72},[62,68642,68643],{"class":64,"line":179},[62,68644,79],{"emptyLinePlaceholder":13},[62,68646,68647,68649,68652,68654],{"class":64,"line":185},[62,68648,12741],{"class":68},[62,68650,68651],{"class":72},"( handler ",[62,68653,12747],{"class":68},[62,68655,68656],{"class":72}," HandlerMethod ) {\n",[62,68658,68659],{"class":64,"line":191},[62,68660,68661],{"class":85}," // there are cases where this handler isn't an instance of HandlerMethod, so the cast fails.\n",[62,68663,68664,68667,68669],{"class":64,"line":209},[62,68665,68666],{"class":72}," HandlerMethod handlerMethod ",[62,68668,146],{"class":68},[62,68670,68671],{"class":72}," (HandlerMethod) handler;\n",[62,68673,68674],{"class":64,"line":220},[62,68675,68676],{"class":85}," //controllerName = handlerMethod.getBean().getClass().getSimpleName().replace(\"Controller\", \"\");\n",[62,68678,68679,68682,68684,68687,68690,68692,68695,68697,68700,68702,68705,68707,68709],{"class":64,"line":226},[62,68680,68681],{"class":72}," controllerName ",[62,68683,146],{"class":68},[62,68685,68686],{"class":72}," handlerMethod.",[62,68688,68689],{"class":122},"getBeanType",[62,68691,3229],{"class":72},[62,68693,68694],{"class":122},"getSimpleName",[62,68696,3229],{"class":72},[62,68698,68699],{"class":122},"replace",[62,68701,2109],{"class":72},[62,68703,68704],{"class":1675},"\"Controller\"",[62,68706,976],{"class":72},[62,68708,25895],{"class":1675},[62,68710,1133],{"class":72},[62,68712,68713,68716,68718,68720,68722,68724,68726],{"class":64,"line":231},[62,68714,68715],{"class":72}," actionName ",[62,68717,146],{"class":68},[62,68719,68686],{"class":72},[62,68721,13290],{"class":122},[62,68723,3229],{"class":72},[62,68725,12678],{"class":122},[62,68727,822],{"class":72},[62,68729,68730],{"class":64,"line":236},[62,68731,533],{"class":72},[62,68733,68734],{"class":64,"line":242},[62,68735,79],{"emptyLinePlaceholder":13},[62,68737,68738,68741,68744,68746,68748],{"class":64,"line":247},[62,68739,68740],{"class":72}," modelAndView.",[62,68742,68743],{"class":122},"addObject",[62,68745,2109],{"class":72},[62,68747,68244],{"class":1675},[62,68749,68750],{"class":72},", controllerName );\n",[62,68752,68753,68755,68757,68759,68761],{"class":64,"line":252},[62,68754,68740],{"class":72},[62,68756,68743],{"class":122},[62,68758,2109],{"class":72},[62,68760,68262],{"class":1675},[62,68762,68763],{"class":72},", actionName );\n",[62,68765,68766],{"class":64,"line":257},[62,68767,223],{"class":72},[62,68769,68770],{"class":64,"line":271},[62,68771,79],{"emptyLinePlaceholder":13},[62,68773,68774],{"class":64,"line":281},[62,68775,379],{"class":72},[22,68777,68778],{},"There was one issue I found doing this. At first I was setting the controller using the following code.",[52,68780,68782],{"className":54,"code":68781,"language":56,"meta":57,"style":57},"controllerName = handlerMethod.getBean().getClass().getSimpleName().replace(\"Controller\", \"\");\n",[59,68783,68784],{"__ignoreMap":57},[62,68785,68786,68789,68791,68793,68796,68798,68800,68802,68804,68806,68808,68810,68812,68814,68816],{"class":64,"line":65},[62,68787,68788],{"class":72},"controllerName ",[62,68790,146],{"class":68},[62,68792,68686],{"class":72},[62,68794,68795],{"class":122},"getBean",[62,68797,3229],{"class":72},[62,68799,64744],{"class":122},[62,68801,3229],{"class":72},[62,68803,68694],{"class":122},[62,68805,3229],{"class":72},[62,68807,68699],{"class":122},[62,68809,2109],{"class":72},[62,68811,68704],{"class":1675},[62,68813,976],{"class":72},[62,68815,25895],{"class":1675},[62,68817,1133],{"class":72},[22,68819,68820],{},"This would work 99% of the time except when I was using Spring Security. When I was logged in Spring Security would wrap my controller with a proxy class and the name of my controller would look something like",[52,68822,68824],{"className":54,"code":68823,"language":56,"meta":57,"style":57},"AdminPostController$$EnhancerBySpringCGLIB$$3ef51261\n",[59,68825,68826],{"__ignoreMap":57},[62,68827,68828],{"class":64,"line":65},[62,68829,68823],{"class":72},[22,68831,68832],{},"So I switched it up and got the actual type of the class which gave me what I needed. Now that you have the Base Interceptor created you need to register it.",[52,68834,68836],{"className":54,"code":68835,"language":56,"meta":57,"style":57},"@Configuration\npublic class WebConfig extends WebMvcConfigurerAdapter {\n\n @Override\n public void addInterceptors(InterceptorRegistry registry) {\n registry.addInterceptor(new BaseInterceptor());\n }\n}\n",[59,68837,68838,68844,68859,68863,68869,68885,68901,68905],{"__ignoreMap":57},[62,68839,68840,68842],{"class":64,"line":65},[62,68841,942],{"class":72},[62,68843,11133],{"class":68},[62,68845,68846,68848,68850,68852,68854,68857],{"class":64,"line":76},[62,68847,116],{"class":68},[62,68849,119],{"class":68},[62,68851,55976],{"class":122},[62,68853,8537],{"class":68},[62,68855,68856],{"class":122}," WebMvcConfigurerAdapter",[62,68858,126],{"class":72},[62,68860,68861],{"class":64,"line":82},[62,68862,79],{"emptyLinePlaceholder":13},[62,68864,68865,68867],{"class":64,"line":89},[62,68866,2143],{"class":72},[62,68868,13555],{"class":68},[62,68870,68871,68873,68875,68878,68881,68883],{"class":64,"line":95},[62,68872,194],{"class":68},[62,68874,200],{"class":68},[62,68876,68877],{"class":122}," addInterceptors",[62,68879,68880],{"class":72},"(InterceptorRegistry ",[62,68882,39791],{"class":889},[62,68884,768],{"class":72},[62,68886,68887,68890,68893,68895,68897,68899],{"class":64,"line":101},[62,68888,68889],{"class":72}," registry.",[62,68891,68892],{"class":122},"addInterceptor",[62,68894,2109],{"class":72},[62,68896,2426],{"class":68},[62,68898,68572],{"class":122},[62,68900,1091],{"class":72},[62,68902,68903],{"class":64,"line":107},[62,68904,223],{"class":72},[62,68906,68907],{"class":64,"line":113},[62,68908,379],{"class":72},[22,68910,68911],{},"That is all there was to it. Now in every view I had those two variables available. In my template I was able to do something like this",[52,68913,68915],{"className":15773,"code":68914,"language":15775,"meta":57,"style":57},"\u003Cli class=\"dropdown\" th:class=\"${controllerName == 'AdminPost'} ? 'dropdown active' : 'dropdown'\" sec:authorize=\"hasRole('ROLE\\_ADMIN')\">\n \u003Ca href=\"#\" class=\"dropdown-toggle\" data-toggle=\"dropdown\" role=\"button\" aria-haspopup=\"true\" aria-expanded=\"false\">Admin \u003Cspan class=\"caret\">\u003C/span>\u003C/a>\n \u003Cul class=\"dropdown-menu\">\n \u003Cli>\u003Ca href=\"/admin/posts/\">Posts\u003C/a>\u003C/li>\n \u003C/ul>\n\u003C/li>\n",[59,68916,68917,68943,69005,69019,69045,69053],{"__ignoreMap":57},[62,68918,68919,68921,68923,68925,68927,68929,68931,68933,68935,68937,68939,68941],{"class":64,"line":65},[62,68920,760],{"class":72},[62,68922,37],{"class":1780},[62,68924,119],{"class":122},[62,68926,146],{"class":72},[62,68928,68311],{"class":1675},[62,68930,68314],{"class":122},[62,68932,146],{"class":72},[62,68934,68319],{"class":1675},[62,68936,68322],{"class":122},[62,68938,146],{"class":72},[62,68940,68327],{"class":1675},[62,68942,1784],{"class":72},[62,68944,68945,68947,68949,68951,68953,68955,68957,68959,68961,68963,68965,68967,68969,68971,68973,68975,68977,68979,68981,68983,68985,68987,68989,68991,68993,68995,68997,68999,69001,69003],{"class":64,"line":76},[62,68946,1789],{"class":72},[62,68948,677],{"class":1780},[62,68950,16494],{"class":122},[62,68952,146],{"class":72},[62,68954,30687],{"class":1675},[62,68956,119],{"class":122},[62,68958,146],{"class":72},[62,68960,68383],{"class":1675},[62,68962,68386],{"class":122},[62,68964,146],{"class":72},[62,68966,68311],{"class":1675},[62,68968,68393],{"class":122},[62,68970,146],{"class":72},[62,68972,68398],{"class":1675},[62,68974,68401],{"class":122},[62,68976,146],{"class":72},[62,68978,68406],{"class":1675},[62,68980,68409],{"class":122},[62,68982,146],{"class":72},[62,68984,68414],{"class":1675},[62,68986,68417],{"class":72},[62,68988,62],{"class":1780},[62,68990,119],{"class":122},[62,68992,146],{"class":72},[62,68994,68426],{"class":1675},[62,68996,15857],{"class":72},[62,68998,62],{"class":1780},[62,69000,15857],{"class":72},[62,69002,677],{"class":1780},[62,69004,1784],{"class":72},[62,69006,69007,69009,69011,69013,69015,69017],{"class":64,"line":82},[62,69008,1789],{"class":72},[62,69010,915],{"class":1780},[62,69012,119],{"class":122},[62,69014,146],{"class":72},[62,69016,68449],{"class":1675},[62,69018,1784],{"class":72},[62,69020,69021,69023,69025,69027,69029,69031,69033,69035,69037,69039,69041,69043],{"class":64,"line":89},[62,69022,1896],{"class":72},[62,69024,37],{"class":1780},[62,69026,68460],{"class":72},[62,69028,677],{"class":1780},[62,69030,16494],{"class":122},[62,69032,146],{"class":72},[62,69034,68469],{"class":1675},[62,69036,68472],{"class":72},[62,69038,677],{"class":1780},[62,69040,15857],{"class":72},[62,69042,37],{"class":1780},[62,69044,1784],{"class":72},[62,69046,69047,69049,69051],{"class":64,"line":95},[62,69048,1982],{"class":72},[62,69050,915],{"class":1780},[62,69052,1784],{"class":72},[62,69054,69055,69057,69059],{"class":64,"line":101},[62,69056,1818],{"class":72},[62,69058,37],{"class":1780},[62,69060,1784],{"class":72},[22,69062,69063,69064,69067],{},"I hope this helps and if you can think of any reasons ",[646,69065,69066],{},"NOT"," to do this or have a more elegant solution please let me know below.",[1527,69069,69070],{},"html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .spoOn, html code.shiki .spoOn{--shiki-default:#E36209;--shiki-dark:#FFAB70;--shiki-sepia:#E36209}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}html pre.shiki code .suaqH, html code.shiki .suaqH{--shiki-default:#22863A;--shiki-dark:#85E89D;--shiki-sepia:#22863A}html pre.shiki code .s4-Ip, html code.shiki .s4-Ip{--shiki-default:#6A737D;--shiki-dark:#6A737D;--shiki-sepia:#6A737D}",{"title":57,"searchDepth":76,"depth":76,"links":69072},[69073],{"id":68502,"depth":82,"text":68503},{"_id":69075,"path":69076,"title":69077,"description":69078,"meta":69079,"body":69084},"content/blog/2016/01/13/sending-async-emails-in-spring.md","/blog/2016/01/13/sending-async-emails-in-spring","Sending Async Emails in Spring","In this article I will walk you through how to send email asynchronously in Spring Boot.",{"slug":69080,"date":69081,"published":13,"tags":69082,"author":-1,"cover":69083,"excerpt":-1},"sending-async-emails-in-spring","2016-01-13T10:30:20-05:00",[56,11002],"./email-cover.jpg",{"type":19,"value":69085,"toc":69859},[69086,69089,69094,69097,69102,69107,69110,69148,69151,69204,69207,69450,69462,69728,69736,69839,69847,69850,69856],[22,69087,69088],{},"I have a student taking my course that sent me the following question",[29685,69090,69091],{},[22,69092,69093],{},"It would be really cool to show how to send this email asynchronously. Because it's never a good idea to send any sort of notification synchronously.",[22,69095,69096],{},"I do agree with him in that there is no reason for us block program execution for something like sending an email. I am going to take this chance to walk you through sending email asynchronously using Spring Boot. Start off by creating a basic new Spring Boot project using the Spring Initializr from the web or your favorite IDE. We are going to select Web & Mail as dependencies.",[22,69098,69099],{},[653,69100],{"alt":57,"src":69101},"./email_async_1.png",[22,69103,69104,67401],{},[653,69105],{"alt":57,"src":69106},"./email_async_2.png",[22,69108,69109],{},"By selecting web and mail as dependencies we should have these two starter dependencies included included in our build file.",[52,69111,69113],{"className":1769,"code":69112,"language":1771,"meta":57,"style":57},"\u003Cdependency>\n \u003CgroupId>org.springframework.boot\u003C/groupId>\n \u003CartifactId>spring-boot-starter-mail\u003C/artifactId>\n\u003C/dependency>\n\u003Cdependency>\n \u003CgroupId>org.springframework.boot\u003C/groupId>\n \u003CartifactId>spring-boot-starter-web\u003C/artifactId>\n\u003C/dependency>\n",[59,69114,69115,69119,69123,69128,69132,69136,69140,69144],{"__ignoreMap":57},[62,69116,69117],{"class":64,"line":65},[62,69118,46425],{},[62,69120,69121],{"class":64,"line":76},[62,69122,54736],{},[62,69124,69125],{"class":64,"line":82},[62,69126,69127],{}," \u003CartifactId>spring-boot-starter-mail\u003C/artifactId>\n",[62,69129,69130],{"class":64,"line":89},[62,69131,46445],{},[62,69133,69134],{"class":64,"line":95},[62,69135,46425],{},[62,69137,69138],{"class":64,"line":101},[62,69139,54736],{},[62,69141,69142],{"class":64,"line":107},[62,69143,56937],{},[62,69145,69146],{"class":64,"line":113},[62,69147,46445],{},[22,69149,69150],{},"With the starter mail dependency in place we need to configure our mail server properties.",[52,69152,69154],{"className":1663,"code":69153,"language":1665,"meta":57,"style":57},"spring.mail.host = smtp.gmail.com\nspring.mail.username = username@gmail.com\nspring.mail.password = xxxxxxxxxxxxxxxxxx\nspring.mail.port=587\nspring.mail.properties.mail.smtp.starttls.enable = true\n",[59,69155,69156,69166,69176,69186,69194],{"__ignoreMap":57},[62,69157,69158,69161,69163],{"class":64,"line":65},[62,69159,69160],{"class":122},"spring.mail.host",[62,69162,2556],{"class":1675},[62,69164,69165],{"class":1675}," smtp.gmail.com\n",[62,69167,69168,69171,69173],{"class":64,"line":76},[62,69169,69170],{"class":122},"spring.mail.username",[62,69172,2556],{"class":1675},[62,69174,69175],{"class":1675}," username@gmail.com\n",[62,69177,69178,69181,69183],{"class":64,"line":82},[62,69179,69180],{"class":122},"spring.mail.password",[62,69182,2556],{"class":1675},[62,69184,69185],{"class":1675}," xxxxxxxxxxxxxxxxxx\n",[62,69187,69188,69191],{"class":64,"line":89},[62,69189,69190],{"class":122},"spring.mail.port",[62,69192,69193],{"class":1675},"=587\n",[62,69195,69196,69199,69201],{"class":64,"line":95},[62,69197,69198],{"class":122},"spring.mail.properties.mail.smtp.starttls.enable",[62,69200,2556],{"class":1675},[62,69202,69203],{"class":149}," true\n",[22,69205,69206],{},"The first thing we are going to do is create a controller. This controller is going to have a request mapping of signup-success. This method is going to create a user (just a simple POJO) and then try to send a notification using our notification service.",[52,69208,69210],{"className":54,"code":69209,"language":56,"meta":57,"style":57},"@RestController\npublic class RegistrationController {\n\n private Logger logger = LoggerFactory.getLogger(RegistrationController.class);\n\n @Autowired\n private NotificationService notificationService;\n\n @RequestMapping(\"/signup-success\")\n public String signupSuccess(){\n\n // create user\n User user = new User();\n user.setFirstName(\"Dan\");\n user.setLastName(\"Vega\");\n user.setEmailAddress(\"dan@clecares.org\");\n\n // send a notification\n try {\n notificationService.sendNotificaitoin(user);\n }catch( Exception e ){\n // catch error\n logger.info(\"Error Sending Email: \" + e.getMessage());\n }\n\n return \"Thank you for registering with us.\";\n }\n\n}\n",[59,69211,69212,69218,69229,69233,69248,69252,69258,69265,69269,69282,69293,69297,69302,69314,69328,69341,69355,69359,69364,69370,69380,69395,69400,69421,69425,69429,69438,69442,69446],{"__ignoreMap":57},[62,69213,69214,69216],{"class":64,"line":65},[62,69215,942],{"class":72},[62,69217,2342],{"class":68},[62,69219,69220,69222,69224,69227],{"class":64,"line":76},[62,69221,116],{"class":68},[62,69223,119],{"class":68},[62,69225,69226],{"class":122}," RegistrationController",[62,69228,126],{"class":72},[62,69230,69231],{"class":64,"line":82},[62,69232,79],{"emptyLinePlaceholder":13},[62,69234,69235,69237,69239,69241,69243,69245],{"class":64,"line":89},[62,69236,137],{"class":68},[62,69238,61921],{"class":72},[62,69240,146],{"class":68},[62,69242,3066],{"class":72},[62,69244,3069],{"class":122},[62,69246,69247],{"class":72},"(RegistrationController.class);\n",[62,69249,69250],{"class":64,"line":95},[62,69251,79],{"emptyLinePlaceholder":13},[62,69253,69254,69256],{"class":64,"line":101},[62,69255,2143],{"class":72},[62,69257,11687],{"class":68},[62,69259,69260,69262],{"class":64,"line":107},[62,69261,137],{"class":68},[62,69263,69264],{"class":72}," NotificationService notificationService;\n",[62,69266,69267],{"class":64,"line":113},[62,69268,79],{"emptyLinePlaceholder":13},[62,69270,69271,69273,69275,69277,69280],{"class":64,"line":129},[62,69272,2143],{"class":72},[62,69274,10592],{"class":68},[62,69276,2109],{"class":72},[62,69278,69279],{"class":1675},"\"/signup-success\"",[62,69281,2212],{"class":72},[62,69283,69284,69286,69288,69291],{"class":64,"line":134},[62,69285,194],{"class":68},[62,69287,2469],{"class":72},[62,69289,69290],{"class":122},"signupSuccess",[62,69292,57791],{"class":72},[62,69294,69295],{"class":64,"line":156},[62,69296,79],{"emptyLinePlaceholder":13},[62,69298,69299],{"class":64,"line":161},[62,69300,69301],{"class":85}," // create user\n",[62,69303,69304,69306,69308,69310,69312],{"class":64,"line":167},[62,69305,64762],{"class":72},[62,69307,146],{"class":68},[62,69309,466],{"class":68},[62,69311,22289],{"class":122},[62,69313,822],{"class":72},[62,69315,69316,69319,69322,69324,69326],{"class":64,"line":173},[62,69317,69318],{"class":72}," user.",[62,69320,69321],{"class":122},"setFirstName",[62,69323,2109],{"class":72},[62,69325,25684],{"class":1675},[62,69327,1133],{"class":72},[62,69329,69330,69332,69335,69337,69339],{"class":64,"line":179},[62,69331,69318],{"class":72},[62,69333,69334],{"class":122},"setLastName",[62,69336,2109],{"class":72},[62,69338,52643],{"class":1675},[62,69340,1133],{"class":72},[62,69342,69343,69345,69348,69350,69353],{"class":64,"line":185},[62,69344,69318],{"class":72},[62,69346,69347],{"class":122},"setEmailAddress",[62,69349,2109],{"class":72},[62,69351,69352],{"class":1675},"\"dan@clecares.org\"",[62,69354,1133],{"class":72},[62,69356,69357],{"class":64,"line":191},[62,69358,79],{"emptyLinePlaceholder":13},[62,69360,69361],{"class":64,"line":209},[62,69362,69363],{"class":85}," // send a notification\n",[62,69365,69366,69368],{"class":64,"line":220},[62,69367,807],{"class":68},[62,69369,126],{"class":72},[62,69371,69372,69375,69378],{"class":64,"line":226},[62,69373,69374],{"class":72}," notificationService.",[62,69376,69377],{"class":122},"sendNotificaitoin",[62,69379,15739],{"class":72},[62,69381,69382,69385,69387,69390,69392],{"class":64,"line":231},[62,69383,69384],{"class":72}," }",[62,69386,883],{"class":68},[62,69388,69389],{"class":72},"( Exception ",[62,69391,890],{"class":889},[62,69393,69394],{"class":72}," ){\n",[62,69396,69397],{"class":64,"line":236},[62,69398,69399],{"class":85}," // catch error\n",[62,69401,69402,69405,69407,69409,69412,69414,69417,69419],{"class":64,"line":242},[62,69403,69404],{"class":72}," logger.",[62,69406,12688],{"class":122},[62,69408,2109],{"class":72},[62,69410,69411],{"class":1675},"\"Error Sending Email: \"",[62,69413,4507],{"class":68},[62,69415,69416],{"class":72}," e.",[62,69418,28459],{"class":122},[62,69420,1091],{"class":72},[62,69422,69423],{"class":64,"line":247},[62,69424,533],{"class":72},[62,69426,69427],{"class":64,"line":252},[62,69428,79],{"emptyLinePlaceholder":13},[62,69430,69431,69433,69436],{"class":64,"line":257},[62,69432,360],{"class":68},[62,69434,69435],{"class":1675}," \"Thank you for registering with us.\"",[62,69437,153],{"class":72},[62,69439,69440],{"class":64,"line":271},[62,69441,223],{"class":72},[62,69443,69444],{"class":64,"line":281},[62,69445,79],{"emptyLinePlaceholder":13},[62,69447,69448],{"class":64,"line":286},[62,69449,379],{"class":72},[22,69451,69452,69453,69458,69459,13206],{},"The Notification Service is where the real magic happens. At the start of our send notification method we are going to simulate a long process by using the sleep method. Notice that we have add the ",[677,69454,69457],{"href":69455,"rel":69456},"https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/scheduling/annotation/Async.html",[681],"@Async annotation"," to the method that marks a method as a candidate for ",[4534,69460,69461],{},"asynchronous",[52,69463,69465],{"className":54,"code":69464,"language":56,"meta":57,"style":57},"@Service\npublic class NotificationService {\n\n private JavaMailSender javaMailSender;\n\n @Autowired\n public NotificationService(JavaMailSender javaMailSender){\n this.javaMailSender = javaMailSender;\n }\n\n @Async\n public void sendNotificaitoin(User user) throws MailException, InterruptedException {\n\n System.out.println(\"Sleeping now...\");\n Thread.sleep(10000);\n\n System.out.println(\"Sending email...\");\n\n SimpleMailMessage mail = new SimpleMailMessage();\n mail.setTo(user.getEmailAddress());\n mail.setFrom(\"danvega@gmail.com\");\n mail.setSubject(\"Spring Boot is awesome!\");\n mail.setText(\"Why aren't you using Spring Boot?\");\n javaMailSender.send(mail);\n\n System.out.println(\"Email Sent!\");\n }\n\n}\n",[59,69466,69467,69473,69484,69488,69495,69499,69505,69519,69531,69535,69539,69546,69567,69571,69584,69597,69601,69614,69618,69632,69648,69661,69675,69689,69699,69703,69716,69720,69724],{"__ignoreMap":57},[62,69468,69469,69471],{"class":64,"line":65},[62,69470,942],{"class":72},[62,69472,945],{"class":68},[62,69474,69475,69477,69479,69482],{"class":64,"line":76},[62,69476,116],{"class":68},[62,69478,119],{"class":68},[62,69480,69481],{"class":122}," NotificationService",[62,69483,126],{"class":72},[62,69485,69486],{"class":64,"line":82},[62,69487,79],{"emptyLinePlaceholder":13},[62,69489,69490,69492],{"class":64,"line":89},[62,69491,137],{"class":68},[62,69493,69494],{"class":72}," JavaMailSender javaMailSender;\n",[62,69496,69497],{"class":64,"line":95},[62,69498,79],{"emptyLinePlaceholder":13},[62,69500,69501,69503],{"class":64,"line":101},[62,69502,2143],{"class":72},[62,69504,11687],{"class":68},[62,69506,69507,69509,69511,69514,69517],{"class":64,"line":107},[62,69508,194],{"class":68},[62,69510,69481],{"class":122},[62,69512,69513],{"class":72},"(JavaMailSender ",[62,69515,69516],{"class":889},"javaMailSender",[62,69518,34126],{"class":72},[62,69520,69521,69523,69526,69528],{"class":64,"line":113},[62,69522,2405],{"class":149},[62,69524,69525],{"class":72},".javaMailSender ",[62,69527,146],{"class":68},[62,69529,69530],{"class":72}," javaMailSender;\n",[62,69532,69533],{"class":64,"line":129},[62,69534,223],{"class":72},[62,69536,69537],{"class":64,"line":134},[62,69538,79],{"emptyLinePlaceholder":13},[62,69540,69541,69543],{"class":64,"line":156},[62,69542,2143],{"class":72},[62,69544,69545],{"class":68},"Async\n",[62,69547,69548,69550,69552,69555,69558,69560,69562,69564],{"class":64,"line":161},[62,69549,194],{"class":68},[62,69551,200],{"class":68},[62,69553,69554],{"class":122}," sendNotificaitoin",[62,69556,69557],{"class":72},"(User ",[62,69559,2502],{"class":889},[62,69561,5024],{"class":72},[62,69563,11501],{"class":68},[62,69565,69566],{"class":72}," MailException, InterruptedException {\n",[62,69568,69569],{"class":64,"line":167},[62,69570,79],{"emptyLinePlaceholder":13},[62,69572,69573,69575,69577,69579,69582],{"class":64,"line":173},[62,69574,27297],{"class":72},[62,69576,2244],{"class":122},[62,69578,2109],{"class":72},[62,69580,69581],{"class":1675},"\"Sleeping now...\"",[62,69583,1133],{"class":72},[62,69585,69586,69588,69590,69592,69595],{"class":64,"line":179},[62,69587,48438],{"class":72},[62,69589,847],{"class":122},[62,69591,2109],{"class":72},[62,69593,69594],{"class":149},"10000",[62,69596,1133],{"class":72},[62,69598,69599],{"class":64,"line":185},[62,69600,79],{"emptyLinePlaceholder":13},[62,69602,69603,69605,69607,69609,69612],{"class":64,"line":191},[62,69604,27297],{"class":72},[62,69606,2244],{"class":122},[62,69608,2109],{"class":72},[62,69610,69611],{"class":1675},"\"Sending email...\"",[62,69613,1133],{"class":72},[62,69615,69616],{"class":64,"line":209},[62,69617,79],{"emptyLinePlaceholder":13},[62,69619,69620,69623,69625,69627,69630],{"class":64,"line":220},[62,69621,69622],{"class":72}," SimpleMailMessage mail ",[62,69624,146],{"class":68},[62,69626,466],{"class":68},[62,69628,69629],{"class":122}," SimpleMailMessage",[62,69631,822],{"class":72},[62,69633,69634,69637,69640,69643,69646],{"class":64,"line":226},[62,69635,69636],{"class":72}," mail.",[62,69638,69639],{"class":122},"setTo",[62,69641,69642],{"class":72},"(user.",[62,69644,69645],{"class":122},"getEmailAddress",[62,69647,1091],{"class":72},[62,69649,69650,69652,69655,69657,69659],{"class":64,"line":231},[62,69651,69636],{"class":72},[62,69653,69654],{"class":122},"setFrom",[62,69656,2109],{"class":72},[62,69658,52648],{"class":1675},[62,69660,1133],{"class":72},[62,69662,69663,69665,69668,69670,69673],{"class":64,"line":236},[62,69664,69636],{"class":72},[62,69666,69667],{"class":122},"setSubject",[62,69669,2109],{"class":72},[62,69671,69672],{"class":1675},"\"Spring Boot is awesome!\"",[62,69674,1133],{"class":72},[62,69676,69677,69679,69682,69684,69687],{"class":64,"line":242},[62,69678,69636],{"class":72},[62,69680,69681],{"class":122},"setText",[62,69683,2109],{"class":72},[62,69685,69686],{"class":1675},"\"Why aren't you using Spring Boot?\"",[62,69688,1133],{"class":72},[62,69690,69691,69694,69696],{"class":64,"line":247},[62,69692,69693],{"class":72}," javaMailSender.",[62,69695,18436],{"class":122},[62,69697,69698],{"class":72},"(mail);\n",[62,69700,69701],{"class":64,"line":252},[62,69702,79],{"emptyLinePlaceholder":13},[62,69704,69705,69707,69709,69711,69714],{"class":64,"line":257},[62,69706,27297],{"class":72},[62,69708,2244],{"class":122},[62,69710,2109],{"class":72},[62,69712,69713],{"class":1675},"\"Email Sent!\"",[62,69715,1133],{"class":72},[62,69717,69718],{"class":64,"line":271},[62,69719,223],{"class":72},[62,69721,69722],{"class":64,"line":281},[62,69723,79],{"emptyLinePlaceholder":13},[62,69725,69726],{"class":64,"line":286},[62,69727,379],{"class":72},[22,69729,69730,69731,2755],{},"We have one final task to make all of this work. Go to the main application class and add the ",[677,69732,69735],{"href":69733,"rel":69734},"https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/scheduling/annotation/EnableAsync.html",[681],"@EnableAsync annotation",[52,69737,69739],{"className":54,"code":69738,"language":56,"meta":57,"style":57},"package com.therealdanvega;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.scheduling.annotation.EnableAsync;\n\n@SpringBootApplication\n@EnableAsync\npublic class SendingEmailAsyncApplication {\n\n public static void main(String\\[\\] args) {\n SpringApplication.run(SendingEmailAsyncApplication.class, args);\n }\n}\n",[59,69740,69741,69747,69751,69757,69763,69770,69774,69780,69787,69798,69802,69822,69831,69835],{"__ignoreMap":57},[62,69742,69743,69745],{"class":64,"line":65},[62,69744,69],{"class":68},[62,69746,52481],{"class":72},[62,69748,69749],{"class":64,"line":76},[62,69750,79],{"emptyLinePlaceholder":13},[62,69752,69753,69755],{"class":64,"line":82},[62,69754,27875],{"class":68},[62,69756,52513],{"class":72},[62,69758,69759,69761],{"class":64,"line":89},[62,69760,27875],{"class":68},[62,69762,52520],{"class":72},[62,69764,69765,69767],{"class":64,"line":95},[62,69766,27875],{"class":68},[62,69768,69769],{"class":72}," org.springframework.scheduling.annotation.EnableAsync;\n",[62,69771,69772],{"class":64,"line":101},[62,69773,79],{"emptyLinePlaceholder":13},[62,69775,69776,69778],{"class":64,"line":107},[62,69777,942],{"class":72},[62,69779,2079],{"class":68},[62,69781,69782,69784],{"class":64,"line":113},[62,69783,942],{"class":72},[62,69785,69786],{"class":68},"EnableAsync\n",[62,69788,69789,69791,69793,69796],{"class":64,"line":129},[62,69790,116],{"class":68},[62,69792,119],{"class":68},[62,69794,69795],{"class":122}," SendingEmailAsyncApplication",[62,69797,126],{"class":72},[62,69799,69800],{"class":64,"line":134},[62,69801,79],{"emptyLinePlaceholder":13},[62,69803,69804,69806,69808,69810,69812,69814,69816,69818,69820],{"class":64,"line":156},[62,69805,194],{"class":68},[62,69807,2101],{"class":68},[62,69809,200],{"class":68},[62,69811,2106],{"class":122},[62,69813,2109],{"class":72},[62,69815,973],{"class":889},[62,69817,52569],{"class":72},[62,69819,2117],{"class":889},[62,69821,768],{"class":72},[62,69823,69824,69826,69828],{"class":64,"line":161},[62,69825,2124],{"class":72},[62,69827,2127],{"class":122},[62,69829,69830],{"class":72},"(SendingEmailAsyncApplication.class, args);\n",[62,69832,69833],{"class":64,"line":167},[62,69834,223],{"class":72},[62,69836,69837],{"class":64,"line":173},[62,69838,379],{"class":72},[22,69840,69841,69842,69846],{},"Start the application and go to ",[677,69843,69844],{"href":69844,"rel":69845},"http://localhost:8080/signup-success",[681],". You will see the message \"Thank you for registering with us.\" right away and in the console you will see the Sleeping/Sending/Sent messages printed out to the console. If you aren't familiar with Async this is where the real power comes in. The program is not blocking a task like sending an email and returns execution to the user while it performs that task in the background.",[22,69848,69849],{},"You can grab the source for this application below.",[22,69851,69852],{},[677,69853,69854],{"href":69854,"rel":69855},"https://github.com/danvega/spring-boot-intro/tree/master/guides/sending-email-async",[681],[1527,69857,69858],{},"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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .s4-Ip, html code.shiki .s4-Ip{--shiki-default:#6A737D;--shiki-dark:#6A737D;--shiki-sepia:#6A737D}html pre.shiki code .spoOn, html code.shiki .spoOn{--shiki-default:#E36209;--shiki-dark:#FFAB70;--shiki-sepia:#E36209}",{"title":57,"searchDepth":76,"depth":76,"links":69860},[],{"_id":69862,"path":69863,"title":69864,"description":69864,"meta":69865,"body":69870},"content/blog/2015/11/25/using-gorm-in-spring-boot.md","/blog/2015/11/25/using-gorm-in-spring-boot","Using GORM in Spring Boot",{"slug":69866,"date":69867,"published":13,"tags":69868,"author":-1,"cover":69869,"excerpt":-1},"using-gorm-in-spring-boot","2015-11-25T11:43:13-05:00",[53536,11002,56],"./adult-back-view-data-1181345.jpg",{"type":19,"value":69871,"toc":71835},[69872,69890,69893,69905,69912,69916,69929,69945,69954,69958,70094,70097,70103,70106,70109,70112,70134,70143,70150,70154,70157,70409,70412,70430,70433,70767,70770,70774,70777,71141,71144,71147,71150,71155,71158,71162,71165,71168,71183,71214,71223,71401,71404,71408,71425,71428,71431,71819,71822,71824,71832],[22,69873,69874,69875,69880,69881,69884,69885,2755],{},"In this tutorial I am going to explain how to use ",[677,69876,69879],{"href":69877,"rel":69878},"http://grails.github.io/grails-data-mapping/latest/",[681],"GORM"," in Spring Boot. If you have been following along on this blog lately you know that I have been working a lot with ",[677,69882,2925],{"href":61147,"rel":69883},[681],". So much so that ",[677,69886,69889],{"href":69887,"rel":69888},"http://udemy.com/spring-boot-intro",[681],"I created a course on it",[22,69891,69892],{},"I really do like Spring Data in the limited time that I have had to play around with it and this is by no means an article against it. I like to play outside the sandbox sometimes and thats what we are going to do here. I have been working with Grails the past few years and really have grown to love GORM.",[22,69894,69895,69896,69898,69899,69904],{},"If you don't know what GORM is it stands for Grails Object Relational Mapper. ",[646,69897,69879],{}," is the data access toolkit used by ",[677,69900,69903],{"href":69901,"rel":69902},"http://grails.org/",[681],"Grails"," and provides a rich set of APIs for accessing relational and non-relational data including implementations for Hibernate (SQL), MongoDB, Neo4j, Cassandra, Redis and an in-memory ConcurrentHashMap for testing. They just released a new website to go along with",[22,69906,69907,69911],{},[653,69908],{"alt":69909,"src":69910},"gorm5","./gorm5.png"," ",[636,69913,69915],{"id":69914},"gorm-in-spring-boot-demo","GORM in Spring Boot Demo",[22,69917,69918,69919,69922,69923,69928],{},"The first thing you will want to do is create a new project using the ",[677,69920,24606],{"href":2901,"rel":69921},[681],". You will want to include the Web & H2 database dependencies. I am also selecting Groovy ( ",[677,69924,69927],{"href":69925,"rel":69926},"http://www.groovy-lang.org/",[681],"Apache Groovy FTW"," ) as my language and Gradle as my build system. I am using IntelliJ here but you can use whatever your comfortable using. When the project has loaded we are going to add a new dependency to our build.gradle.",[52,69930,69932],{"className":54,"code":69931,"language":56,"meta":57,"style":57},"compile(\"org.grails:gorm-hibernate4-spring-boot:5.0.0.RC1\")\n",[59,69933,69934],{"__ignoreMap":57},[62,69935,69936,69938,69940,69943],{"class":64,"line":65},[62,69937,6624],{"class":122},[62,69939,2109],{"class":72},[62,69941,69942],{"class":1675},"\"org.grails:gorm-hibernate4-spring-boot:5.0.0.RC1\"",[62,69944,2212],{"class":72},[22,69946,69947,69948,69953],{},"Now that we have the appropriate dependencies included we can begin building our application. I will talk through a couple GORM specific things in here but this is no way a GORM 101 tutorial. If you want to learn more about how to use GORM ",[677,69949,69952],{"href":69950,"rel":69951},"http://grails.github.io/grails-doc/latest/guide/GORM.html",[681],"please read through the documentation",". As a convention I like to keep my entities in a separate package so I will create a new one called domain. I will create a new Groovy class and call it Post.",[636,69955,69957],{"id":69956},"creating-the-domain-object","Creating the domain object",[52,69959,69961],{"className":54,"code":69960,"language":56,"meta":57,"style":57},"package com.therealdanvega.domain\n\nimport grails.persistence.Entity\n\n@Entity\nclass Post {\n\n String title\n String body\n String teaser\n String slug\n Date postedOn\n\n static mapping = {\n version false\n body type: 'text'\n teaser type: 'text'\n }\n\n}\n",[59,69962,69963,69970,69974,69987,69991,69999,70009,70013,70019,70026,70033,70040,70053,70057,70065,70072,70077,70082,70086,70090],{"__ignoreMap":57},[62,69964,69965,69967],{"class":64,"line":65},[62,69966,69],{"class":68},[62,69968,69969],{"class":72}," com.therealdanvega.domain\n",[62,69971,69972],{"class":64,"line":76},[62,69973,79],{"emptyLinePlaceholder":13},[62,69975,69976,69978,69981,69984],{"class":64,"line":82},[62,69977,27875],{"class":23824},[62,69979,69980],{"class":72}," grails.persistence.",[62,69982,69983],{"class":23824},"E",[62,69985,69986],{"class":72},"ntity\n",[62,69988,69989],{"class":64,"line":89},[62,69990,79],{"emptyLinePlaceholder":13},[62,69992,69993,69995,69997],{"class":64,"line":95},[62,69994,942],{"class":72},[62,69996,69983],{"class":23824},[62,69998,69986],{"class":72},[62,70000,70001,70003,70006],{"class":64,"line":101},[62,70002,11671],{"class":23824},[62,70004,70005],{"class":23824}," P",[62,70007,70008],{"class":72},"ost {\n",[62,70010,70011],{"class":64,"line":107},[62,70012,79],{"emptyLinePlaceholder":13},[62,70014,70015,70017],{"class":64,"line":113},[62,70016,53871],{"class":23824},[62,70018,53874],{"class":72},[62,70020,70021,70023],{"class":64,"line":129},[62,70022,53871],{"class":23824},[62,70024,70025],{"class":72},"tring body\n",[62,70027,70028,70030],{"class":64,"line":134},[62,70029,53871],{"class":23824},[62,70031,70032],{"class":72},"tring teaser\n",[62,70034,70035,70037],{"class":64,"line":156},[62,70036,53871],{"class":23824},[62,70038,70039],{"class":72},"tring slug\n",[62,70041,70042,70045,70048,70051],{"class":64,"line":161},[62,70043,70044],{"class":23824}," D",[62,70046,70047],{"class":72},"ate posted",[62,70049,70050],{"class":23824},"O",[62,70052,54318],{"class":72},[62,70054,70055],{"class":64,"line":167},[62,70056,79],{"emptyLinePlaceholder":13},[62,70058,70059,70062],{"class":64,"line":173},[62,70060,70061],{"class":23824}," static",[62,70063,70064],{"class":72}," mapping = {\n",[62,70066,70067,70070],{"class":64,"line":179},[62,70068,70069],{"class":72}," version ",[62,70071,40782],{"class":23824},[62,70073,70074],{"class":64,"line":185},[62,70075,70076],{"class":72}," body type: 'text'\n",[62,70078,70079],{"class":64,"line":191},[62,70080,70081],{"class":72}," teaser type: 'text'\n",[62,70083,70084],{"class":64,"line":209},[62,70085,223],{"class":72},[62,70087,70088],{"class":64,"line":220},[62,70089,79],{"emptyLinePlaceholder":13},[62,70091,70092],{"class":64,"line":226},[62,70093,379],{"class":72},[22,70095,70096],{},"When we create domain objects in Grails we use a convention of placing them in a specific location. In Spring Boot we don't have that convention so we need a way to mark this class as an domain class and we can do so by using the @Entity annotation. The default convention by GORM is to use the class name as the table name. If the class was separated by camel case \"BlogPosts\" the table name would be \"blog_posts\". You can override that by using the mapping closure but we are just going to leave it as is.",[22,70098,70099,70100,70102],{},"You also might of noticed that we didn't import the Date class and that is because Groovy imports that along with a bunch of others by default. Finally we have some specific information about the body and teaser. By default a String data type would be a ",[4534,70101,41867],{}," and we actually want these columns to hold a bunch of text.",[22,70104,70105],{},"In Spring Data we would do something similar and at this time we would now create a new repository. In GORM we don't have to do that and the domain object itself is going to give us the power to do basic CRUD, dynamic finders and so much more. I would read through the docs to learn more about what GORM can do but the good news for you is all we had to do was create that domain object to get it all.",[636,70107,70108],{"id":2290},"Running the application",[22,70110,70111],{},"Before we go creating a controller we should test out our changes. Before we can do that lets open up our application.properties file and add the 2 lines of configuration.",[52,70113,70115],{"className":54,"code":70114,"language":56,"meta":57,"style":57},"spring.h2.console.enabled=true\nspring.h2.console.path=/console\n",[59,70116,70117,70125],{"__ignoreMap":57},[62,70118,70119,70121,70123],{"class":64,"line":65},[62,70120,41993],{"class":72},[62,70122,146],{"class":68},[62,70124,51914],{"class":149},[62,70126,70127,70130,70132],{"class":64,"line":76},[62,70128,70129],{"class":72},"spring.h2.console.path",[62,70131,51922],{"class":68},[62,70133,51929],{"class":72},[22,70135,70136,70137,70142],{},"This allows us to hit the H2 web console and see our brand new table created. Run the application and visit ",[677,70138,70141],{"href":70139,"rel":70140},"http://localhost:8080/console",[681],"http://localhost:8080/console ","to see the POST table created.",[22,70144,70145,70149],{},[653,70146],{"alt":70147,"src":70148},"post_table","./post_table.png"," ",[636,70151,70153],{"id":70152},"accessing-the-data","Accessing the data",[22,70155,70156],{},"Now that we have our domain object we need to create a controller and some mappings to grab some data from the database. Enforcing some good standards here we are going to move our business logic (data retrieval) to a service and we are going to program to an interface.",[52,70158,70160],{"className":54,"code":70159,"language":56,"meta":57,"style":57},"package com.therealdanvega.service\n\nimport com.therealdanvega.domain.Post\n\ninterface PostService {\n\n ArrayList\u003CPost> list()\n\n Post read()\n}\n\npackage com.therealdanvega.service\n\nimport com.therealdanvega.domain.Post\nimport org.springframework.stereotype.Service\n\n@Service('postService')\nclass PostServiceImpl implements PostService {\n\n @Override\n ArrayList\u003CPost> list() {\n Post.list()\n }\n\n @Override\n Post read(int id) {\n Post.get(id)\n }\n\n}\n",[59,70161,70162,70169,70173,70186,70190,70204,70208,70227,70231,70239,70243,70247,70253,70257,70267,70279,70283,70297,70326,70330,70339,70354,70362,70366,70370,70378,70390,70397,70401,70405],{"__ignoreMap":57},[62,70163,70164,70166],{"class":64,"line":65},[62,70165,69],{"class":68},[62,70167,70168],{"class":72}," com.therealdanvega.service\n",[62,70170,70171],{"class":64,"line":76},[62,70172,79],{"emptyLinePlaceholder":13},[62,70174,70175,70177,70180,70183],{"class":64,"line":82},[62,70176,27875],{"class":23824},[62,70178,70179],{"class":72}," com.therealdanvega.domain.",[62,70181,70182],{"class":23824},"P",[62,70184,70185],{"class":72},"ost\n",[62,70187,70188],{"class":64,"line":89},[62,70189,79],{"emptyLinePlaceholder":13},[62,70191,70192,70194,70196,70199,70201],{"class":64,"line":95},[62,70193,36687],{"class":23824},[62,70195,70005],{"class":23824},[62,70197,70198],{"class":72},"ost",[62,70200,37504],{"class":23824},[62,70202,70203],{"class":72},"ervice {\n",[62,70205,70206],{"class":64,"line":101},[62,70207,79],{"emptyLinePlaceholder":13},[62,70209,70210,70213,70216,70219,70222,70224],{"class":64,"line":107},[62,70211,70212],{"class":23824}," A",[62,70214,70215],{"class":72},"rray",[62,70217,70218],{"class":23824},"L",[62,70220,70221],{"class":72},"ist\u003C",[62,70223,70182],{"class":23824},[62,70225,70226],{"class":72},"ost> list()\n",[62,70228,70229],{"class":64,"line":113},[62,70230,79],{"emptyLinePlaceholder":13},[62,70232,70233,70236],{"class":64,"line":129},[62,70234,70235],{"class":23824}," P",[62,70237,70238],{"class":72},"ost read()\n",[62,70240,70241],{"class":64,"line":134},[62,70242,379],{"class":72},[62,70244,70245],{"class":64,"line":156},[62,70246,79],{"emptyLinePlaceholder":13},[62,70248,70249,70251],{"class":64,"line":161},[62,70250,69],{"class":23824},[62,70252,70168],{"class":72},[62,70254,70255],{"class":64,"line":167},[62,70256,79],{"emptyLinePlaceholder":13},[62,70258,70259,70261,70263,70265],{"class":64,"line":173},[62,70260,27875],{"class":23824},[62,70262,70179],{"class":72},[62,70264,70182],{"class":23824},[62,70266,70185],{"class":72},[62,70268,70269,70271,70274,70276],{"class":64,"line":179},[62,70270,27875],{"class":23824},[62,70272,70273],{"class":72}," org.springframework.stereotype.",[62,70275,37504],{"class":23824},[62,70277,70278],{"class":72},"ervice\n",[62,70280,70281],{"class":64,"line":185},[62,70282,79],{"emptyLinePlaceholder":13},[62,70284,70285,70287,70289,70292,70294],{"class":64,"line":191},[62,70286,942],{"class":72},[62,70288,37504],{"class":23824},[62,70290,70291],{"class":72},"ervice('post",[62,70293,37504],{"class":23824},[62,70295,70296],{"class":72},"ervice')\n",[62,70298,70299,70301,70303,70305,70307,70310,70312,70315,70318,70320,70322,70324],{"class":64,"line":209},[62,70300,11671],{"class":23824},[62,70302,70005],{"class":23824},[62,70304,70198],{"class":72},[62,70306,37504],{"class":23824},[62,70308,70309],{"class":72},"ervice",[62,70311,54120],{"class":23824},[62,70313,70314],{"class":72},"mpl ",[62,70316,70317],{"class":23824},"implements",[62,70319,70005],{"class":23824},[62,70321,70198],{"class":72},[62,70323,37504],{"class":23824},[62,70325,70203],{"class":72},[62,70327,70328],{"class":64,"line":220},[62,70329,79],{"emptyLinePlaceholder":13},[62,70331,70332,70334,70336],{"class":64,"line":226},[62,70333,2143],{"class":72},[62,70335,70050],{"class":23824},[62,70337,70338],{"class":72},"verride\n",[62,70340,70341,70343,70345,70347,70349,70351],{"class":64,"line":231},[62,70342,70212],{"class":23824},[62,70344,70215],{"class":72},[62,70346,70218],{"class":23824},[62,70348,70221],{"class":72},[62,70350,70182],{"class":23824},[62,70352,70353],{"class":72},"ost> list() {\n",[62,70355,70356,70359],{"class":64,"line":236},[62,70357,70358],{"class":23824}," P",[62,70360,70361],{"class":72},"ost.list()\n",[62,70363,70364],{"class":64,"line":242},[62,70365,223],{"class":72},[62,70367,70368],{"class":64,"line":247},[62,70369,79],{"emptyLinePlaceholder":13},[62,70371,70372,70374,70376],{"class":64,"line":252},[62,70373,2143],{"class":72},[62,70375,70050],{"class":23824},[62,70377,70338],{"class":72},[62,70379,70380,70382,70385,70387],{"class":64,"line":257},[62,70381,70235],{"class":23824},[62,70383,70384],{"class":72},"ost read(",[62,70386,747],{"class":23824},[62,70388,70389],{"class":72}," id) {\n",[62,70391,70392,70394],{"class":64,"line":271},[62,70393,70358],{"class":23824},[62,70395,70396],{"class":72},"ost.get(id)\n",[62,70398,70399],{"class":64,"line":281},[62,70400,223],{"class":72},[62,70402,70403],{"class":64,"line":286},[62,70404,79],{"emptyLinePlaceholder":13},[62,70406,70407],{"class":64,"line":291},[62,70408,379],{"class":72},[22,70410,70411],{},"Again if you're not familiar with GORM there is some dynamic programming going on with our domain object that gives us methods like",[915,70413,70414,70416,70418,70420,70422,70425,70427],{},[37,70415,38739],{},[37,70417,11363],{},[37,70419,22562],{},[37,70421,41666],{},[37,70423,70424],{},"findWhere",[37,70426,8563],{},[37,70428,70429],{},"etc...",[22,70431,70432],{},"Finally we create a controller with some mappings that simply call our service to get the data.",[52,70434,70436],{"className":54,"code":70435,"language":56,"meta":57,"style":57},"package com.therealdanvega.controller\n\nimport com.therealdanvega.service.PostService\nimport com.therealdanvega.service.PostServiceImpl\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.web.bind.annotation.PathVariable\nimport org.springframework.web.bind.annotation.RequestMapping\nimport org.springframework.web.bind.annotation.RestController\n\n@RestController\n@RequestMapping(\"/posts\")\nclass PostController {\n\n PostService postService\n\n @Autowired\n PostController(PostService postService) {\n this.postService = postService\n }\n\n @RequestMapping(\"/\")\n public String list(){\n postService.list()\n }\n\n @RequestMapping(\"/{id}\")\n public String get(@PathVariable(value = \"id\") int id){\n postService.get(id)\n }\n\n}\n",[59,70437,70438,70445,70449,70464,70483,70496,70514,70532,70548,70552,70564,70577,70590,70594,70609,70613,70621,70645,70661,70665,70669,70682,70692,70702,70706,70710,70723,70746,70755,70759,70763],{"__ignoreMap":57},[62,70439,70440,70442],{"class":64,"line":65},[62,70441,69],{"class":68},[62,70443,70444],{"class":72}," com.therealdanvega.controller\n",[62,70446,70447],{"class":64,"line":76},[62,70448,79],{"emptyLinePlaceholder":13},[62,70450,70451,70453,70456,70458,70460,70462],{"class":64,"line":82},[62,70452,27875],{"class":23824},[62,70454,70455],{"class":72}," com.therealdanvega.service.",[62,70457,70182],{"class":23824},[62,70459,70198],{"class":72},[62,70461,37504],{"class":23824},[62,70463,70278],{"class":72},[62,70465,70466,70468,70470,70472,70474,70476,70478,70480],{"class":64,"line":89},[62,70467,27875],{"class":23824},[62,70469,70455],{"class":72},[62,70471,70182],{"class":23824},[62,70473,70198],{"class":72},[62,70475,37504],{"class":23824},[62,70477,70309],{"class":72},[62,70479,54120],{"class":23824},[62,70481,70482],{"class":72},"mpl\n",[62,70484,70485,70487,70490,70493],{"class":64,"line":95},[62,70486,27875],{"class":23824},[62,70488,70489],{"class":72}," org.springframework.beans.factory.annotation.",[62,70491,70492],{"class":23824},"A",[62,70494,70495],{"class":72},"utowired\n",[62,70497,70498,70500,70503,70505,70508,70511],{"class":64,"line":101},[62,70499,27875],{"class":23824},[62,70501,70502],{"class":72}," org.springframework.web.bind.annotation.",[62,70504,70182],{"class":23824},[62,70506,70507],{"class":72},"ath",[62,70509,70510],{"class":23824},"V",[62,70512,70513],{"class":72},"ariable\n",[62,70515,70516,70518,70520,70523,70526,70529],{"class":64,"line":107},[62,70517,27875],{"class":23824},[62,70519,70502],{"class":72},[62,70521,70522],{"class":23824},"R",[62,70524,70525],{"class":72},"equest",[62,70527,70528],{"class":23824},"M",[62,70530,70531],{"class":72},"apping\n",[62,70533,70534,70536,70538,70540,70543,70545],{"class":64,"line":113},[62,70535,27875],{"class":23824},[62,70537,70502],{"class":72},[62,70539,70522],{"class":23824},[62,70541,70542],{"class":72},"est",[62,70544,53828],{"class":23824},[62,70546,70547],{"class":72},"ontroller\n",[62,70549,70550],{"class":64,"line":129},[62,70551,79],{"emptyLinePlaceholder":13},[62,70553,70554,70556,70558,70560,70562],{"class":64,"line":134},[62,70555,942],{"class":72},[62,70557,70522],{"class":23824},[62,70559,70542],{"class":72},[62,70561,53828],{"class":23824},[62,70563,70547],{"class":72},[62,70565,70566,70568,70570,70572,70574],{"class":64,"line":156},[62,70567,942],{"class":72},[62,70569,70522],{"class":23824},[62,70571,70525],{"class":72},[62,70573,70528],{"class":23824},[62,70575,70576],{"class":72},"apping(\"/posts\")\n",[62,70578,70579,70581,70583,70585,70587],{"class":64,"line":161},[62,70580,11671],{"class":23824},[62,70582,70005],{"class":23824},[62,70584,70198],{"class":72},[62,70586,53828],{"class":23824},[62,70588,70589],{"class":72},"ontroller {\n",[62,70591,70592],{"class":64,"line":167},[62,70593,79],{"emptyLinePlaceholder":13},[62,70595,70596,70598,70600,70602,70605,70607],{"class":64,"line":173},[62,70597,70235],{"class":23824},[62,70599,70198],{"class":72},[62,70601,37504],{"class":23824},[62,70603,70604],{"class":72},"ervice post",[62,70606,37504],{"class":23824},[62,70608,70278],{"class":72},[62,70610,70611],{"class":64,"line":179},[62,70612,79],{"emptyLinePlaceholder":13},[62,70614,70615,70617,70619],{"class":64,"line":185},[62,70616,2143],{"class":72},[62,70618,70492],{"class":23824},[62,70620,70495],{"class":72},[62,70622,70623,70625,70627,70629,70632,70634,70636,70638,70640,70642],{"class":64,"line":191},[62,70624,70235],{"class":23824},[62,70626,70198],{"class":72},[62,70628,53828],{"class":23824},[62,70630,70631],{"class":72},"ontroller(",[62,70633,70182],{"class":23824},[62,70635,70198],{"class":72},[62,70637,37504],{"class":23824},[62,70639,70604],{"class":72},[62,70641,37504],{"class":23824},[62,70643,70644],{"class":72},"ervice) {\n",[62,70646,70647,70649,70652,70654,70657,70659],{"class":64,"line":209},[62,70648,2405],{"class":23824},[62,70650,70651],{"class":72},".post",[62,70653,37504],{"class":23824},[62,70655,70656],{"class":72},"ervice = post",[62,70658,37504],{"class":23824},[62,70660,70278],{"class":72},[62,70662,70663],{"class":64,"line":220},[62,70664,223],{"class":72},[62,70666,70667],{"class":64,"line":226},[62,70668,79],{"emptyLinePlaceholder":13},[62,70670,70671,70673,70675,70677,70679],{"class":64,"line":231},[62,70672,2143],{"class":72},[62,70674,70522],{"class":23824},[62,70676,70525],{"class":72},[62,70678,70528],{"class":23824},[62,70680,70681],{"class":72},"apping(\"/\")\n",[62,70683,70684,70686,70689],{"class":64,"line":236},[62,70685,194],{"class":23824},[62,70687,70688],{"class":23824}," S",[62,70690,70691],{"class":72},"tring list(){\n",[62,70693,70694,70697,70699],{"class":64,"line":242},[62,70695,70696],{"class":72}," post",[62,70698,37504],{"class":23824},[62,70700,70701],{"class":72},"ervice.list()\n",[62,70703,70704],{"class":64,"line":247},[62,70705,223],{"class":72},[62,70707,70708],{"class":64,"line":252},[62,70709,79],{"emptyLinePlaceholder":13},[62,70711,70712,70714,70716,70718,70720],{"class":64,"line":257},[62,70713,2143],{"class":72},[62,70715,70522],{"class":23824},[62,70717,70525],{"class":72},[62,70719,70528],{"class":23824},[62,70721,70722],{"class":72},"apping(\"/{id}\")\n",[62,70724,70725,70727,70729,70732,70734,70736,70738,70741,70743],{"class":64,"line":271},[62,70726,194],{"class":23824},[62,70728,70688],{"class":23824},[62,70730,70731],{"class":72},"tring get(@",[62,70733,70182],{"class":23824},[62,70735,70507],{"class":72},[62,70737,70510],{"class":23824},[62,70739,70740],{"class":72},"ariable(value = \"id\") ",[62,70742,747],{"class":23824},[62,70744,70745],{"class":72}," id){\n",[62,70747,70748,70750,70752],{"class":64,"line":281},[62,70749,70696],{"class":72},[62,70751,37504],{"class":23824},[62,70753,70754],{"class":72},"ervice.get(id)\n",[62,70756,70757],{"class":64,"line":286},[62,70758,223],{"class":72},[62,70760,70761],{"class":64,"line":291},[62,70762,79],{"emptyLinePlaceholder":13},[62,70764,70765],{"class":64,"line":296},[62,70766,379],{"class":72},[22,70768,70769],{},"This would appear like everything we need to run our application right? I want you to go ahead and run this application now without looking below. What happened? Do you know why it happened?",[636,70771,70773],{"id":70772},"the-hibernate-session","The Hibernate Session",[22,70775,70776],{},"If you ran the example we have been building you should of seen an error similar to this one.",[52,70778,70780],{"className":32625,"code":70779,"language":32627,"meta":57,"style":57},"org.hibernate.HibernateException: No Session found for current thread\n at org.grails.orm.hibernate.GrailsSessionContext.currentSession(GrailsSessionContext.java:117) ~\\[grails-datastore-gorm-hibernate4-5.0.0.RC1.jar:na\\]\n at org.hibernate.internal.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:1014) ~\\[hibernate-core-4.3.11.Final.jar:4.3.11.Final\\]\n at org.grails.orm.hibernate.GrailsHibernateTemplate.getSession(GrailsHibernateTemplate.java:225) ~\\[grails-datastore-gorm-hibernate4-5.0.0.RC1.jar:na\\]\n at org.grails.orm.hibernate.GrailsHibernateTemplate.doExecute(GrailsHibernateTemplate.java:183) ~\\[grails-datastore-gorm-hibernate4-5.0.0.RC1.jar:na\\]\n at org.grails.orm.hibernate.GrailsHibernateTemplate.execute(GrailsHibernateTemplate.java:140) ~\\[grails-datastore-gorm-hibernate4-5.0.0.RC1.jar:na\\]\n at org.grails.orm.hibernate.GrailsHibernateTemplate.execute(GrailsHibernateTemplate.java:110) ~\\[grails-datastore-gorm-hibernate4-5.0.0.RC1.jar:na\\]\n at org.grails.orm.hibernate.HibernateGormStaticApi.list(HibernateGormStaticApi.groovy:76) ~\\[grails-datastore-gorm-hibernate4-5.0.0.RC1.jar:na\\]\n at org.grails.orm.hibernate.HibernateGormStaticApi.list(HibernateGormStaticApi.groovy:75) ~\\[grails-datastore-gorm-hibernate4-5.0.0.RC1.jar:na\\]\n at org.grails.datastore.gorm.GormEntity$Trait$Helper.list(GormEntity.groovy:639) ~\\[grails-datastore-gorm-5.0.0.RC1.jar:na\\]\n at org.grails.datastore.gorm.GormEntity$Trait$Helper$list.call(Unknown Source) ~\\[na:na\\]\n at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:48) ~\\[groovy-2.4.4.jar:2.4.4\\]\n at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:113) ~\\[groovy-2.4.4.jar:2.4.4\\]\n at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:125) ~\\[groovy-2.4.4.jar:2.4.4\\]\n at com.therealdanvega.domain.Post.list(Post.groovy) ~\\[main/:na\\]\n\n...\n",[59,70781,70782,70804,70830,70853,70875,70897,70919,70940,70962,70983,71012,71043,71066,71088,71109,71132,71136],{"__ignoreMap":57},[62,70783,70784,70787,70790,70793,70796,70798,70801],{"class":64,"line":65},[62,70785,70786],{"class":122},"org.hibernate.HibernateException:",[62,70788,70789],{"class":1675}," No",[62,70791,70792],{"class":1675}," Session",[62,70794,70795],{"class":1675}," found",[62,70797,53629],{"class":1675},[62,70799,70800],{"class":1675}," current",[62,70802,70803],{"class":1675}," thread\n",[62,70805,70806,70809,70812,70814,70817,70819,70822,70825,70828],{"class":64,"line":76},[62,70807,70808],{"class":122}," at",[62,70810,70811],{"class":1675}," org.grails.orm.hibernate.GrailsSessionContext.currentSession",[62,70813,2109],{"class":72},[62,70815,70816],{"class":122},"GrailsSessionContext.java:117",[62,70818,5024],{"class":72},[62,70820,70821],{"class":1675},"~",[62,70823,70824],{"class":149},"\\[",[62,70826,70827],{"class":1675},"grails-datastore-gorm-hibernate4-5.0.0.RC1.jar:na",[62,70829,50699],{"class":149},[62,70831,70832,70834,70837,70839,70842,70844,70846,70848,70851],{"class":64,"line":82},[62,70833,70808],{"class":122},[62,70835,70836],{"class":1675}," org.hibernate.internal.SessionFactoryImpl.getCurrentSession",[62,70838,2109],{"class":72},[62,70840,70841],{"class":122},"SessionFactoryImpl.java:1014",[62,70843,5024],{"class":72},[62,70845,70821],{"class":1675},[62,70847,70824],{"class":149},[62,70849,70850],{"class":1675},"hibernate-core-4.3.11.Final.jar:4.3.11.Final",[62,70852,50699],{"class":149},[62,70854,70855,70857,70860,70862,70865,70867,70869,70871,70873],{"class":64,"line":89},[62,70856,70808],{"class":122},[62,70858,70859],{"class":1675}," org.grails.orm.hibernate.GrailsHibernateTemplate.getSession",[62,70861,2109],{"class":72},[62,70863,70864],{"class":122},"GrailsHibernateTemplate.java:225",[62,70866,5024],{"class":72},[62,70868,70821],{"class":1675},[62,70870,70824],{"class":149},[62,70872,70827],{"class":1675},[62,70874,50699],{"class":149},[62,70876,70877,70879,70882,70884,70887,70889,70891,70893,70895],{"class":64,"line":95},[62,70878,70808],{"class":122},[62,70880,70881],{"class":1675}," org.grails.orm.hibernate.GrailsHibernateTemplate.doExecute",[62,70883,2109],{"class":72},[62,70885,70886],{"class":122},"GrailsHibernateTemplate.java:183",[62,70888,5024],{"class":72},[62,70890,70821],{"class":1675},[62,70892,70824],{"class":149},[62,70894,70827],{"class":1675},[62,70896,50699],{"class":149},[62,70898,70899,70901,70904,70906,70909,70911,70913,70915,70917],{"class":64,"line":101},[62,70900,70808],{"class":122},[62,70902,70903],{"class":1675}," org.grails.orm.hibernate.GrailsHibernateTemplate.execute",[62,70905,2109],{"class":72},[62,70907,70908],{"class":122},"GrailsHibernateTemplate.java:140",[62,70910,5024],{"class":72},[62,70912,70821],{"class":1675},[62,70914,70824],{"class":149},[62,70916,70827],{"class":1675},[62,70918,50699],{"class":149},[62,70920,70921,70923,70925,70927,70930,70932,70934,70936,70938],{"class":64,"line":107},[62,70922,70808],{"class":122},[62,70924,70903],{"class":1675},[62,70926,2109],{"class":72},[62,70928,70929],{"class":122},"GrailsHibernateTemplate.java:110",[62,70931,5024],{"class":72},[62,70933,70821],{"class":1675},[62,70935,70824],{"class":149},[62,70937,70827],{"class":1675},[62,70939,50699],{"class":149},[62,70941,70942,70944,70947,70949,70952,70954,70956,70958,70960],{"class":64,"line":113},[62,70943,70808],{"class":122},[62,70945,70946],{"class":1675}," org.grails.orm.hibernate.HibernateGormStaticApi.list",[62,70948,2109],{"class":72},[62,70950,70951],{"class":122},"HibernateGormStaticApi.groovy:76",[62,70953,5024],{"class":72},[62,70955,70821],{"class":1675},[62,70957,70824],{"class":149},[62,70959,70827],{"class":1675},[62,70961,50699],{"class":149},[62,70963,70964,70966,70968,70970,70973,70975,70977,70979,70981],{"class":64,"line":129},[62,70965,70808],{"class":122},[62,70967,70946],{"class":1675},[62,70969,2109],{"class":72},[62,70971,70972],{"class":122},"HibernateGormStaticApi.groovy:75",[62,70974,5024],{"class":72},[62,70976,70821],{"class":1675},[62,70978,70824],{"class":149},[62,70980,70827],{"class":1675},[62,70982,50699],{"class":149},[62,70984,70985,70987,70990,70993,70996,70998,71001,71003,71005,71007,71010],{"class":64,"line":134},[62,70986,70808],{"class":122},[62,70988,70989],{"class":1675}," org.grails.datastore.gorm.GormEntity",[62,70991,70992],{"class":72},"$Trait$Helper",[62,70994,70995],{"class":1675},".list",[62,70997,2109],{"class":72},[62,70999,71000],{"class":122},"GormEntity.groovy:639",[62,71002,5024],{"class":72},[62,71004,70821],{"class":1675},[62,71006,70824],{"class":149},[62,71008,71009],{"class":1675},"grails-datastore-gorm-5.0.0.RC1.jar:na",[62,71011,50699],{"class":149},[62,71013,71014,71016,71018,71021,71024,71026,71029,71032,71034,71036,71038,71041],{"class":64,"line":156},[62,71015,70808],{"class":122},[62,71017,70989],{"class":1675},[62,71019,71020],{"class":72},"$Trait$Helper$list",[62,71022,71023],{"class":1675},".call",[62,71025,2109],{"class":72},[62,71027,71028],{"class":122},"Unknown",[62,71030,71031],{"class":1675}," Source",[62,71033,5024],{"class":72},[62,71035,70821],{"class":1675},[62,71037,70824],{"class":149},[62,71039,71040],{"class":1675},"na:na",[62,71042,50699],{"class":149},[62,71044,71045,71047,71050,71052,71055,71057,71059,71061,71064],{"class":64,"line":161},[62,71046,70808],{"class":122},[62,71048,71049],{"class":1675}," org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall",[62,71051,2109],{"class":72},[62,71053,71054],{"class":122},"CallSiteArray.java:48",[62,71056,5024],{"class":72},[62,71058,70821],{"class":1675},[62,71060,70824],{"class":149},[62,71062,71063],{"class":1675},"groovy-2.4.4.jar:2.4.4",[62,71065,50699],{"class":149},[62,71067,71068,71070,71073,71075,71078,71080,71082,71084,71086],{"class":64,"line":167},[62,71069,70808],{"class":122},[62,71071,71072],{"class":1675}," org.codehaus.groovy.runtime.callsite.AbstractCallSite.call",[62,71074,2109],{"class":72},[62,71076,71077],{"class":122},"AbstractCallSite.java:113",[62,71079,5024],{"class":72},[62,71081,70821],{"class":1675},[62,71083,70824],{"class":149},[62,71085,71063],{"class":1675},[62,71087,50699],{"class":149},[62,71089,71090,71092,71094,71096,71099,71101,71103,71105,71107],{"class":64,"line":173},[62,71091,70808],{"class":122},[62,71093,71072],{"class":1675},[62,71095,2109],{"class":72},[62,71097,71098],{"class":122},"AbstractCallSite.java:125",[62,71100,5024],{"class":72},[62,71102,70821],{"class":1675},[62,71104,70824],{"class":149},[62,71106,71063],{"class":1675},[62,71108,50699],{"class":149},[62,71110,71111,71113,71116,71118,71121,71123,71125,71127,71130],{"class":64,"line":179},[62,71112,70808],{"class":122},[62,71114,71115],{"class":1675}," com.therealdanvega.domain.Post.list",[62,71117,2109],{"class":72},[62,71119,71120],{"class":122},"Post.groovy",[62,71122,5024],{"class":72},[62,71124,70821],{"class":1675},[62,71126,70824],{"class":149},[62,71128,71129],{"class":1675},"main/:na",[62,71131,50699],{"class":149},[62,71133,71134],{"class":64,"line":185},[62,71135,79],{"emptyLinePlaceholder":13},[62,71137,71138],{"class":64,"line":191},[62,71139,71140],{"class":149},"...\n",[22,71142,71143],{},"The main takeaway from that error message is this",[22,71145,71146],{},"No Session found for current thread",[22,71148,71149],{},"This is because we currently don't have a Hibernate session to work with. In Grails we could of run Post.list() directly from our controller and it would of worked. So why does it work there an not here? I mean at the end of the day Grails 3 is a Spring Boot app, what funny business is going on behind the scenes? This is a good explanation by Burt Beckwith...",[29685,71151,71152],{},[22,71153,71154],{},"Grails registers a customized subclass of OpenSessionInViewInterceptor (it adds WebFlow awareness). So anything done in the context of a web request will have an open session and lazy-loaded references and collections will resolve.",[22,71156,71157],{},"This means that our request would have an open session and that is what gives us the ability to call our list method. We can do something similar but lets talk through our 2 options here.",[28831,71159,71161],{"id":71160},"transactional","@Transactional",[22,71163,71164],{},"We could manually write the logic in every single method to start a transaction, open a hibernate session, perform our business logic and then close the session and commit the transaction but that would be a huge pain in the butt. The @Transactional annotation gives us a shortcut of doing all that by using AOP. The first thing we need to do to is to enable Spring's annotation-driven transaction management capability by adding the following annotation to our main application class.",[22,71166,71167],{},"@EnableTransactionManagement",[22,71169,55057,71170,71172,71173,71179,71180,71182],{},[59,71171,71161],{}," annotation is metadata that specifies that an interface, class, or method must have transactional semantics; for example, \"",[62,71174,71176],{"style":71175},"emphasis",[4534,71177,71178],{},"start a brand new read-only transaction when this method is invoked, suspending any existing transaction","\". The default ",[59,71181,71161],{}," settings are as follows:",[915,71184,71185,71191,71197,71200,71203],{},[37,71186,71187,71188],{},"Propagation setting is ",[59,71189,71190],{},"PROPAGATION_REQUIRED.",[37,71192,71193,71194],{},"Isolation level is ",[59,71195,71196],{},"ISOLATION_DEFAULT.",[37,71198,71199],{},"Transaction is read/write.",[37,71201,71202],{},"Transaction timeout defaults to the default timeout of the underlying transaction system, or to none if timeouts are not supported.",[37,71204,71205,71206,71209,71210,71213],{},"Any ",[59,71207,71208],{},"RuntimeException"," triggers rollback, and any checked ",[59,71211,71212],{},"Exception"," does not.",[22,71215,71216,71217,71222],{},"Those settings can be changed using attributes in the annotation. To learn more about those settings ",[677,71218,71221],{"href":71219,"rel":71220},"http://docs.spring.io/spring/docs/current/spring-framework-reference/html/transaction.html#transaction-declarative-annotations",[681],"please read the documentation",". With that little knowledge we can go and apply the annotation to our service class.",[52,71224,71226],{"className":54,"code":71225,"language":56,"meta":57,"style":57},"package com.therealdanvega.service\n\nimport com.therealdanvega.domain.Post\nimport org.springframework.stereotype.Service\nimport org.springframework.transaction.annotation.Transactional\n\n\n@Service('postService')\n@Transactional\nclass PostServiceImpl implements PostService {\n\n @Override\n ArrayList\u003CPost> list() {\n Post.list()\n }\n\n @Override\n Post read(int id) {\n Post.get(id)\n }\n\n}\n",[59,71227,71228,71234,71238,71248,71258,71271,71275,71279,71291,71299,71325,71329,71337,71351,71357,71361,71365,71373,71383,71389,71393,71397],{"__ignoreMap":57},[62,71229,71230,71232],{"class":64,"line":65},[62,71231,69],{"class":68},[62,71233,70168],{"class":72},[62,71235,71236],{"class":64,"line":76},[62,71237,79],{"emptyLinePlaceholder":13},[62,71239,71240,71242,71244,71246],{"class":64,"line":82},[62,71241,27875],{"class":23824},[62,71243,70179],{"class":72},[62,71245,70182],{"class":23824},[62,71247,70185],{"class":72},[62,71249,71250,71252,71254,71256],{"class":64,"line":89},[62,71251,27875],{"class":23824},[62,71253,70273],{"class":72},[62,71255,37504],{"class":23824},[62,71257,70278],{"class":72},[62,71259,71260,71262,71265,71268],{"class":64,"line":95},[62,71261,27875],{"class":23824},[62,71263,71264],{"class":72}," org.springframework.transaction.annotation.",[62,71266,71267],{"class":23824},"T",[62,71269,71270],{"class":72},"ransactional\n",[62,71272,71273],{"class":64,"line":101},[62,71274,79],{"emptyLinePlaceholder":13},[62,71276,71277],{"class":64,"line":107},[62,71278,79],{"emptyLinePlaceholder":13},[62,71280,71281,71283,71285,71287,71289],{"class":64,"line":113},[62,71282,942],{"class":72},[62,71284,37504],{"class":23824},[62,71286,70291],{"class":72},[62,71288,37504],{"class":23824},[62,71290,70296],{"class":72},[62,71292,71293,71295,71297],{"class":64,"line":129},[62,71294,942],{"class":72},[62,71296,71267],{"class":23824},[62,71298,71270],{"class":72},[62,71300,71301,71303,71305,71307,71309,71311,71313,71315,71317,71319,71321,71323],{"class":64,"line":134},[62,71302,11671],{"class":23824},[62,71304,70005],{"class":23824},[62,71306,70198],{"class":72},[62,71308,37504],{"class":23824},[62,71310,70309],{"class":72},[62,71312,54120],{"class":23824},[62,71314,70314],{"class":72},[62,71316,70317],{"class":23824},[62,71318,70005],{"class":23824},[62,71320,70198],{"class":72},[62,71322,37504],{"class":23824},[62,71324,70203],{"class":72},[62,71326,71327],{"class":64,"line":156},[62,71328,79],{"emptyLinePlaceholder":13},[62,71330,71331,71333,71335],{"class":64,"line":161},[62,71332,2143],{"class":72},[62,71334,70050],{"class":23824},[62,71336,70338],{"class":72},[62,71338,71339,71341,71343,71345,71347,71349],{"class":64,"line":167},[62,71340,70212],{"class":23824},[62,71342,70215],{"class":72},[62,71344,70218],{"class":23824},[62,71346,70221],{"class":72},[62,71348,70182],{"class":23824},[62,71350,70353],{"class":72},[62,71352,71353,71355],{"class":64,"line":173},[62,71354,70358],{"class":23824},[62,71356,70361],{"class":72},[62,71358,71359],{"class":64,"line":179},[62,71360,223],{"class":72},[62,71362,71363],{"class":64,"line":185},[62,71364,79],{"emptyLinePlaceholder":13},[62,71366,71367,71369,71371],{"class":64,"line":191},[62,71368,2143],{"class":72},[62,71370,70050],{"class":23824},[62,71372,70338],{"class":72},[62,71374,71375,71377,71379,71381],{"class":64,"line":209},[62,71376,70235],{"class":23824},[62,71378,70384],{"class":72},[62,71380,747],{"class":23824},[62,71382,70389],{"class":72},[62,71384,71385,71387],{"class":64,"line":220},[62,71386,70358],{"class":23824},[62,71388,70396],{"class":72},[62,71390,71391],{"class":64,"line":226},[62,71392,223],{"class":72},[62,71394,71395],{"class":64,"line":231},[62,71396,79],{"emptyLinePlaceholder":13},[62,71398,71399],{"class":64,"line":236},[62,71400,379],{"class":72},[22,71402,71403],{},"If you run the example now you should be able to visit the /posts mapping without error.",[28831,71405,71407],{"id":71406},"open-session-in-view","Open Session In View",[22,71409,71410,71411,71416,71417,71420,71421,71424],{},"Another option is to use the Open Session in View pattern. In Grails there is a subclass of the ",[677,71412,71415],{"href":71413,"rel":71414},"http://docs.spring.io/autorepo/docs/spring/current/javadoc-api/org/springframework/orm/hibernate3/support/OpenSessionInViewInterceptor.html",[681],"OpenSessionInViewInterceptor from Spring"," configured for us and that is why we don't have to worry about it.This is a Spring web request interceptor that binds a Hibernate ",[59,71418,71419],{},"Session"," to the thread for the entire processing of the request. The important part is that the session is available to us ",[646,71422,71423],{},"In View."," If you aren't using a view technology than this doesn't make a lot of sense and some would say even if you are this isn't the best answer.",[22,71426,71427],{},"Take a post object that has a relationship to author. The author on the object is lazy loaded. This means that if we wrapped transactional to get our post and then tried to call post.getAuthor() in the view there would no longer be a session available to load that author object. This is where the need for it comes into play.",[22,71429,71430],{},"Another approach is you could add a new attribute to the model called author in your controller method and set it there eliminating the need for that call in the view. This is for you to decide and do a little reading up on. I am just going to present you with a solution if you decide to go that route. Create a new package called config and create a new class called WebMvcConfig.",[52,71432,71434],{"className":54,"code":71433,"language":56,"meta":57,"style":57},"package com.therealdanvega.config\n\nimport org.hibernate.SessionFactory\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.orm.hibernate4.support.OpenSessionInViewInterceptor\nimport org.springframework.web.servlet.config.annotation.InterceptorRegistry\nimport org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter\n\n@Configuration\nclass WebMvcConfig extends WebMvcConfigurerAdapter {\n\n @Autowired\n SessionFactory sessionFactory\n\n @Override\n void addInterceptors(InterceptorRegistry registry) {\n OpenSessionInViewInterceptor openSessionInViewInterceptor = new OpenSessionInViewInterceptor()\n openSessionInViewInterceptor.setSessionFactory(sessionFactory)\n registry.addWebRequestInterceptor( openSessionInViewInterceptor )\n }\n\n}\n",[59,71435,71436,71443,71447,71465,71475,71487,71518,71535,71562,71566,71574,71612,71616,71624,71639,71643,71651,71671,71736,71772,71807,71811,71815],{"__ignoreMap":57},[62,71437,71438,71440],{"class":64,"line":65},[62,71439,69],{"class":68},[62,71441,71442],{"class":72}," com.therealdanvega.config\n",[62,71444,71445],{"class":64,"line":76},[62,71446,79],{"emptyLinePlaceholder":13},[62,71448,71449,71451,71454,71456,71459,71462],{"class":64,"line":82},[62,71450,27875],{"class":23824},[62,71452,71453],{"class":72}," org.hibernate.",[62,71455,37504],{"class":23824},[62,71457,71458],{"class":72},"ession",[62,71460,71461],{"class":23824},"F",[62,71463,71464],{"class":72},"actory\n",[62,71466,71467,71469,71471,71473],{"class":64,"line":89},[62,71468,27875],{"class":23824},[62,71470,70489],{"class":72},[62,71472,70492],{"class":23824},[62,71474,70495],{"class":72},[62,71476,71477,71479,71482,71484],{"class":64,"line":95},[62,71478,27875],{"class":23824},[62,71480,71481],{"class":72}," org.springframework.context.annotation.",[62,71483,53828],{"class":23824},[62,71485,71486],{"class":72},"onfiguration\n",[62,71488,71489,71491,71494,71496,71499,71501,71503,71505,71508,71510,71513,71515],{"class":64,"line":101},[62,71490,27875],{"class":23824},[62,71492,71493],{"class":72}," org.springframework.orm.hibernate4.support.",[62,71495,70050],{"class":23824},[62,71497,71498],{"class":72},"pen",[62,71500,37504],{"class":23824},[62,71502,71458],{"class":72},[62,71504,54120],{"class":23824},[62,71506,71507],{"class":72},"n",[62,71509,70510],{"class":23824},[62,71511,71512],{"class":72},"iew",[62,71514,54120],{"class":23824},[62,71516,71517],{"class":72},"nterceptor\n",[62,71519,71520,71522,71525,71527,71530,71532],{"class":64,"line":107},[62,71521,27875],{"class":23824},[62,71523,71524],{"class":72}," org.springframework.web.servlet.config.annotation.",[62,71526,54120],{"class":23824},[62,71528,71529],{"class":72},"nterceptor",[62,71531,70522],{"class":23824},[62,71533,71534],{"class":72},"egistry\n",[62,71536,71537,71539,71541,71544,71547,71549,71552,71554,71557,71559],{"class":64,"line":113},[62,71538,27875],{"class":23824},[62,71540,71524],{"class":72},[62,71542,71543],{"class":23824},"W",[62,71545,71546],{"class":72},"eb",[62,71548,70528],{"class":23824},[62,71550,71551],{"class":72},"vc",[62,71553,53828],{"class":23824},[62,71555,71556],{"class":72},"onfigurer",[62,71558,70492],{"class":23824},[62,71560,71561],{"class":72},"dapter\n",[62,71563,71564],{"class":64,"line":129},[62,71565,79],{"emptyLinePlaceholder":13},[62,71567,71568,71570,71572],{"class":64,"line":134},[62,71569,942],{"class":72},[62,71571,53828],{"class":23824},[62,71573,71486],{"class":72},[62,71575,71576,71578,71581,71583,71585,71587,71589,71592,71595,71597,71599,71601,71603,71605,71607,71609],{"class":64,"line":156},[62,71577,11671],{"class":23824},[62,71579,71580],{"class":23824}," W",[62,71582,71546],{"class":72},[62,71584,70528],{"class":23824},[62,71586,71551],{"class":72},[62,71588,53828],{"class":23824},[62,71590,71591],{"class":72},"onfig ",[62,71593,71594],{"class":23824},"extends",[62,71596,71580],{"class":23824},[62,71598,71546],{"class":72},[62,71600,70528],{"class":23824},[62,71602,71551],{"class":72},[62,71604,53828],{"class":23824},[62,71606,71556],{"class":72},[62,71608,70492],{"class":23824},[62,71610,71611],{"class":72},"dapter {\n",[62,71613,71614],{"class":64,"line":161},[62,71615,79],{"emptyLinePlaceholder":13},[62,71617,71618,71620,71622],{"class":64,"line":167},[62,71619,2143],{"class":72},[62,71621,70492],{"class":23824},[62,71623,70495],{"class":72},[62,71625,71626,71628,71630,71632,71635,71637],{"class":64,"line":173},[62,71627,53871],{"class":23824},[62,71629,71458],{"class":72},[62,71631,71461],{"class":23824},[62,71633,71634],{"class":72},"actory session",[62,71636,71461],{"class":23824},[62,71638,71464],{"class":72},[62,71640,71641],{"class":64,"line":179},[62,71642,79],{"emptyLinePlaceholder":13},[62,71644,71645,71647,71649],{"class":64,"line":185},[62,71646,2143],{"class":72},[62,71648,70050],{"class":23824},[62,71650,70338],{"class":72},[62,71652,71653,71655,71657,71659,71662,71664,71666,71668],{"class":64,"line":191},[62,71654,11710],{"class":23824},[62,71656,28863],{"class":72},[62,71658,54120],{"class":23824},[62,71660,71661],{"class":72},"nterceptors(",[62,71663,54120],{"class":23824},[62,71665,71529],{"class":72},[62,71667,70522],{"class":23824},[62,71669,71670],{"class":72},"egistry registry) {\n",[62,71672,71673,71676,71678,71680,71682,71684,71686,71688,71690,71692,71695,71697,71699,71701,71703,71705,71707,71709,71712,71714,71717,71719,71721,71723,71725,71727,71729,71731,71733],{"class":64,"line":209},[62,71674,71675],{"class":23824}," O",[62,71677,71498],{"class":72},[62,71679,37504],{"class":23824},[62,71681,71458],{"class":72},[62,71683,54120],{"class":23824},[62,71685,71507],{"class":72},[62,71687,70510],{"class":23824},[62,71689,71512],{"class":72},[62,71691,54120],{"class":23824},[62,71693,71694],{"class":72},"nterceptor open",[62,71696,37504],{"class":23824},[62,71698,71458],{"class":72},[62,71700,54120],{"class":23824},[62,71702,71507],{"class":72},[62,71704,70510],{"class":23824},[62,71706,71512],{"class":72},[62,71708,54120],{"class":23824},[62,71710,71711],{"class":72},"nterceptor = ",[62,71713,2426],{"class":23824},[62,71715,71716],{"class":23824}," O",[62,71718,71498],{"class":72},[62,71720,37504],{"class":23824},[62,71722,71458],{"class":72},[62,71724,54120],{"class":23824},[62,71726,71507],{"class":72},[62,71728,70510],{"class":23824},[62,71730,71512],{"class":72},[62,71732,54120],{"class":23824},[62,71734,71735],{"class":72},"nterceptor()\n",[62,71737,71738,71741,71743,71745,71747,71749,71751,71753,71755,71758,71760,71762,71764,71767,71769],{"class":64,"line":220},[62,71739,71740],{"class":72}," open",[62,71742,37504],{"class":23824},[62,71744,71458],{"class":72},[62,71746,54120],{"class":23824},[62,71748,71507],{"class":72},[62,71750,70510],{"class":23824},[62,71752,71512],{"class":72},[62,71754,54120],{"class":23824},[62,71756,71757],{"class":72},"nterceptor.set",[62,71759,37504],{"class":23824},[62,71761,71458],{"class":72},[62,71763,71461],{"class":23824},[62,71765,71766],{"class":72},"actory(session",[62,71768,71461],{"class":23824},[62,71770,71771],{"class":72},"actory)\n",[62,71773,71774,71777,71779,71781,71783,71785,71787,71790,71792,71794,71796,71798,71800,71802,71804],{"class":64,"line":226},[62,71775,71776],{"class":72}," registry.add",[62,71778,71543],{"class":23824},[62,71780,71546],{"class":72},[62,71782,70522],{"class":23824},[62,71784,70525],{"class":72},[62,71786,54120],{"class":23824},[62,71788,71789],{"class":72},"nterceptor( open",[62,71791,37504],{"class":23824},[62,71793,71458],{"class":72},[62,71795,54120],{"class":23824},[62,71797,71507],{"class":72},[62,71799,70510],{"class":23824},[62,71801,71512],{"class":72},[62,71803,54120],{"class":23824},[62,71805,71806],{"class":72},"nterceptor )\n",[62,71808,71809],{"class":64,"line":231},[62,71810,223],{"class":72},[62,71812,71813],{"class":64,"line":236},[62,71814,79],{"emptyLinePlaceholder":13},[62,71816,71817],{"class":64,"line":242},[62,71818,379],{"class":72},[22,71820,71821],{},"We are creating a new OpenSessionInViewInterceptor, setting the session factory and adding that as a web request interceptor. You could at this point remove the transactions and our program would again run correctly. Just a quick not though that this doesn't eliminate the need for transactions. The next problem you are going to come across is serializing / deserializing objects. We will leave that discussion for another day.",[636,71823,1499],{"id":1498},[22,71825,71826,71827],{},"At this point I don't want to start a discussion on GORM vs Spring Data. To me they are both awesome and I think the more I use them both in Spring Boot the better equipped I will be to answer those questions. In the meantime I just wanted to show you that you can use GORM in Spring Boot and 1 of the main issues I came across setting it up. ",[677,71828,71831],{"href":71829,"rel":71830},"https://github.com/danvega/gorm-spring-boot-demo",[681],"You can grab the source code for this demo here.",[1527,71833,71834],{},"html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .shOWo, html code.shiki .shOWo{--shiki-default:#B31D28;--shiki-default-font-style:italic;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic;--shiki-sepia:#B31D28;--shiki-sepia-font-style:italic}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}",{"title":57,"searchDepth":76,"depth":76,"links":71836},[71837,71838,71839,71840,71841,71842],{"id":69914,"depth":82,"text":69915},{"id":69956,"depth":82,"text":69957},{"id":2290,"depth":82,"text":70108},{"id":70152,"depth":82,"text":70153},{"id":70772,"depth":82,"text":70773},{"id":1498,"depth":82,"text":1499},{"_id":71844,"path":71845,"title":71846,"description":71846,"meta":71847,"body":71852},"content/blog/2015/10/23/spring-boot-application-annotation.md","/blog/2015/10/23/spring-boot-application-annotation","Spring Boot Application Annotation",{"slug":71848,"date":71849,"published":13,"tags":71850,"author":-1,"cover":71851,"excerpt":-1},"spring-boot-application-annotation","2015-10-23T09:08:37-04:00",[56,11002],"./access-code-connection-1181467.jpg",{"type":19,"value":71853,"toc":72842},[71854,71857,71946,71949,71971,71974,72103,72106,72839],[22,71855,71856],{},"I have been working a lot with Spring Boot lately so you may see a flood of posts from me on the subject (you have been warned). If you are looking at older example and create new projects you may have come across the Spring Boot Application Annotation. If you create a simple project your main class might look something like this.",[52,71858,71860],{"className":54,"code":71859,"language":56,"meta":57,"style":57},"package com.therealdanvega;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class HelloSpringApplication {\n\n public static void main(String\\[\\] args) {\n SpringApplication.run(HelloSpringApplication.class, args);\n }\n}\n",[59,71861,71862,71868,71872,71878,71884,71888,71894,71905,71909,71929,71938,71942],{"__ignoreMap":57},[62,71863,71864,71866],{"class":64,"line":65},[62,71865,69],{"class":68},[62,71867,52481],{"class":72},[62,71869,71870],{"class":64,"line":76},[62,71871,79],{"emptyLinePlaceholder":13},[62,71873,71874,71876],{"class":64,"line":82},[62,71875,27875],{"class":68},[62,71877,52513],{"class":72},[62,71879,71880,71882],{"class":64,"line":89},[62,71881,27875],{"class":68},[62,71883,52520],{"class":72},[62,71885,71886],{"class":64,"line":95},[62,71887,79],{"emptyLinePlaceholder":13},[62,71889,71890,71892],{"class":64,"line":101},[62,71891,942],{"class":72},[62,71893,2079],{"class":68},[62,71895,71896,71898,71900,71903],{"class":64,"line":107},[62,71897,116],{"class":68},[62,71899,119],{"class":68},[62,71901,71902],{"class":122}," HelloSpringApplication",[62,71904,126],{"class":72},[62,71906,71907],{"class":64,"line":113},[62,71908,79],{"emptyLinePlaceholder":13},[62,71910,71911,71913,71915,71917,71919,71921,71923,71925,71927],{"class":64,"line":129},[62,71912,194],{"class":68},[62,71914,2101],{"class":68},[62,71916,200],{"class":68},[62,71918,2106],{"class":122},[62,71920,2109],{"class":72},[62,71922,973],{"class":889},[62,71924,52569],{"class":72},[62,71926,2117],{"class":889},[62,71928,768],{"class":72},[62,71930,71931,71933,71935],{"class":64,"line":134},[62,71932,2124],{"class":72},[62,71934,2127],{"class":122},[62,71936,71937],{"class":72},"(HelloSpringApplication.class, args);\n",[62,71939,71940],{"class":64,"line":156},[62,71941,223],{"class":72},[62,71943,71944],{"class":64,"line":161},[62,71945,379],{"class":72},[22,71947,71948],{},"The Spring Boot Application Annotation is a convenience annotation that adds all of the following:",[915,71950,71951,71956,71962],{},[37,71952,71953,71955],{},[59,71954,48869],{}," tags the class as a source of bean definitions for the application context.",[37,71957,71958,71961],{},[59,71959,71960],{},"@EnableAutoConfiguration"," tells Spring Boot to start adding beans based on class path settings, other beans, and various property settings.",[37,71963,71964,71967,71968,48328],{},[59,71965,71966],{},"@ComponentScan"," tells Spring to look for other components, configurations, and services in the the ",[59,71969,71970],{},"com.therealdanvega",[22,71972,71973],{},"If those 3 annotations look familiar to you its because the new annotation is the same as using those 3. It became so common to have to use those 3 on the main application class that they created the new one. It is perfectly fine to put the other 3 back especially if you get into a situation where you need to pass arguments to one of the annotations. In the following example I moved the 3 annotations back and customized the component scan to search 2 different packages instead of just the current one.",[52,71975,71977],{"className":54,"code":71976,"language":56,"meta":57,"style":57},"package com.therealdanvega;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.EnableAutoConfiguration;\nimport org.springframework.context.annotation.ComponentScan;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration\n@EnableAutoConfiguration\n@ComponentScan({\"com.therealdanvega\",\"com.foo.bar\"})\npublic class HelloSpringApplication {\n\n public static void main(String\\[\\] args) {\n SpringApplication.run(HelloSpringApplication.class, args);\n }\n}\n",[59,71978,71979,71985,71989,71995,72002,72009,72016,72020,72026,72033,72053,72063,72067,72087,72095,72099],{"__ignoreMap":57},[62,71980,71981,71983],{"class":64,"line":65},[62,71982,69],{"class":68},[62,71984,52481],{"class":72},[62,71986,71987],{"class":64,"line":76},[62,71988,79],{"emptyLinePlaceholder":13},[62,71990,71991,71993],{"class":64,"line":82},[62,71992,27875],{"class":68},[62,71994,52513],{"class":72},[62,71996,71997,71999],{"class":64,"line":89},[62,71998,27875],{"class":68},[62,72000,72001],{"class":72}," org.springframework.boot.autoconfigure.EnableAutoConfiguration;\n",[62,72003,72004,72006],{"class":64,"line":95},[62,72005,27875],{"class":68},[62,72007,72008],{"class":72}," org.springframework.context.annotation.ComponentScan;\n",[62,72010,72011,72013],{"class":64,"line":101},[62,72012,27875],{"class":68},[62,72014,72015],{"class":72}," org.springframework.context.annotation.Configuration;\n",[62,72017,72018],{"class":64,"line":107},[62,72019,79],{"emptyLinePlaceholder":13},[62,72021,72022,72024],{"class":64,"line":113},[62,72023,942],{"class":72},[62,72025,11133],{"class":68},[62,72027,72028,72030],{"class":64,"line":129},[62,72029,942],{"class":72},[62,72031,72032],{"class":68},"EnableAutoConfiguration\n",[62,72034,72035,72037,72040,72043,72046,72048,72051],{"class":64,"line":134},[62,72036,942],{"class":72},[62,72038,72039],{"class":68},"ComponentScan",[62,72041,72042],{"class":72},"({",[62,72044,72045],{"class":1675},"\"com.therealdanvega\"",[62,72047,32225],{"class":72},[62,72049,72050],{"class":1675},"\"com.foo.bar\"",[62,72052,50607],{"class":72},[62,72054,72055,72057,72059,72061],{"class":64,"line":156},[62,72056,116],{"class":68},[62,72058,119],{"class":68},[62,72060,71902],{"class":122},[62,72062,126],{"class":72},[62,72064,72065],{"class":64,"line":161},[62,72066,79],{"emptyLinePlaceholder":13},[62,72068,72069,72071,72073,72075,72077,72079,72081,72083,72085],{"class":64,"line":167},[62,72070,194],{"class":68},[62,72072,2101],{"class":68},[62,72074,200],{"class":68},[62,72076,2106],{"class":122},[62,72078,2109],{"class":72},[62,72080,973],{"class":889},[62,72082,52569],{"class":72},[62,72084,2117],{"class":889},[62,72086,768],{"class":72},[62,72088,72089,72091,72093],{"class":64,"line":173},[62,72090,2124],{"class":72},[62,72092,2127],{"class":122},[62,72094,71937],{"class":72},[62,72096,72097],{"class":64,"line":179},[62,72098,223],{"class":72},[62,72100,72101],{"class":64,"line":185},[62,72102,379],{"class":72},[22,72104,72105],{},"If you ctrl + click (cmd+click on the mac) on the Spring Boot Application Annotation you will be taken to the class definition of that so you can see exactly what is going on there.",[52,72107,72109],{"className":54,"code":72108,"language":56,"meta":57,"style":57},"package org.springframework.boot.autoconfigure;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.ComponentScan;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.core.annotation.AliasFor;\n\n/\\*\\*\n \\* Indicates a {@link Configuration configuration} class that declares one or more\n \\* {@link Bean @Bean} methods and also triggers {@link EnableAutoConfiguration\n \\* auto-configuration} and {@link ComponentScan component scanning}. This is a convenience\n \\* annotation that is equivalent to declaring {@code @Configuration},\n \\* {@code @EnableAutoConfiguration} and {@code @ComponentScan}.\n \\*\n \\* @author Phillip Webb\n \\* @author Stephane Nicoll\n \\* @since 1.2.0\n \\*/\n@Target(ElementType.TYPE)\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@Inherited\n@Configuration\n@EnableAutoConfiguration\n@ComponentScan\npublic @interface SpringBootApplication {\n\n /\\*\\*\n \\* Exclude specific auto-configuration classes such that they will never be applied.\n \\* @return the classes to exclude\n \\*/\n Class\u003C?>\\[\\] exclude() default {};\n\n /\\*\\*\n \\* Exclude specific auto-configuration class names such that they will never be\n \\* applied.\n \\* @return the class names to exclude\n \\* @since 1.3.0\n \\*/\n String\\[\\] excludeName() default {};\n\n /\\*\\*\n \\* Base packages to scan for annotated components. Use {@link #scanBasePackageClasses}\n \\* for a type-safe alternative to String-based package names.\n \\* @return base packages to scan\n \\* @since 1.3.0\n \\*/\n @AliasFor(annotation = ComponentScan.class, attribute = \"basePackages\")\n String\\[\\] scanBasePackages() default {};\n\n /\\*\\*\n \\* Type-safe alternative to {@link #scanBasePackages} for specifying the packages to\n \\* scan for annotated components. The package of each class specified will be scanned.\n \\* \u003Cp>\n \\* Consider creating a special no-op marker class or interface in each package that\n \\* serves no purpose other than being referenced by this attribute.\n \\* @return base packages to scan\n \\* @since 1.3.0\n \\*/\n @AliasFor(annotation = ComponentScan.class, attribute = \"basePackageClasses\")\n Class\u003C?>\\[\\] scanBasePackageClasses() default {};\n\n}\n",[59,72110,72111,72118,72122,72128,72135,72142,72148,72155,72161,72165,72171,72177,72183,72190,72194,72207,72229,72250,72269,72287,72315,72321,72334,72347,72366,72373,72382,72391,72397,72404,72410,72416,72423,72436,72440,72453,72468,72481,72487,72504,72508,72520,72537,72542,72554,72559,72564,72573,72577,72589,72608,72629,72642,72659,72665,72692,72706,72710,72722,72746,72768,72777,72794,72799,72804,72808,72812,72817,72831,72835],{"__ignoreMap":57},[62,72112,72113,72115],{"class":64,"line":65},[62,72114,69],{"class":68},[62,72116,72117],{"class":72}," org.springframework.boot.autoconfigure;\n",[62,72119,72120],{"class":64,"line":76},[62,72121,79],{"emptyLinePlaceholder":13},[62,72123,72124,72126],{"class":64,"line":82},[62,72125,27875],{"class":68},[62,72127,59388],{"class":72},[62,72129,72130,72132],{"class":64,"line":89},[62,72131,27875],{"class":68},[62,72133,72134],{"class":72}," java.lang.annotation.ElementType;\n",[62,72136,72137,72139],{"class":64,"line":95},[62,72138,27875],{"class":68},[62,72140,72141],{"class":72}," java.lang.annotation.Inherited;\n",[62,72143,72144,72146],{"class":64,"line":101},[62,72145,27875],{"class":68},[62,72147,59395],{"class":72},[62,72149,72150,72152],{"class":64,"line":107},[62,72151,27875],{"class":68},[62,72153,72154],{"class":72}," java.lang.annotation.RetentionPolicy;\n",[62,72156,72157,72159],{"class":64,"line":113},[62,72158,27875],{"class":68},[62,72160,59402],{"class":72},[62,72162,72163],{"class":64,"line":129},[62,72164,79],{"emptyLinePlaceholder":13},[62,72166,72167,72169],{"class":64,"line":134},[62,72168,27875],{"class":68},[62,72170,52527],{"class":72},[62,72172,72173,72175],{"class":64,"line":156},[62,72174,27875],{"class":68},[62,72176,72008],{"class":72},[62,72178,72179,72181],{"class":64,"line":161},[62,72180,27875],{"class":68},[62,72182,72015],{"class":72},[62,72184,72185,72187],{"class":64,"line":167},[62,72186,27875],{"class":68},[62,72188,72189],{"class":72}," org.springframework.core.annotation.AliasFor;\n",[62,72191,72192],{"class":64,"line":173},[62,72193,79],{"emptyLinePlaceholder":13},[62,72195,72196,72198,72201,72203,72205],{"class":64,"line":179},[62,72197,6936],{"class":68},[62,72199,72200],{"class":72},"\\",[62,72202,6973],{"class":68},[62,72204,72200],{"class":72},[62,72206,26612],{"class":68},[62,72208,72209,72212,72214,72217,72219,72222,72224,72226],{"class":64,"line":185},[62,72210,72211],{"class":72}," \\",[62,72213,6973],{"class":68},[62,72215,72216],{"class":72}," Indicates a {@",[62,72218,33106],{"class":68},[62,72220,72221],{"class":72}," Configuration configuration} ",[62,72223,11671],{"class":68},[62,72225,50417],{"class":122},[62,72227,72228],{"class":72}," declares one or more\n",[62,72230,72231,72234,72236,72239,72242,72245,72247],{"class":64,"line":191},[62,72232,72233],{"class":72}," \\* {@",[62,72235,33106],{"class":68},[62,72237,72238],{"class":72}," Bean @",[62,72240,72241],{"class":68},"Bean",[62,72243,72244],{"class":72},"} methods and also triggers {@",[62,72246,33106],{"class":68},[62,72248,72249],{"class":72}," EnableAutoConfiguration\n",[62,72251,72252,72254,72256,72259,72261,72264,72266],{"class":64,"line":209},[62,72253,72211],{"class":72},[62,72255,6973],{"class":68},[62,72257,72258],{"class":72}," auto",[62,72260,11635],{"class":68},[62,72262,72263],{"class":72},"configuration} and {@",[62,72265,33106],{"class":68},[62,72267,72268],{"class":72}," ComponentScan component scanning}. This is a convenience\n",[62,72270,72271,72273,72275,72278,72280,72282,72284],{"class":64,"line":220},[62,72272,72211],{"class":72},[62,72274,6973],{"class":68},[62,72276,72277],{"class":72}," annotation that is equivalent to declaring {@",[62,72279,59],{"class":68},[62,72281,9019],{"class":72},[62,72283,1998],{"class":68},[62,72285,72286],{"class":72},"},\n",[62,72288,72289,72291,72293,72296,72298,72300,72303,72306,72308,72310,72312],{"class":64,"line":226},[62,72290,72211],{"class":72},[62,72292,6973],{"class":68},[62,72294,72295],{"class":72}," {@",[62,72297,59],{"class":68},[62,72299,9019],{"class":72},[62,72301,72302],{"class":68},"EnableAutoConfiguration",[62,72304,72305],{"class":72},"} and {@",[62,72307,59],{"class":68},[62,72309,9019],{"class":72},[62,72311,72039],{"class":68},[62,72313,72314],{"class":72},"}.\n",[62,72316,72317,72319],{"class":64,"line":231},[62,72318,72211],{"class":72},[62,72320,26612],{"class":68},[62,72322,72323,72325,72327,72329,72331],{"class":64,"line":236},[62,72324,72211],{"class":72},[62,72326,6973],{"class":68},[62,72328,9019],{"class":72},[62,72330,8585],{"class":68},[62,72332,72333],{"class":72}," Phillip Webb\n",[62,72335,72336,72338,72340,72342,72344],{"class":64,"line":242},[62,72337,72211],{"class":72},[62,72339,6973],{"class":68},[62,72341,9019],{"class":72},[62,72343,8585],{"class":68},[62,72345,72346],{"class":72}," Stephane Nicoll\n",[62,72348,72349,72351,72353,72355,72358,72361,72363],{"class":64,"line":247},[62,72350,72211],{"class":72},[62,72352,6973],{"class":68},[62,72354,9019],{"class":72},[62,72356,72357],{"class":68},"since",[62,72359,72360],{"class":149}," 1.2",[62,72362,2755],{"class":72},[62,72364,72365],{"class":23824},"0\n",[62,72367,72368,72370],{"class":64,"line":252},[62,72369,72211],{"class":72},[62,72371,72372],{"class":68},"*/\n",[62,72374,72375,72377,72379],{"class":64,"line":257},[62,72376,942],{"class":72},[62,72378,59477],{"class":68},[62,72380,72381],{"class":72},"(ElementType.TYPE)\n",[62,72383,72384,72386,72388],{"class":64,"line":271},[62,72385,942],{"class":72},[62,72387,59487],{"class":68},[62,72389,72390],{"class":72},"(RetentionPolicy.RUNTIME)\n",[62,72392,72393,72395],{"class":64,"line":281},[62,72394,942],{"class":72},[62,72396,59453],{"class":68},[62,72398,72399,72401],{"class":64,"line":286},[62,72400,942],{"class":72},[62,72402,72403],{"class":68},"Inherited\n",[62,72405,72406,72408],{"class":64,"line":291},[62,72407,942],{"class":72},[62,72409,11133],{"class":68},[62,72411,72412,72414],{"class":64,"line":296},[62,72413,942],{"class":72},[62,72415,72032],{"class":68},[62,72417,72418,72420],{"class":64,"line":302},[62,72419,942],{"class":72},[62,72421,72422],{"class":68},"ComponentScan\n",[62,72424,72425,72427,72429,72431,72434],{"class":64,"line":308},[62,72426,116],{"class":68},[62,72428,9019],{"class":72},[62,72430,36687],{"class":68},[62,72432,72433],{"class":68}," SpringBootApplication",[62,72435,126],{"class":72},[62,72437,72438],{"class":64,"line":314},[62,72439,79],{"emptyLinePlaceholder":13},[62,72441,72442,72445,72447,72449,72451],{"class":64,"line":320},[62,72443,72444],{"class":68}," /",[62,72446,72200],{"class":72},[62,72448,6973],{"class":68},[62,72450,72200],{"class":72},[62,72452,26612],{"class":68},[62,72454,72455,72458,72460,72463,72465],{"class":64,"line":326},[62,72456,72457],{"class":72}," \\",[62,72459,6973],{"class":68},[62,72461,72462],{"class":72}," Exclude specific auto",[62,72464,11635],{"class":68},[62,72466,72467],{"class":72},"configuration classes such that they will never be applied.\n",[62,72469,72470,72472,72474,72476,72478],{"class":64,"line":338},[62,72471,72457],{"class":72},[62,72473,6973],{"class":68},[62,72475,9019],{"class":72},[62,72477,22008],{"class":68},[62,72479,72480],{"class":72}," the classes to exclude\n",[62,72482,72483,72485],{"class":64,"line":343},[62,72484,72457],{"class":72},[62,72486,72372],{"class":68},[62,72488,72489,72491,72493,72495,72498,72500,72502],{"class":64,"line":357},[62,72490,59525],{"class":72},[62,72492,59528],{"class":68},[62,72494,52569],{"class":72},[62,72496,72497],{"class":122},"exclude",[62,72499,5398],{"class":72},[62,72501,59515],{"class":68},[62,72503,59540],{"class":72},[62,72505,72506],{"class":64,"line":366},[62,72507,79],{"emptyLinePlaceholder":13},[62,72509,72510,72512,72514,72516,72518],{"class":64,"line":371},[62,72511,72444],{"class":68},[62,72513,72200],{"class":72},[62,72515,6973],{"class":68},[62,72517,72200],{"class":72},[62,72519,26612],{"class":68},[62,72521,72522,72524,72526,72529,72531,72534],{"class":64,"line":376},[62,72523,72457],{"class":72},[62,72525,6973],{"class":68},[62,72527,72528],{"class":72}," Exclude specific auto-configuration ",[62,72530,11671],{"class":68},[62,72532,72533],{"class":122}," names",[62,72535,72536],{"class":72}," such that they will never be\n",[62,72538,72539],{"class":64,"line":16333},[62,72540,72541],{"class":72}," \\* applied.\n",[62,72543,72544,72547,72549,72551],{"class":64,"line":16349},[62,72545,72546],{"class":72}," \\* @return the ",[62,72548,11671],{"class":68},[62,72550,72533],{"class":122},[62,72552,72553],{"class":72}," to exclude\n",[62,72555,72556],{"class":64,"line":16365},[62,72557,72558],{"class":72}," \\* @since 1.3.0\n",[62,72560,72561],{"class":64,"line":16381},[62,72562,72563],{"class":72}," \\*/\n",[62,72565,72566,72569,72571],{"class":64,"line":16402},[62,72567,72568],{"class":72}," String\\[\\] excludeName() ",[62,72570,59515],{"class":68},[62,72572,59540],{"class":72},[62,72574,72575],{"class":64,"line":16411},[62,72576,79],{"emptyLinePlaceholder":13},[62,72578,72579,72581,72583,72585,72587],{"class":64,"line":16427},[62,72580,72444],{"class":68},[62,72582,72200],{"class":72},[62,72584,6973],{"class":68},[62,72586,72200],{"class":72},[62,72588,26612],{"class":68},[62,72590,72591,72593,72595,72598,72600,72603,72605],{"class":64,"line":16448},[62,72592,72457],{"class":72},[62,72594,6973],{"class":68},[62,72596,72597],{"class":72}," Base packages to scan ",[62,72599,741],{"class":68},[62,72601,72602],{"class":72}," annotated components. Use {@",[62,72604,33106],{"class":68},[62,72606,72607],{"class":72}," #scanBasePackageClasses}\n",[62,72609,72610,72612,72614,72616,72619,72621,72624,72626],{"class":64,"line":16457},[62,72611,72457],{"class":72},[62,72613,6973],{"class":68},[62,72615,53629],{"class":68},[62,72617,72618],{"class":72}," a type",[62,72620,11635],{"class":68},[62,72622,72623],{"class":72},"safe alternative to String",[62,72625,11635],{"class":68},[62,72627,72628],{"class":72},"based package names.\n",[62,72630,72631,72633,72635,72637,72639],{"class":64,"line":16466},[62,72632,72457],{"class":72},[62,72634,6973],{"class":68},[62,72636,9019],{"class":72},[62,72638,22008],{"class":68},[62,72640,72641],{"class":72}," base packages to scan\n",[62,72643,72644,72646,72648,72650,72652,72655,72657],{"class":64,"line":16471},[62,72645,72457],{"class":72},[62,72647,6973],{"class":68},[62,72649,9019],{"class":72},[62,72651,72357],{"class":68},[62,72653,72654],{"class":149}," 1.3",[62,72656,2755],{"class":72},[62,72658,72365],{"class":23824},[62,72660,72661,72663],{"class":64,"line":16487},[62,72662,72457],{"class":72},[62,72664,72372],{"class":68},[62,72666,72667,72669,72672,72674,72677,72679,72682,72685,72687,72690],{"class":64,"line":16504},[62,72668,2143],{"class":72},[62,72670,72671],{"class":68},"AliasFor",[62,72673,2109],{"class":72},[62,72675,72676],{"class":149},"annotation",[62,72678,2556],{"class":68},[62,72680,72681],{"class":72}," ComponentScan.class, ",[62,72683,72684],{"class":149},"attribute",[62,72686,2556],{"class":68},[62,72688,72689],{"class":1675}," \"basePackages\"",[62,72691,2212],{"class":72},[62,72693,72694,72697,72700,72702,72704],{"class":64,"line":16517},[62,72695,72696],{"class":72}," String\\[\\] ",[62,72698,72699],{"class":122},"scanBasePackages",[62,72701,5398],{"class":72},[62,72703,59515],{"class":68},[62,72705,59540],{"class":72},[62,72707,72708],{"class":64,"line":16523},[62,72709,79],{"emptyLinePlaceholder":13},[62,72711,72712,72714,72716,72718,72720],{"class":64,"line":16532},[62,72713,72444],{"class":68},[62,72715,72200],{"class":72},[62,72717,6973],{"class":68},[62,72719,72200],{"class":72},[62,72721,26612],{"class":68},[62,72723,72724,72726,72728,72731,72733,72736,72738,72741,72743],{"class":64,"line":16546},[62,72725,72457],{"class":72},[62,72727,6973],{"class":68},[62,72729,72730],{"class":72}," Type",[62,72732,11635],{"class":68},[62,72734,72735],{"class":72},"safe alternative to {@",[62,72737,33106],{"class":68},[62,72739,72740],{"class":72}," #scanBasePackages} ",[62,72742,741],{"class":68},[62,72744,72745],{"class":72}," specifying the packages to\n",[62,72747,72748,72750,72752,72755,72757,72760,72762,72765],{"class":64,"line":16557},[62,72749,72457],{"class":72},[62,72751,6973],{"class":68},[62,72753,72754],{"class":72}," scan ",[62,72756,741],{"class":68},[62,72758,72759],{"class":72}," annotated components. The package of each ",[62,72761,11671],{"class":68},[62,72763,72764],{"class":122}," specified",[62,72766,72767],{"class":72}," will be scanned.\n",[62,72769,72770,72773,72775],{"class":64,"line":16563},[62,72771,72772],{"class":72}," \\* \u003C",[62,72774,22],{"class":68},[62,72776,1784],{"class":72},[62,72778,72779,72782,72784,72787,72789,72791],{"class":64,"line":16572},[62,72780,72781],{"class":72}," \\* Consider creating a special no-op marker ",[62,72783,11671],{"class":68},[62,72785,72786],{"class":122}," or",[62,72788,8531],{"class":68},[62,72790,57031],{"class":122},[62,72792,72793],{"class":72}," each package that\n",[62,72795,72796],{"class":64,"line":16581},[62,72797,72798],{"class":72}," \\* serves no purpose other than being referenced by this attribute.\n",[62,72800,72801],{"class":64,"line":16590},[62,72802,72803],{"class":72}," \\* @return base packages to scan\n",[62,72805,72806],{"class":64,"line":16599},[62,72807,72558],{"class":72},[62,72809,72810],{"class":64,"line":22013},[62,72811,72563],{"class":72},[62,72813,72814],{"class":64,"line":22023},[62,72815,72816],{"class":72}," @AliasFor(annotation = ComponentScan.class, attribute = \"basePackageClasses\")\n",[62,72818,72819,72822,72824,72827,72829],{"class":64,"line":22049},[62,72820,72821],{"class":72}," Class\u003C",[62,72823,5668],{"class":68},[62,72825,72826],{"class":72},">\\[\\] scanBasePackageClasses() ",[62,72828,59515],{"class":68},[62,72830,59540],{"class":72},[62,72832,72833],{"class":64,"line":22054},[62,72834,79],{"emptyLinePlaceholder":13},[62,72836,72837],{"class":64,"line":22059},[62,72838,379],{"class":72},[1527,72840,72841],{},"html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .spoOn, html code.shiki .spoOn{--shiki-default:#E36209;--shiki-dark:#FFAB70;--shiki-sepia:#E36209}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}html pre.shiki code .shOWo, html code.shiki .shOWo{--shiki-default:#B31D28;--shiki-default-font-style:italic;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic;--shiki-sepia:#B31D28;--shiki-sepia-font-style:italic}",{"title":57,"searchDepth":76,"depth":76,"links":72843},[],{"_id":72845,"path":72846,"title":72847,"description":72848,"meta":72849,"body":72854},"content/blog/2015/04/24/ready-player-one-review.md","/blog/2015/04/24/ready-player-one-review","Ready Player One Review","A quick review of the book Ready Player One",{"slug":72850,"date":72851,"published":13,"tags":72852,"author":-1,"cover":72853,"excerpt":-1},"ready-player-one-review","2015-04-24T17:00:00.000Z",[37756],"bookcase-books-bookshelves-159711.jpg",{"type":19,"value":72855,"toc":72952},[72856,72860,72869,72878,72883,72886,72895,72898,72901,72905,72914,72920,72934,72937,72941,72944],[636,72857,72859],{"id":72858},"the-book","The Book",[22,72861,72862,72863,72868],{},"I recently finished the book ",[677,72864,72867],{"href":72865,"rel":72866},"http://www.amazon.com/Ready-Player-One-Ernest-Cline/dp/0307887448/ref=sr_1_1?ie=UTF8&qid=1429924661&sr=8-1&keywords=ready+player+one",[681],"Ready Player One"," and I thought I would take this opportunity to give it a quick review. I don't give a ton of book reviews on this website but I enjoy talking about what is going on in my life and I love to hear from my readers so let's give it a try. I'm not going to give any spoilers away so if you haven't read the book, yet you can continue reading without worry.",[22,72870,72871,72872,72877],{},"I don't think I would have ever come across this book if it were not for ",[677,72873,72876],{"href":72874,"rel":72875},"https://www.lootcrate.com/",[681],"Loot Crate",". Loot Crate is wonderful monthly subscription box and I highly recommend checking them out. A couple of months ago I got a new Loot Crate that contained the book Ready Player One. It was a pretty small book, so it was right in my wheelhouse. Not having heard of or having any idea what this book was about I decided to read the synopsis that went something like this",[29685,72879,72880],{},[22,72881,72882],{},"In the year 2044, reality is an ugly place. The only time teenage Wade Watts really feels alive is when he's jacked into the virtual utopia known as the OASIS. Wade's devoted his life to studying the puzzles hidden within this world's digital confines—puzzles that are based on their creator's obsession with the pop culture of decades past and that promise massive power and fortune to whoever can unlock them. But when Wade stumbles upon the first clue, he finds himself beset by players willing to kill to take this ultimate prize. The race is on, and if Wade's going to survive, he'll have to win—and confront the real world he's always been so desperate to escape.",[22,72884,72885],{},"I was immediately hooked. In fact if you call yourself a geek I just can't imagine how you don't get excited reading that. The book centers around a virtual world called the Oasis. The Oasis was a way to escape the reality that at the time wasn't that great for a lot of people, so it was easy to see why it became so popular. When the book was written Virtual Reality technology was still in its infancy but what really excites me is that I could totally see this becoming reality. It's not that far-fetched to think that we could all be logging into a virtual reality to escape the real world in the not to distant future. Thrown in are some really great references to 80's pop culture and video games play a huge role throughout the book.",[22,72887,72888,72889,72894],{},"I mean the creator of the Oasis ends up turning the entire hunt into one large video game. I read about half the book, and then I just couldn't find any time to finish it. I really wanted to find out how this story ended, so I decided to check out the audiobook which is narrated by ",[677,72890,72893],{"href":72891,"rel":72892},"https://twitter.com/wilw",[681],"Wil Wheaton",". I run a lot on the treadmill at lunch so this was a perfect way to kill 2 birds with one stone. I made the decision that I would just start from the beginning, and I am really glad I did because I immediately learned a few more things the 2nd time through.",[22,72896,72897],{},"I fell in love with the characters and how they developed throughout the book. Wade Watts, our fearless leader shows such devotion to his quest. It was also a very suspenseful book. I found myself running on the treadmill at lunch pumping my fist in excitement at certain points in the quest.",[22,72899,72900],{},"I really don't want to give away too much, so I will leave it at this. The book is very well written and the story is packed with excitement from start to end. There are also some surprises throughout,a fantastic ending and a huge moral to the story.",[636,72902,72904],{"id":72903},"the-movie","The Movie",[22,72906,72907,72908,72913],{},"When I finished the book I was just surfing the web and trying to find more info about the book when ",[677,72909,72912],{"href":72910,"rel":72911},"http://readyplayerone.com/",[681],"I came across the official website",". There were some really great fan submitted art that helped visualize certain aspects of the book. This is one of my favorite images and I don't want to give much away but if you have read the book you should recognize it.",[22,72915,72916],{},[653,72917],{"alt":72918,"src":72919},"Ready Player One Movie","/images/blog/2015/04/24/ready_player_one-300x244.jpg",[22,72921,72922,72923,72928,72929,2755],{},"There was also a mention on the website that the book had plans to be turned into movie. This was exactly what I wanted to hear. This magical adventure is going to be turned into something I can see with my eyes I thought? My next thought was I really hope they find someone great to direct it. ",[677,72924,72927],{"href":72925,"rel":72926},"http://deadline.com/2015/03/ready-player-one-movie-steven-spielberg-ernest-cline-warner-bros-1201398299/",[681],"I found the story on Deadline"," and to my amazement Steven Spielberg had signed on to direct it. This was not only great news but actually pretty funny as a ton of his movies are referenced throughout the book. Here is a quick breakdown of the news by ",[677,72930,72933],{"href":72931,"rel":72932},"http://nerdist.com/",[681],"Nerdist",[36089,72935],{"id":72936},"zwcpM0mXoPg",[636,72938,72940],{"id":72939},"read-it-listen-to-it-watch-it","Read It. Listen to It. Watch It.",[22,72942,72943],{},"I hope that this is the first you're hearing about this book and I have you excited to get this book ASAP. If you have heard of it before and were on the fence what are you waiting for? This is a must-read / listen and I would encourage all of you to find some time to enjoy this wonderful fantasy thrill ride.",[22,72945,72946,72947,6277],{},"If you have already read the book I would love to hear your thoughts on it. Please remember though people reading the comments may not have read it yet so don't give anything big away. If you read the book based on this review I would certainly like to hear that as well. All in all this is by far one of the best books I have ever read. Thank you ",[677,72948,72951],{"href":72949,"rel":72950},"https://twitter.com/erniecline",[681],"Ernest Cline",{"title":57,"searchDepth":76,"depth":76,"links":72953},[72954,72955,72956],{"id":72858,"depth":82,"text":72859},{"id":72903,"depth":82,"text":72904},{"id":72939,"depth":82,"text":72940},{"_id":72958,"path":72959,"title":72960,"description":72960,"meta":72961,"body":72966},"content/blog/2015/04/16/windows-kill-process-by-port-number.md","/blog/2015/04/16/windows-kill-process-by-port-number","Windows Kill Process By Port Number",{"slug":72962,"date":72963,"published":13,"tags":72964,"author":-1,"cover":72965,"excerpt":-1},"windows-kill-process-by-port-number","2015-04-16T10:35:05-04:00",[41143],"./windows-kill-port-cover.jpg",{"type":19,"value":72967,"toc":73023},[72968,72971,72979,72982,72994,73004,73009,73017,73020],[22,72969,72970],{},"In this short tutorial, we will look at how to kill a process by port number on Windows. I am doing a lot of Java development these days and I have about 5 different applications that I may fire up during a day and because they all run on my local machine most of them use port 8080 by default. Once in a while, I will forget to kill an application. When I try and start another application I will receive this error.",[22,72972,72973],{},[677,72974,72976],{"href":72975},"./port_already_in_use.png",[653,72977],{"alt":72978,"src":72975},"Windows Port Already in use",[22,72980,72981],{},"If you need to kill a process manually on Windows it's actually pretty easy. First, fire up a command prompt and type the following command.",[52,72983,72985],{"className":1663,"code":72984,"language":1665,"meta":57,"style":57},"> netstat -a -o -n\n",[59,72986,72987],{"__ignoreMap":57},[62,72988,72989,72991],{"class":64,"line":65},[62,72990,2583],{"class":68},[62,72992,72993],{"class":72}," netstat -a -o -n\n",[22,72995,72996,72997,73003],{},"To kill the process we need to find the PID of the process in question. I just run down the list by port until I find port 8080 and here you will see the process id was 28344. ",[677,72998,73000],{"href":72999},"./netstat_1.png",[653,73001],{"alt":73002,"src":72999},"netstat_1"," Finally, with the PID we can run the following command to kill the process",[29685,73005,73006],{},[22,73007,73008],{},"taskkill /F /PID 28344",[22,73010,73011],{},[677,73012,73014],{"href":73013},"./netstat_2.png",[653,73015],{"alt":73016,"src":73013},"Windows Netstat",[22,73018,73019],{},"Pretty easy solution to a pretty simple problem. Hope this helps!",[1527,73021,73022],{},"html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}",{"title":57,"searchDepth":76,"depth":76,"links":73024},[],{"_id":73026,"path":73027,"title":73028,"description":73029,"meta":73030,"body":73035},"content/blog/2015/02/05/sql-server-exception-statement-not-return-result-set.md","/blog/2015/02/05/sql-server-exception-statement-not-return-result-set","SQL Server Exception - The Statement Did Not Return A Result Set","Using Stored Procedures in a Grails Application with Groovy",{"slug":73031,"date":73032,"published":13,"tags":73033,"author":-1,"cover":73034,"excerpt":-1},"sql-server-exception-statement-not-return-result-set","2015-02-05T11:48:18-05:00",[53536,38716],"./data-cover.jpg",{"type":19,"value":73036,"toc":73319},[73037,73046,73060,73121,73124,73175,73178,73183,73189,73246,73249,73313,73316],[22,73038,73039,73040,73045],{},"We are working on moving a fairly large Dynamic Java Web Project over to a Grails application. In this application we have a ton of stored procedures that we need to continue to use in our Grails app. Luckily it couldn't be easier to call a stored proc in Groovy. If you look at the ",[677,73041,73044],{"href":73042,"rel":73043},"http://groovy.codehaus.org/api/groovy/sql/Sql.html",[681],"groovy.sql.Sql"," class there are a ton of methods for this.",[22,73047,73048,73049,73052,73053,73056,73057,73059],{},"There are numerous calls methods but one of the easiest ways to get back a list is to use the rows method and pass in a ",[59,73050,73051],{},"gstring",". In this example we are calling a stored procedure called ",[59,73054,73055],{},"getFooStoredProc"," that takes 1 parameter and because this is a ",[59,73058,73051],{}," we can include our variable in our call.",[52,73061,73064],{"className":73062,"code":73063,"language":53536,"meta":57,"style":57},"language-groovy shiki shiki-themes github-light github-dark github-light","def getFoo( String id ) {\n List results = sql.rows( \"{call getFooStoredProc($id)}\" )\n results\n}\n",[59,73065,73066,73083,73112,73117],{"__ignoreMap":57},[62,73067,73068,73071,73074,73076,73078,73080],{"class":64,"line":65},[62,73069,73070],{"class":68},"def",[62,73072,73073],{"class":122}," getFoo",[62,73075,52630],{"class":72},[62,73077,973],{"class":68},[62,73079,20831],{"class":889},[62,73081,73082],{"class":72}," ) {\n",[62,73084,73085,73088,73091,73093,73096,73098,73101,73104,73107,73110],{"class":64,"line":76},[62,73086,73087],{"class":68}," List",[62,73089,73090],{"class":72}," results ",[62,73092,146],{"class":68},[62,73094,73095],{"class":72}," sql",[62,73097,2755],{"class":68},[62,73099,73100],{"class":72},"rows( ",[62,73102,73103],{"class":1675},"\"{call getFooStoredProc(",[62,73105,73106],{"class":72},"$id",[62,73108,73109],{"class":1675},")}\"",[62,73111,59250],{"class":72},[62,73113,73114],{"class":64,"line":82},[62,73115,73116],{"class":72}," results\n",[62,73118,73119],{"class":64,"line":89},[62,73120,379],{"class":72},[22,73122,73123],{},"So I had this working for a ton of stored procedure calls already when I came across a very strange error yesterday.",[52,73125,73127],{"className":73062,"code":73126,"language":53536,"meta":57,"style":57},"def getBar( String id ) {\n List results = sql.rows( \"{call getBarStoredProc($id)}\" )\n results\n}\n",[59,73128,73129,73144,73167,73171],{"__ignoreMap":57},[62,73130,73131,73133,73136,73138,73140,73142],{"class":64,"line":65},[62,73132,73070],{"class":68},[62,73134,73135],{"class":122}," getBar",[62,73137,52630],{"class":72},[62,73139,973],{"class":68},[62,73141,20831],{"class":889},[62,73143,73082],{"class":72},[62,73145,73146,73148,73150,73152,73154,73156,73158,73161,73163,73165],{"class":64,"line":76},[62,73147,73087],{"class":68},[62,73149,73090],{"class":72},[62,73151,146],{"class":68},[62,73153,73095],{"class":72},[62,73155,2755],{"class":68},[62,73157,73100],{"class":72},[62,73159,73160],{"class":1675},"\"{call getBarStoredProc(",[62,73162,73106],{"class":72},[62,73164,73109],{"class":1675},[62,73166,59250],{"class":72},[62,73168,73169],{"class":64,"line":82},[62,73170,73116],{"class":72},[62,73172,73173],{"class":64,"line":89},[62,73174,379],{"class":72},[22,73176,73177],{},"This looks almost identical to the call I made before but this time the code was throwing an exception.",[29685,73179,73180],{},[22,73181,73182],{},"SqlServerException: The Statement Did Not Return A Result Set",[22,73184,73185,73186],{},"I was really confused by this so I went to the database and the stored procedure with the same parameter was running just fine. After some digging around and talking it through with some coworkers we finally found the solution. As I said before this wasn't happening for stored procedures that I was converting before but that is because none of them were doing any inserts or updates. If you are doing any inserts/updates before the final select for whatever reason this error will be thrown. For the life of me I still don't understand why I just know that it happens. So you have 2 solutions to fix this. First you can update the stored procedure to ",[59,73187,73188],{},"SET NOTCOUNT ON",[52,73190,73192],{"className":41824,"code":73191,"language":38716,"meta":57,"style":57},"create procedure getBarStoredProc (\n @id int\n)\nAS\nBEGIN\nSET NOCOUNT ON\n...\nEND\n",[59,73193,73194,73204,73212,73216,73221,73226,73237,73241],{"__ignoreMap":57},[62,73195,73196,73198,73201],{"class":64,"line":65},[62,73197,14348],{"class":68},[62,73199,73200],{"class":68}," procedure",[62,73202,73203],{"class":72}," getBarStoredProc (\n",[62,73205,73206,73209],{"class":64,"line":76},[62,73207,73208],{"class":72}," @id ",[62,73210,73211],{"class":68},"int\n",[62,73213,73214],{"class":64,"line":82},[62,73215,2212],{"class":72},[62,73217,73218],{"class":64,"line":89},[62,73219,73220],{"class":68},"AS\n",[62,73222,73223],{"class":64,"line":95},[62,73224,73225],{"class":68},"BEGIN\n",[62,73227,73228,73231,73234],{"class":64,"line":101},[62,73229,73230],{"class":68},"SET",[62,73232,73233],{"class":68}," NOCOUNT",[62,73235,73236],{"class":68}," ON\n",[62,73238,73239],{"class":64,"line":107},[62,73240,71140],{"class":72},[62,73242,73243],{"class":64,"line":113},[62,73244,73245],{"class":68},"END\n",[22,73247,73248],{},"If you only have a few stored procedures this would be an ideal solution. In my case though I have way to many to do that. The good news is you can do this via code before that stored procedure is called by executing a sql statement before your stored procedure call.",[52,73250,73252],{"className":73062,"code":73251,"language":53536,"meta":57,"style":57},"def getBar( String id ) {\n sql.execute( \"SET NOCOUNT ON\" )\n List results = sql.rows( \"{call getBarStoredProc($id)}\" )\n results\n}\n",[59,73253,73254,73268,73283,73305,73309],{"__ignoreMap":57},[62,73255,73256,73258,73260,73262,73264,73266],{"class":64,"line":65},[62,73257,73070],{"class":68},[62,73259,73135],{"class":122},[62,73261,52630],{"class":72},[62,73263,973],{"class":68},[62,73265,20831],{"class":889},[62,73267,73082],{"class":72},[62,73269,73270,73273,73275,73278,73281],{"class":64,"line":76},[62,73271,73272],{"class":72}," sql",[62,73274,2755],{"class":68},[62,73276,73277],{"class":72},"execute( ",[62,73279,73280],{"class":1675},"\"SET NOCOUNT ON\"",[62,73282,59250],{"class":72},[62,73284,73285,73287,73289,73291,73293,73295,73297,73299,73301,73303],{"class":64,"line":82},[62,73286,73087],{"class":68},[62,73288,73090],{"class":72},[62,73290,146],{"class":68},[62,73292,73095],{"class":72},[62,73294,2755],{"class":68},[62,73296,73100],{"class":72},[62,73298,73160],{"class":1675},[62,73300,73106],{"class":72},[62,73302,73109],{"class":1675},[62,73304,59250],{"class":72},[62,73306,73307],{"class":64,"line":89},[62,73308,73116],{"class":72},[62,73310,73311],{"class":64,"line":95},[62,73312,379],{"class":72},[22,73314,73315],{},"Hopefully this helps someone else who comes across this crazy issue.",[1527,73317,73318],{},"html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .spoOn, html code.shiki .spoOn{--shiki-default:#E36209;--shiki-dark:#FFAB70;--shiki-sepia:#E36209}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}",{"title":57,"searchDepth":76,"depth":76,"links":73320},[],{"_id":73322,"path":73323,"title":73324,"description":73325,"meta":73326,"body":73332},"content/blog/2014/04/08/gravatars-on-a-grails-application.md","/blog/2014/04/08/gravatars-on-a-grails-application","Gravatars on a Grails Application","Gravatars in a Grails Application",{"slug":73327,"date":73328,"published":13,"tags":73329,"author":-1,"cover":73331,"excerpt":-1},"gravatars-on-a-grails-application","2014-04-08T14:04:00-04:00",[73330,53536],"grails",null,{"type":19,"value":73333,"toc":73884},[73334,73349,73357,73360,73368,73371,73587,73590,73881],[22,73335,73336,73337,73342,73343,73348],{},"Building out my new site in Grails I needed the ability to show a ",[677,73338,73341],{"href":73339,"rel":73340},"http://en.gravatar.com/",[681],"Gravatar"," in the comments. I know there are usually plugins out there but I there are a lot of things I am going to do on my own just so I have an excuse to blog about them. First off visit the ",[677,73344,73347],{"href":73345,"rel":73346},"http://en.gravatar.com/site/implement/images/",[681],"Gravatar docs"," to get an idea of how to request an image. This is what the finished product is going to look like.  The most basic image request URL looks like this:",[29685,73350,73351],{},[22,73352,73353],{},[677,73354,73355],{"href":73355,"rel":73356},"http://www.gravatar.com/avatar/HASH",[681],[22,73358,73359],{},"where HASH is replaced with the calculated hash for the specific email address you are requesting. For example, here is my base URL:",[29685,73361,73362],{},[22,73363,73364],{},[677,73365,73366],{"href":73366,"rel":73367},"http://www.gravatar.com/avatar/205e460b479e2e5b48aec07710c08d50",[681],[22,73369,73370],{},"If you look at the docs there are ways to customize the size and default image if an account is not found. There are a couple different approaches we can take but this is the route I decided to go. Since a comment is just going to be inserted why not store the email hash in the db. We have an easy way of doing this with GORM events. On insert I am going to get the MD5hash of the email address and store it.",[52,73372,73374],{"className":73062,"code":73373,"language":53536,"meta":57,"style":57},"package com.vega.blog\n\nclass Comment {\n\n String name\n String email\n String emailHash\n String website\n String content\n Boolean subscribe = false\n Date dateCreated\n\n static belongsTo = \\[post:Post\\]\n\n static constraints = {\n emailHash nullable: true\n }\n\n static mapping = {\n sort dateCreated: 'desc'\n }\n\n // event handlers\n def beforeInsert(){\n emailHash = email.encodeAsMD5()\n }\n\n}\n",[59,73375,73376,73383,73387,73396,73400,73408,73415,73422,73428,73435,73448,73456,73460,73479,73483,73494,73506,73510,73514,73525,73538,73542,73546,73551,73561,73575,73579,73583],{"__ignoreMap":57},[62,73377,73378,73380],{"class":64,"line":65},[62,73379,69],{"class":68},[62,73381,73382],{"class":72}," com.vega.blog\n",[62,73384,73385],{"class":64,"line":76},[62,73386,79],{"emptyLinePlaceholder":13},[62,73388,73389,73391,73394],{"class":64,"line":82},[62,73390,11671],{"class":68},[62,73392,73393],{"class":122}," Comment",[62,73395,126],{"class":72},[62,73397,73398],{"class":64,"line":89},[62,73399,79],{"emptyLinePlaceholder":13},[62,73401,73402,73405],{"class":64,"line":95},[62,73403,73404],{"class":68}," String",[62,73406,73407],{"class":72}," name\n",[62,73409,73410,73412],{"class":64,"line":101},[62,73411,73404],{"class":68},[62,73413,73414],{"class":72}," email\n",[62,73416,73417,73419],{"class":64,"line":107},[62,73418,73404],{"class":68},[62,73420,73421],{"class":72}," emailHash\n",[62,73423,73424,73426],{"class":64,"line":113},[62,73425,73404],{"class":68},[62,73427,53637],{"class":72},[62,73429,73430,73432],{"class":64,"line":129},[62,73431,73404],{"class":68},[62,73433,73434],{"class":72}," content\n",[62,73436,73437,73440,73443,73445],{"class":64,"line":134},[62,73438,73439],{"class":68}," Boolean",[62,73441,73442],{"class":72}," subscribe ",[62,73444,146],{"class":68},[62,73446,73447],{"class":149}," false\n",[62,73449,73450,73453],{"class":64,"line":156},[62,73451,73452],{"class":68}," Date",[62,73454,73455],{"class":72}," dateCreated\n",[62,73457,73458],{"class":64,"line":161},[62,73459,79],{"emptyLinePlaceholder":13},[62,73461,73462,73464,73467,73469,73471,73473,73475,73477],{"class":64,"line":167},[62,73463,70061],{"class":68},[62,73465,73466],{"class":72}," belongsTo ",[62,73468,146],{"class":68},[62,73470,58153],{"class":72},[62,73472,38838],{"class":149},[62,73474,1266],{"class":72},[62,73476,38700],{"class":68},[62,73478,50699],{"class":72},[62,73480,73481],{"class":64,"line":173},[62,73482,79],{"emptyLinePlaceholder":13},[62,73484,73485,73487,73490,73492],{"class":64,"line":179},[62,73486,70061],{"class":68},[62,73488,73489],{"class":72}," constraints ",[62,73491,146],{"class":68},[62,73493,126],{"class":72},[62,73495,73496,73499,73502,73504],{"class":64,"line":185},[62,73497,73498],{"class":72}," emailHash ",[62,73500,73501],{"class":149},"nullable",[62,73503,3696],{"class":72},[62,73505,51914],{"class":149},[62,73507,73508],{"class":64,"line":191},[62,73509,223],{"class":72},[62,73511,73512],{"class":64,"line":209},[62,73513,79],{"emptyLinePlaceholder":13},[62,73515,73516,73518,73521,73523],{"class":64,"line":220},[62,73517,70061],{"class":68},[62,73519,73520],{"class":72}," mapping ",[62,73522,146],{"class":68},[62,73524,126],{"class":72},[62,73526,73527,73530,73533,73535],{"class":64,"line":226},[62,73528,73529],{"class":72}," sort ",[62,73531,73532],{"class":149},"dateCreated",[62,73534,3696],{"class":72},[62,73536,73537],{"class":1675},"'desc'\n",[62,73539,73540],{"class":64,"line":231},[62,73541,223],{"class":72},[62,73543,73544],{"class":64,"line":236},[62,73545,79],{"emptyLinePlaceholder":13},[62,73547,73548],{"class":64,"line":242},[62,73549,73550],{"class":85}," // event handlers\n",[62,73552,73553,73556,73559],{"class":64,"line":247},[62,73554,73555],{"class":68}," def",[62,73557,73558],{"class":122}," beforeInsert",[62,73560,57791],{"class":72},[62,73562,73563,73565,73567,73570,73572],{"class":64,"line":252},[62,73564,73498],{"class":72},[62,73566,146],{"class":68},[62,73568,73569],{"class":72}," email",[62,73571,2755],{"class":68},[62,73573,73574],{"class":72},"encodeAsMD5()\n",[62,73576,73577],{"class":64,"line":257},[62,73578,223],{"class":72},[62,73580,73581],{"class":64,"line":271},[62,73582,79],{"emptyLinePlaceholder":13},[62,73584,73585],{"class":64,"line":281},[62,73586,379],{"class":72},[22,73588,73589],{},"That is really all there is to it. Now we can call ${comment.emailHash} and grab that value right from the db. Here is what the final comments template looks like.",[52,73591,73593],{"className":15773,"code":73592,"language":15775,"meta":57,"style":57},"\u003Cdiv class=\"comments well\">\n \u003Cdiv class=\"title\">\n \u003Ch4>${post.comments.size()} Comments\u003C/h4>\n \u003C/div>\n \u003Ca name=\"comments\">\u003C/a>\u003Ca name=\"${comment.id}\">\u003C/a>\n \u003Cul class=\"comment-list\">\n \u003Cli class=\"comment\">\u003Cimg class=\"avatar pull-left\" title=\"${comment.name}\" src=\"http://www.gravatar.com/avatar/${comment.emailHash}?s=64&r=pg&d=mm\" alt=\"\" />\n \u003Cdiv class=\"comment-author\">${comment.name} \u003Ca title=\"permalink\" href=\"#\">#\u003C/a>\u003C/div>\n \u003Cdiv class=\"cmeta\">Commented on ${comment.dateCreated.format('MM/dd/yyyy hh:mm')}\u003C/div>\n ${comment.content}\n \u003Cdiv class=\"clearfix\">\u003C/div>\n \u003C/li>\n \u003C/ul>\n\u003C/div>\n\u003Cdiv class=\"clear\">\u003C/div>\n",[59,73594,73595,73610,73624,73637,73645,73679,73694,73752,73794,73814,73819,73838,73846,73854,73862],{"__ignoreMap":57},[62,73596,73597,73599,73601,73603,73605,73608],{"class":64,"line":65},[62,73598,760],{"class":72},[62,73600,15944],{"class":1780},[62,73602,119],{"class":122},[62,73604,146],{"class":72},[62,73606,73607],{"class":1675},"\"comments well\"",[62,73609,1784],{"class":72},[62,73611,73612,73614,73616,73618,73620,73622],{"class":64,"line":76},[62,73613,1789],{"class":72},[62,73615,15944],{"class":1780},[62,73617,119],{"class":122},[62,73619,146],{"class":72},[62,73621,42161],{"class":1675},[62,73623,1784],{"class":72},[62,73625,73626,73628,73630,73633,73635],{"class":64,"line":82},[62,73627,1896],{"class":72},[62,73629,28831],{"class":1780},[62,73631,73632],{"class":72},">${post.comments.size()} Comments\u003C/",[62,73634,28831],{"class":1780},[62,73636,1784],{"class":72},[62,73638,73639,73641,73643],{"class":64,"line":89},[62,73640,1982],{"class":72},[62,73642,15944],{"class":1780},[62,73644,1784],{"class":72},[62,73646,73647,73649,73651,73653,73655,73658,73660,73662,73664,73666,73668,73670,73673,73675,73677],{"class":64,"line":95},[62,73648,1789],{"class":72},[62,73650,677],{"class":1780},[62,73652,16107],{"class":122},[62,73654,146],{"class":72},[62,73656,73657],{"class":1675},"\"comments\"",[62,73659,15857],{"class":72},[62,73661,677],{"class":1780},[62,73663,68460],{"class":72},[62,73665,677],{"class":1780},[62,73667,16107],{"class":122},[62,73669,146],{"class":72},[62,73671,73672],{"class":1675},"\"${comment.id}\"",[62,73674,15857],{"class":72},[62,73676,677],{"class":1780},[62,73678,1784],{"class":72},[62,73680,73681,73683,73685,73687,73689,73692],{"class":64,"line":101},[62,73682,1789],{"class":72},[62,73684,915],{"class":1780},[62,73686,119],{"class":122},[62,73688,146],{"class":72},[62,73690,73691],{"class":1675},"\"comment-list\"",[62,73693,1784],{"class":72},[62,73695,73696,73698,73700,73702,73704,73707,73709,73711,73713,73715,73718,73721,73723,73726,73728,73730,73733,73736,73739,73741,73744,73746,73748,73750],{"class":64,"line":107},[62,73697,1896],{"class":72},[62,73699,37],{"class":1780},[62,73701,119],{"class":122},[62,73703,146],{"class":72},[62,73705,73706],{"class":1675},"\"comment\"",[62,73708,68460],{"class":72},[62,73710,653],{"class":1780},[62,73712,119],{"class":122},[62,73714,146],{"class":72},[62,73716,73717],{"class":1675},"\"avatar pull-left\"",[62,73719,73720],{"class":122}," title",[62,73722,146],{"class":72},[62,73724,73725],{"class":1675},"\"${comment.name}\"",[62,73727,15849],{"class":122},[62,73729,146],{"class":72},[62,73731,73732],{"class":1675},"\"http://www.gravatar.com/avatar/${comment.emailHash}?s=64",[62,73734,73735],{"class":149},"&",[62,73737,73738],{"class":1675},"r=pg",[62,73740,73735],{"class":149},[62,73742,73743],{"class":1675},"d=mm\"",[62,73745,30619],{"class":122},[62,73747,146],{"class":72},[62,73749,25895],{"class":1675},[62,73751,67133],{"class":72},[62,73753,73754,73756,73758,73760,73762,73765,73768,73770,73772,73774,73777,73779,73781,73783,73786,73788,73790,73792],{"class":64,"line":113},[62,73755,1905],{"class":72},[62,73757,15944],{"class":1780},[62,73759,119],{"class":122},[62,73761,146],{"class":72},[62,73763,73764],{"class":1675},"\"comment-author\"",[62,73766,73767],{"class":72},">${comment.name} \u003C",[62,73769,677],{"class":1780},[62,73771,73720],{"class":122},[62,73773,146],{"class":72},[62,73775,73776],{"class":1675},"\"permalink\"",[62,73778,16494],{"class":122},[62,73780,146],{"class":72},[62,73782,30687],{"class":1675},[62,73784,73785],{"class":72},">#\u003C/",[62,73787,677],{"class":1780},[62,73789,15857],{"class":72},[62,73791,15944],{"class":1780},[62,73793,1784],{"class":72},[62,73795,73796,73798,73800,73802,73804,73807,73810,73812],{"class":64,"line":129},[62,73797,1905],{"class":72},[62,73799,15944],{"class":1780},[62,73801,119],{"class":122},[62,73803,146],{"class":72},[62,73805,73806],{"class":1675},"\"cmeta\"",[62,73808,73809],{"class":72},">Commented on ${comment.dateCreated.format('MM/dd/yyyy hh:mm')}\u003C/",[62,73811,15944],{"class":1780},[62,73813,1784],{"class":72},[62,73815,73816],{"class":64,"line":134},[62,73817,73818],{"class":72}," ${comment.content}\n",[62,73820,73821,73823,73825,73827,73829,73832,73834,73836],{"class":64,"line":156},[62,73822,1905],{"class":72},[62,73824,15944],{"class":1780},[62,73826,119],{"class":122},[62,73828,146],{"class":72},[62,73830,73831],{"class":1675},"\"clearfix\"",[62,73833,15857],{"class":72},[62,73835,15944],{"class":1780},[62,73837,1784],{"class":72},[62,73839,73840,73842,73844],{"class":64,"line":161},[62,73841,1973],{"class":72},[62,73843,37],{"class":1780},[62,73845,1784],{"class":72},[62,73847,73848,73850,73852],{"class":64,"line":167},[62,73849,1982],{"class":72},[62,73851,915],{"class":1780},[62,73853,1784],{"class":72},[62,73855,73856,73858,73860],{"class":64,"line":173},[62,73857,1818],{"class":72},[62,73859,15944],{"class":1780},[62,73861,1784],{"class":72},[62,73863,73864,73866,73868,73870,73872,73875,73877,73879],{"class":64,"line":179},[62,73865,760],{"class":72},[62,73867,15944],{"class":1780},[62,73869,119],{"class":122},[62,73871,146],{"class":72},[62,73873,73874],{"class":1675},"\"clear\"",[62,73876,15857],{"class":72},[62,73878,15944],{"class":1780},[62,73880,1784],{"class":72},[1527,73882,73883],{},"html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}html pre.shiki code .s4-Ip, html code.shiki .s4-Ip{--shiki-default:#6A737D;--shiki-dark:#6A737D;--shiki-sepia:#6A737D}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .suaqH, html code.shiki .suaqH{--shiki-default:#22863A;--shiki-dark:#85E89D;--shiki-sepia:#22863A}",{"title":57,"searchDepth":76,"depth":76,"links":73885},[],{"_id":73887,"path":73888,"title":73889,"description":73890,"meta":73891,"body":73895},"content/blog/2014/03/31/grails-generating-links-in-your-domain-class.md","/blog/2014/03/31/grails-generating-links-in-your-domain-class","Grails: Generating links in your domain class","Grails: Generating Links in your Domain Class",{"slug":73892,"date":73893,"published":13,"tags":73894,"author":-1,"cover":73331,"excerpt":-1},"grails-generating-links-in-your-domain-class","2014-03-31T09:03:00-04:00",[73330],{"type":19,"value":73896,"toc":74501},[73897,73900,73905,73908,74063,74066,74149,74158,74252,74255,74414,74417,74498],[22,73898,73899],{},"I am working on coverting this site over to a grails applicaiton. While there are some good plugins out there I could of taken advantage of I decided to use this as a learning experience and write a ton of it from scratch. For the blog post itself we have some unique URLs that look like this",[29685,73901,73902],{},[22,73903,73904],{},"/blog/2014/3/30/Grails-views-Controller-and-Action-name",[22,73906,73907],{},"This converts to /blog/year/month/day/slug. So first thing I needed to was setup a url mapping for this path. I also put some constraints on the parameters so we get exactly what we are looking for.",[52,73909,73911],{"className":73062,"code":73910,"language":53536,"meta":57,"style":57},"// blog post\n\"/blog/$year?/$month?/$day?/$slug?\"(controller:\"blog\", action:\"show\") {\n constraints {\n year matches:/\\\\d{4}/\n month matches:/\\\\d{1,2}/\n day matches:/\\\\d{1,2}/\n slug matches: /^\\[a-z0-9-\\]+$/\n }\n}\n",[59,73912,73913,73918,73966,73971,73993,74011,74028,74055,74059],{"__ignoreMap":57},[62,73914,73915],{"class":64,"line":65},[62,73916,73917],{"class":85},"// blog post\n",[62,73919,73920,73923,73926,73929,73932,73934,73937,73939,73942,73945,73947,73949,73951,73954,73956,73959,73961,73964],{"class":64,"line":76},[62,73921,73922],{"class":1675},"\"/blog/",[62,73924,73925],{"class":72},"$year",[62,73927,73928],{"class":1675},"?/",[62,73930,73931],{"class":72},"$month",[62,73933,73928],{"class":1675},[62,73935,73936],{"class":72},"$day",[62,73938,73928],{"class":1675},[62,73940,73941],{"class":72},"$slug",[62,73943,73944],{"class":1675},"?\"",[62,73946,2109],{"class":72},[62,73948,48327],{"class":149},[62,73950,1266],{"class":72},[62,73952,73953],{"class":1675},"\"blog\"",[62,73955,976],{"class":72},[62,73957,73958],{"class":149},"action",[62,73960,1266],{"class":72},[62,73962,73963],{"class":1675},"\"show\"",[62,73965,768],{"class":72},[62,73967,73968],{"class":64,"line":82},[62,73969,73970],{"class":72}," constraints {\n",[62,73972,73973,73976,73979,73981,73983,73986,73990],{"class":64,"line":89},[62,73974,73975],{"class":72}," year ",[62,73977,73978],{"class":149},"matches",[62,73980,1266],{"class":72},[62,73982,6936],{"class":1675},[62,73984,6632],{"class":73985},"sIS5D",[62,73987,73989],{"class":73988},"sLcMm","d{4}",[62,73991,73992],{"class":1675},"/\n",[62,73994,73995,73998,74000,74002,74004,74006,74009],{"class":64,"line":95},[62,73996,73997],{"class":72}," month ",[62,73999,73978],{"class":149},[62,74001,1266],{"class":72},[62,74003,6936],{"class":1675},[62,74005,6632],{"class":73985},[62,74007,74008],{"class":73988},"d{1,2}",[62,74010,73992],{"class":1675},[62,74012,74013,74016,74018,74020,74022,74024,74026],{"class":64,"line":101},[62,74014,74015],{"class":72}," day ",[62,74017,73978],{"class":149},[62,74019,1266],{"class":72},[62,74021,6936],{"class":1675},[62,74023,6632],{"class":73985},[62,74025,74008],{"class":73988},[62,74027,73992],{"class":1675},[62,74029,74030,74033,74035,74037,74039,74042,74044,74047,74050,74053],{"class":64,"line":107},[62,74031,74032],{"class":72}," slug ",[62,74034,73978],{"class":149},[62,74036,3696],{"class":72},[62,74038,6936],{"class":1675},[62,74040,74041],{"class":73988},"^",[62,74043,70824],{"class":73985},[62,74045,74046],{"class":73988},"a-z0-9-",[62,74048,74049],{"class":73985},"\\]",[62,74051,74052],{"class":73988},"+$",[62,74054,73992],{"class":1675},[62,74056,74057],{"class":64,"line":113},[62,74058,223],{"class":72},[62,74060,74061],{"class":64,"line":129},[62,74062,379],{"class":72},[22,74064,74065],{},"Now in our controller or event our views I could do something like this to create a link to that blog post. This is going to be a big pain every single time I need to link to a post.",[52,74067,74069],{"className":15773,"code":74068,"language":15775,"meta":57,"style":57},"\u003Cdiv class=\"blog\">\n \u003Cdiv class=\"entry\">\n \u003Ch2>\u003Ca href=\"${createLink(uri:\"> ${post.title} \u003C/a>\u003C/h2>\n ${raw(post.teaser)} ${raw(post.content)}\n \u003C/div>\n\u003C/div>\n",[59,74070,74071,74085,74100,74128,74133,74141],{"__ignoreMap":57},[62,74072,74073,74075,74077,74079,74081,74083],{"class":64,"line":65},[62,74074,760],{"class":72},[62,74076,15944],{"class":1780},[62,74078,119],{"class":122},[62,74080,146],{"class":72},[62,74082,73953],{"class":1675},[62,74084,1784],{"class":72},[62,74086,74087,74089,74091,74093,74095,74098],{"class":64,"line":76},[62,74088,1789],{"class":72},[62,74090,15944],{"class":1780},[62,74092,119],{"class":122},[62,74094,146],{"class":72},[62,74096,74097],{"class":1675},"\"entry\"",[62,74099,1784],{"class":72},[62,74101,74102,74104,74106,74108,74110,74112,74114,74117,74120,74122,74124,74126],{"class":64,"line":82},[62,74103,1789],{"class":72},[62,74105,26],{"class":1780},[62,74107,68460],{"class":72},[62,74109,677],{"class":1780},[62,74111,16494],{"class":122},[62,74113,146],{"class":72},[62,74115,74116],{"class":1675},"\"${createLink(uri:\"",[62,74118,74119],{"class":72},"> ${post.title} \u003C/",[62,74121,677],{"class":1780},[62,74123,15857],{"class":72},[62,74125,26],{"class":1780},[62,74127,1784],{"class":72},[62,74129,74130],{"class":64,"line":89},[62,74131,74132],{"class":72}," ${raw(post.teaser)} ${raw(post.content)}\n",[62,74134,74135,74137,74139],{"class":64,"line":95},[62,74136,1982],{"class":72},[62,74138,15944],{"class":1780},[62,74140,1784],{"class":72},[62,74142,74143,74145,74147],{"class":64,"line":101},[62,74144,1818],{"class":72},[62,74146,15944],{"class":1780},[62,74148,1784],{"class":72},[22,74150,74151,74152,74157],{},"So i set out to find an easier way to do this. It seems like the post object itself should know how to create its own link. So wouldn't it be nice if we could call post.createPermaLink()? I think that would be great so let's do it. First off we have no access to that createLink method in our domain class. We do however have ability to inject the ",[677,74153,74156],{"href":74154,"rel":74155},"http://grails.org/doc/2.0.x/api/org/codehaus/groovy/grails/web/mapping/LinkGenerator.html",[681],"LinkGenerator"," class and this class has a method called link that will do exactly what we are looking for. First thing we need to do is inject that class into our domain object as a transient property.",[52,74159,74161],{"className":73062,"code":74160,"language":53536,"meta":57,"style":57},"package com.vega.blog\n\nimport com.vega.auth.User\n\nimport org.grails.comments.Commentable\nimport org.grails.taggable.Taggable\nimport org.codehaus.groovy.grails.web.mapping.LinkGenerator\n\nclass Post implements Commentable,Taggable {\n\n static transient LinkGenerator grailsLinkGenerator\n\n}\n",[59,74162,74163,74169,74173,74180,74184,74191,74198,74205,74209,74227,74231,74244,74248],{"__ignoreMap":57},[62,74164,74165,74167],{"class":64,"line":65},[62,74166,69],{"class":68},[62,74168,73382],{"class":72},[62,74170,74171],{"class":64,"line":76},[62,74172,79],{"emptyLinePlaceholder":13},[62,74174,74175,74177],{"class":64,"line":82},[62,74176,27875],{"class":68},[62,74178,74179],{"class":72}," com.vega.auth.User\n",[62,74181,74182],{"class":64,"line":89},[62,74183,79],{"emptyLinePlaceholder":13},[62,74185,74186,74188],{"class":64,"line":95},[62,74187,27875],{"class":68},[62,74189,74190],{"class":72}," org.grails.comments.Commentable\n",[62,74192,74193,74195],{"class":64,"line":101},[62,74194,27875],{"class":68},[62,74196,74197],{"class":72}," org.grails.taggable.Taggable\n",[62,74199,74200,74202],{"class":64,"line":107},[62,74201,27875],{"class":68},[62,74203,74204],{"class":72}," org.codehaus.groovy.grails.web.mapping.LinkGenerator\n",[62,74206,74207],{"class":64,"line":113},[62,74208,79],{"emptyLinePlaceholder":13},[62,74210,74211,74213,74215,74217,74220,74222,74225],{"class":64,"line":129},[62,74212,11671],{"class":68},[62,74214,41321],{"class":122},[62,74216,13520],{"class":68},[62,74218,74219],{"class":122}," Commentable",[62,74221,32225],{"class":72},[62,74223,74224],{"class":122},"Taggable",[62,74226,126],{"class":72},[62,74228,74229],{"class":64,"line":134},[62,74230,79],{"emptyLinePlaceholder":13},[62,74232,74233,74235,74238,74241],{"class":64,"line":156},[62,74234,70061],{"class":68},[62,74236,74237],{"class":68}," transient",[62,74239,74240],{"class":68}," LinkGenerator",[62,74242,74243],{"class":72}," grailsLinkGenerator\n",[62,74245,74246],{"class":64,"line":161},[62,74247,79],{"emptyLinePlaceholder":13},[62,74249,74250],{"class":64,"line":167},[62,74251,379],{"class":72},[22,74253,74254],{},"Now we can create out method and use this class to generate this link for us.",[52,74256,74258],{"className":73062,"code":74257,"language":53536,"meta":57,"style":57},"/\\*\\*\n \\* Utility method that will handle creating a link to this post\n \\* @return String\n \\*/\nString createPermalink(){\n def year = dateCreated\\[Calendar.YEAR\\]\n def month = dateCreated\\[Calendar.MONTH\\]\n def day = dateCreated\\[Calendar.DATE\\]\n\n grailsLinkGenerator.link(uri:\"/blog/$year/$month/$day/$slug\")\n}\n",[59,74259,74260,74272,74289,74301,74307,74316,74336,74354,74372,74376,74410],{"__ignoreMap":57},[62,74261,74262,74264,74266,74268,74270],{"class":64,"line":65},[62,74263,6936],{"class":68},[62,74265,72200],{"class":72},[62,74267,6973],{"class":68},[62,74269,72200],{"class":72},[62,74271,26612],{"class":68},[62,74273,74274,74276,74278,74281,74284,74286],{"class":64,"line":76},[62,74275,72211],{"class":72},[62,74277,6973],{"class":68},[62,74279,74280],{"class":68}," Utility",[62,74282,74283],{"class":72}," method that will handle creating a link to ",[62,74285,1295],{"class":149},[62,74287,74288],{"class":72}," post\n",[62,74290,74291,74293,74295,74298],{"class":64,"line":82},[62,74292,72211],{"class":72},[62,74294,6973],{"class":68},[62,74296,74297],{"class":68}," @return",[62,74299,74300],{"class":68}," String\n",[62,74302,74303,74305],{"class":64,"line":89},[62,74304,72211],{"class":72},[62,74306,72372],{"class":68},[62,74308,74309,74311,74314],{"class":64,"line":95},[62,74310,973],{"class":68},[62,74312,74313],{"class":122}," createPermalink",[62,74315,57791],{"class":72},[62,74317,74318,74320,74323,74325,74328,74331,74334],{"class":64,"line":101},[62,74319,73555],{"class":68},[62,74321,74322],{"class":72}," year ",[62,74324,146],{"class":68},[62,74326,74327],{"class":72}," dateCreated\\[",[62,74329,74330],{"class":68},"Calendar.",[62,74332,74333],{"class":149},"YEAR",[62,74335,50699],{"class":72},[62,74337,74338,74340,74343,74345,74347,74349,74352],{"class":64,"line":107},[62,74339,73555],{"class":68},[62,74341,74342],{"class":72}," month ",[62,74344,146],{"class":68},[62,74346,74327],{"class":72},[62,74348,74330],{"class":68},[62,74350,74351],{"class":149},"MONTH",[62,74353,50699],{"class":72},[62,74355,74356,74358,74361,74363,74365,74367,74370],{"class":64,"line":113},[62,74357,73555],{"class":68},[62,74359,74360],{"class":72}," day ",[62,74362,146],{"class":68},[62,74364,74327],{"class":72},[62,74366,74330],{"class":68},[62,74368,74369],{"class":149},"DATE",[62,74371,50699],{"class":72},[62,74373,74374],{"class":64,"line":129},[62,74375,79],{"emptyLinePlaceholder":13},[62,74377,74378,74381,74383,74386,74388,74390,74392,74394,74396,74398,74400,74402,74404,74406,74408],{"class":64,"line":134},[62,74379,74380],{"class":72}," grailsLinkGenerator",[62,74382,2755],{"class":68},[62,74384,74385],{"class":72},"link(",[62,74387,11372],{"class":149},[62,74389,1266],{"class":72},[62,74391,73922],{"class":1675},[62,74393,73925],{"class":72},[62,74395,6936],{"class":1675},[62,74397,73931],{"class":72},[62,74399,6936],{"class":1675},[62,74401,73936],{"class":72},[62,74403,6936],{"class":1675},[62,74405,73941],{"class":72},[62,74407,4498],{"class":1675},[62,74409,2212],{"class":72},[62,74411,74412],{"class":64,"line":156},[62,74413,379],{"class":72},[22,74415,74416],{},"Finally in our view we have an easy way to generate that link. Pretty cool right?",[52,74418,74420],{"className":15773,"code":74419,"language":15775,"meta":57,"style":57},"\u003Cdiv class=\"blog\">\n \u003Cdiv class=\"entry\">\n \u003Ch2>\u003Ca href=\"${post.createPermalink()}\">${post.title}\u003C/a>\u003C/h2>\n ${raw(post.teaser)} ${raw(post.content)}\n \u003C/div>\n\u003C/div>\n",[59,74421,74422,74436,74450,74478,74482,74490],{"__ignoreMap":57},[62,74423,74424,74426,74428,74430,74432,74434],{"class":64,"line":65},[62,74425,760],{"class":72},[62,74427,15944],{"class":1780},[62,74429,119],{"class":122},[62,74431,146],{"class":72},[62,74433,73953],{"class":1675},[62,74435,1784],{"class":72},[62,74437,74438,74440,74442,74444,74446,74448],{"class":64,"line":76},[62,74439,1789],{"class":72},[62,74441,15944],{"class":1780},[62,74443,119],{"class":122},[62,74445,146],{"class":72},[62,74447,74097],{"class":1675},[62,74449,1784],{"class":72},[62,74451,74452,74454,74456,74458,74460,74462,74464,74467,74470,74472,74474,74476],{"class":64,"line":82},[62,74453,1896],{"class":72},[62,74455,26],{"class":1780},[62,74457,68460],{"class":72},[62,74459,677],{"class":1780},[62,74461,16494],{"class":122},[62,74463,146],{"class":72},[62,74465,74466],{"class":1675},"\"${post.createPermalink()}\"",[62,74468,74469],{"class":72},">${post.title}\u003C/",[62,74471,677],{"class":1780},[62,74473,15857],{"class":72},[62,74475,26],{"class":1780},[62,74477,1784],{"class":72},[62,74479,74480],{"class":64,"line":89},[62,74481,74132],{"class":72},[62,74483,74484,74486,74488],{"class":64,"line":95},[62,74485,1982],{"class":72},[62,74487,15944],{"class":1780},[62,74489,1784],{"class":72},[62,74491,74492,74494,74496],{"class":64,"line":101},[62,74493,1818],{"class":72},[62,74495,15944],{"class":1780},[62,74497,1784],{"class":72},[1527,74499,74500],{},"html pre.shiki code .s4-Ip, html code.shiki .s4-Ip{--shiki-default:#6A737D;--shiki-dark:#6A737D;--shiki-sepia:#6A737D}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}html pre.shiki code .sIS5D, html code.shiki .sIS5D{--shiki-default:#22863A;--shiki-default-font-weight:bold;--shiki-dark:#85E89D;--shiki-dark-font-weight:bold;--shiki-sepia:#22863A;--shiki-sepia-font-weight:bold}html pre.shiki code .sLcMm, html code.shiki .sLcMm{--shiki-default:#032F62;--shiki-dark:#DBEDFF;--shiki-sepia:#032F62}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .suaqH, html code.shiki .suaqH{--shiki-default:#22863A;--shiki-dark:#85E89D;--shiki-sepia:#22863A}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}",{"title":57,"searchDepth":76,"depth":76,"links":74502},[],{"_id":74504,"path":74505,"title":74506,"description":74507,"meta":74508,"body":74512},"content/blog/2014/02/25/grails-spring-security-plugin-logout-postonly-setting.md","/blog/2014/02/25/grails-spring-security-plugin-logout-postonly-setting","Grails Spring Security Plugin - Logout postOnly setting","Grails Spring Security Plugin - Logout PostOnly Setting",{"slug":74509,"date":74510,"published":13,"tags":74511,"author":-1,"cover":73331,"excerpt":-1},"grails-spring-security-plugin-logout-postonly-setting","2014-02-25T13:02:00-05:00",[73330],{"type":19,"value":74513,"toc":74713},[74514,74517,74522,74525,74667,74670,74695,74704,74710],[22,74515,74516],{},"I had a question come in about a setting in Spring Security so I thought I would take a quick minute and explain it in case anyone else also has the same question. There is a setting",[29685,74518,74519],{},[22,74520,74521],{},"'grails.plugin.springsecurity.logout.postOnly = true'",[22,74523,74524],{},"that is true by default. If you look at the LogoutController's index action this make a little more sense.",[52,74526,74528],{"className":54,"code":74527,"language":56,"meta":57,"style":57},"@Secured('permitAll')\nclass LogoutController {\n\n /\\*\\*\n \\* Index action. Redirects to the Spring security logout uri.\n \\*/\n def index() {\n\n if (!request.post && SpringSecurityUtils.getSecurityConfig().logout.postOnly) {\n response.sendError HttpServletResponse.SC\\_METHOD\\_NOT\\_ALLOWED // 405\n return\n }\n\n // TODO put any pre-logout code here\n redirect uri: SpringSecurityUtils.securityConfig.logout.filterProcessesUrl // '/j\\_spring\\_security\\_logout'\n }\n}\n",[59,74529,74530,74544,74553,74557,74569,74578,74584,74593,74597,74620,74628,74633,74637,74641,74646,74659,74663],{"__ignoreMap":57},[62,74531,74532,74534,74537,74539,74542],{"class":64,"line":65},[62,74533,942],{"class":72},[62,74535,74536],{"class":68},"Secured",[62,74538,2109],{"class":72},[62,74540,74541],{"class":1675},"'permitAll'",[62,74543,2212],{"class":72},[62,74545,74546,74548,74551],{"class":64,"line":76},[62,74547,11671],{"class":68},[62,74549,74550],{"class":122}," LogoutController",[62,74552,126],{"class":72},[62,74554,74555],{"class":64,"line":82},[62,74556,79],{"emptyLinePlaceholder":13},[62,74558,74559,74561,74563,74565,74567],{"class":64,"line":89},[62,74560,72444],{"class":68},[62,74562,72200],{"class":72},[62,74564,6973],{"class":68},[62,74566,72200],{"class":72},[62,74568,26612],{"class":68},[62,74570,74571,74573,74575],{"class":64,"line":95},[62,74572,72457],{"class":72},[62,74574,6973],{"class":68},[62,74576,74577],{"class":72}," Index action. Redirects to the Spring security logout uri.\n",[62,74579,74580,74582],{"class":64,"line":101},[62,74581,72457],{"class":72},[62,74583,72372],{"class":68},[62,74585,74586,74589,74591],{"class":64,"line":107},[62,74587,74588],{"class":72}," def ",[62,74590,22472],{"class":122},[62,74592,206],{"class":72},[62,74594,74595],{"class":64,"line":113},[62,74596,79],{"emptyLinePlaceholder":13},[62,74598,74599,74601,74603,74605,74608,74611,74614,74617],{"class":64,"line":129},[62,74600,12741],{"class":68},[62,74602,744],{"class":72},[62,74604,6277],{"class":68},[62,74606,74607],{"class":72},"request.post ",[62,74609,74610],{"class":68},"&&",[62,74612,74613],{"class":72}," SpringSecurityUtils.",[62,74615,74616],{"class":122},"getSecurityConfig",[62,74618,74619],{"class":72},"().logout.postOnly) {\n",[62,74621,74622,74625],{"class":64,"line":134},[62,74623,74624],{"class":72}," response.sendError HttpServletResponse.SC\\_METHOD\\_NOT\\_ALLOWED ",[62,74626,74627],{"class":85},"// 405\n",[62,74629,74630],{"class":64,"line":156},[62,74631,74632],{"class":68}," return\n",[62,74634,74635],{"class":64,"line":161},[62,74636,533],{"class":72},[62,74638,74639],{"class":64,"line":167},[62,74640,79],{"emptyLinePlaceholder":13},[62,74642,74643],{"class":64,"line":173},[62,74644,74645],{"class":85}," // TODO put any pre-logout code here\n",[62,74647,74648,74651,74653,74656],{"class":64,"line":179},[62,74649,74650],{"class":72}," redirect uri",[62,74652,1266],{"class":68},[62,74654,74655],{"class":72}," SpringSecurityUtils.securityConfig.logout.filterProcessesUrl ",[62,74657,74658],{"class":85},"// '/j\\_spring\\_security\\_logout'\n",[62,74660,74661],{"class":64,"line":185},[62,74662,223],{"class":72},[62,74664,74665],{"class":64,"line":191},[62,74666,379],{"class":72},[22,74668,74669],{},"All this is saying is that to Logout we must have that request made in the form of a post. An easy way to do that is create a link to the logout controller (remember index is our default action).",[52,74671,74673],{"className":73062,"code":74672,"language":53536,"meta":57,"style":57}," [Logout](${createLink(controller: 'logout')})\n",[59,74674,74675],{"__ignoreMap":57},[62,74676,74677,74679,74682,74685,74687,74689,74692],{"class":64,"line":65},[62,74678,36860],{"class":72},[62,74680,74681],{"class":68},"Logout",[62,74683,74684],{"class":72},"](${createLink(",[62,74686,48327],{"class":149},[62,74688,3696],{"class":72},[62,74690,74691],{"class":1675},"'logout'",[62,74693,74694],{"class":72},")})\n",[22,74696,74697,74698,74703],{},"If you try and just visit the URL ",[677,74699,74702],{"href":74700,"rel":74701},"http://localhost:8080/%7Byour%5C_context%7D/logout",[681],"http://localhost:8080/{your\\_context}/logout"," you can tell by the code that this should throw a 405 error, and it does.",[22,74705,74706],{},[653,74707],{"alt":74708,"src":74709},"405 Error","./405.png",[1527,74711,74712],{},"html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .s4-Ip, html code.shiki .s4-Ip{--shiki-default:#6A737D;--shiki-dark:#6A737D;--shiki-sepia:#6A737D}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}",{"title":57,"searchDepth":76,"depth":76,"links":74714},[],{"_id":74716,"path":74717,"title":74718,"description":74718,"meta":74719,"body":74723},"content/blog/2013/08/27/grails-mysql-boolean-gotcha.md","/blog/2013/08/27/grails-mysql-boolean-gotcha","Grails MySQL Boolean Gotcha",{"slug":74720,"date":74721,"published":13,"tags":74722,"author":-1,"cover":73331,"excerpt":-1},"grails-mysql-boolean-gotcha","2013-08-27T18:08:00-04:00",[73330,53536],{"type":19,"value":74724,"toc":75125},[74725,74740,74745,74748,74889,74892,74897,74902,74907,74916,74921,74924,74933,75021,75024,75111,75117,75122],[22,74726,74727,74728,74733,74734,74739],{},"In the small app I am writing for fun I decided that it was time to switch over to a MySQL database. It was suprisingly easy to get Grails to talk to this new database. I won't go over every little step because ",[677,74729,74732],{"href":74730,"rel":74731},"http://compiledammit.com/2012/08/12/connecting-grails-to-mysql-and-others/",[681],"Joe Rinehart already wrote up a nice guide for connecting grails to mysql",". One comment I will make on that article and this was mentioned in the comments is that you can avoid downloading the mysql drivers and having to drop it in the lib folder. Go into grails-app/conf/BuildConfig.groovy & under dependencies uncomment the following line. Grails will then ",[677,74735,74738],{"href":74736,"rel":74737},"http://search.maven.org/#browse",[681],"go out and download the dependency"," for you.",[29685,74741,74742],{},[22,74743,74744],{},"runtime 'mysql:mysql-connector-java:5.1.22'",[22,74746,74747],{},"So now that we have MySQL up and running we can take a look at the big GOTCHA I ran into tonight. I have a domain class (many actually) that define an active flag (boolean). If we take a look at my Brand domain you will see our boolean active property. This is meant as a flag to tell if the Brand should be active or not.",[52,74749,74751],{"className":73062,"code":74750,"language":53536,"meta":57,"style":57},"package com.vega.sneakerhead\n\nclass Brand {\n\n String name\n boolean active = true\n Date dateCreated\n Date lastUpdated\n\n static hasMany = \\[shoes:Shoe\\]\n\n static constraints = {\n name blank:false\n }\n\n String toString() {\n \"${name}\"\n }\n}\n",[59,74752,74753,74760,74764,74773,74777,74783,74795,74801,74808,74812,74833,74837,74847,74859,74863,74867,74876,74881,74885],{"__ignoreMap":57},[62,74754,74755,74757],{"class":64,"line":65},[62,74756,69],{"class":68},[62,74758,74759],{"class":72}," com.vega.sneakerhead\n",[62,74761,74762],{"class":64,"line":76},[62,74763,79],{"emptyLinePlaceholder":13},[62,74765,74766,74768,74771],{"class":64,"line":82},[62,74767,11671],{"class":68},[62,74769,74770],{"class":122}," Brand",[62,74772,126],{"class":72},[62,74774,74775],{"class":64,"line":89},[62,74776,79],{"emptyLinePlaceholder":13},[62,74778,74779,74781],{"class":64,"line":95},[62,74780,73404],{"class":68},[62,74782,73407],{"class":72},[62,74784,74785,74788,74791,74793],{"class":64,"line":101},[62,74786,74787],{"class":68}," boolean",[62,74789,74790],{"class":72}," active ",[62,74792,146],{"class":68},[62,74794,69203],{"class":149},[62,74796,74797,74799],{"class":64,"line":107},[62,74798,73452],{"class":68},[62,74800,73455],{"class":72},[62,74802,74803,74805],{"class":64,"line":113},[62,74804,73452],{"class":68},[62,74806,74807],{"class":72}," lastUpdated\n",[62,74809,74810],{"class":64,"line":129},[62,74811,79],{"emptyLinePlaceholder":13},[62,74813,74814,74816,74819,74821,74823,74826,74828,74831],{"class":64,"line":134},[62,74815,70061],{"class":68},[62,74817,74818],{"class":72}," hasMany ",[62,74820,146],{"class":68},[62,74822,58153],{"class":72},[62,74824,74825],{"class":149},"shoes",[62,74827,1266],{"class":72},[62,74829,74830],{"class":68},"Shoe",[62,74832,50699],{"class":72},[62,74834,74835],{"class":64,"line":156},[62,74836,79],{"emptyLinePlaceholder":13},[62,74838,74839,74841,74843,74845],{"class":64,"line":161},[62,74840,70061],{"class":68},[62,74842,73489],{"class":72},[62,74844,146],{"class":68},[62,74846,126],{"class":72},[62,74848,74849,74852,74855,74857],{"class":64,"line":167},[62,74850,74851],{"class":72}," name ",[62,74853,74854],{"class":149},"blank",[62,74856,1266],{"class":72},[62,74858,40782],{"class":149},[62,74860,74861],{"class":64,"line":173},[62,74862,223],{"class":72},[62,74864,74865],{"class":64,"line":179},[62,74866,79],{"emptyLinePlaceholder":13},[62,74868,74869,74871,74874],{"class":64,"line":185},[62,74870,73404],{"class":68},[62,74872,74873],{"class":122}," toString",[62,74875,206],{"class":72},[62,74877,74878],{"class":64,"line":191},[62,74879,74880],{"class":1675}," \"${name}\"\n",[62,74882,74883],{"class":64,"line":209},[62,74884,223],{"class":72},[62,74886,74887],{"class":64,"line":220},[62,74888,379],{"class":72},[22,74890,74891],{},"After I was done with the domain model I fired up my application with some default data getting created in the Bootstrap. When I looked at the table structure and content this is what I saw.",[22,74893,74894],{},[653,74895],{"alt":57,"src":74896},"./active_design.png",[22,74898,74899],{},[653,74900],{"alt":57,"src":74901},"./active_content.png",[22,74903,74904],{},[653,74905],{"alt":57,"src":74906},"./active_mysqlbrowser.png",[22,74908,74909,74910,74915],{},"The problem with this is that our boolean is actually mapping to a BIT column type where we actually want it to be a TINYINT(1). This is actually a ",[677,74911,74914],{"href":74912,"rel":74913},"https://hibernate.atlassian.net/browse/HHH-468",[681],"bug that you can read about right here",". If you don't feel like reading the entire JIRA ticket this is the main take away from it.",[29685,74917,74918],{},[22,74919,74920],{},"I didn't track down how java.lang.Boolean gets mapped to Types.BIT in hibernate, but you probably don't want to map to \"bit\" like you do in MysqlDialect.",[22,74922,74923],{},"It looks like they fixed this issue in Hibernate 4.0 but the current version that ships with Grails is still 3.6. If anyone reading this knows when the plugin will update to 4.0 can you please post it in the comments. (Thank You)",[22,74925,74926,74927,74932],{},"The good news is that there is a solution to this and I want to say a big thank you to ",[677,74928,74931],{"href":74929,"rel":74930},"https://twitter.com/burtbeckwith",[681],"Burt Beckwith"," for pointing me to it. What we are going to do is create our custom dialect. It might sound a little scary but I promise you its really easy. The first step is to create the following class in src/groovy/ and whatever package and class name you want. All we are doing here is extending the base dialect class and in the constructor registering a new column type to correctly map our BIT type to a boolean and not a bit.",[52,74934,74936],{"className":73062,"code":74935,"language":53536,"meta":57,"style":57},"package com.vega.sneakerhead\n\nimport org.hibernate.dialect.MySQL5InnoDBDialect\nimport java.sql.Types\n\nclass MyCustomMySQL5InnoDBDialect extends MySQL5InnoDBDialect {\n\n MyCustomMySQL5InnoDBDialect() {\n registerColumnType(Types.BIT, 'boolean')\n }\n\n}\n",[59,74937,74938,74944,74948,74955,74962,74966,74980,74984,74991,75009,75013,75017],{"__ignoreMap":57},[62,74939,74940,74942],{"class":64,"line":65},[62,74941,69],{"class":68},[62,74943,74759],{"class":72},[62,74945,74946],{"class":64,"line":76},[62,74947,79],{"emptyLinePlaceholder":13},[62,74949,74950,74952],{"class":64,"line":82},[62,74951,27875],{"class":68},[62,74953,74954],{"class":72}," org.hibernate.dialect.MySQL5InnoDBDialect\n",[62,74956,74957,74959],{"class":64,"line":89},[62,74958,27875],{"class":68},[62,74960,74961],{"class":72}," java.sql.Types\n",[62,74963,74964],{"class":64,"line":95},[62,74965,79],{"emptyLinePlaceholder":13},[62,74967,74968,74970,74973,74975,74978],{"class":64,"line":101},[62,74969,11671],{"class":68},[62,74971,74972],{"class":122}," MyCustomMySQL5InnoDBDialect",[62,74974,8537],{"class":68},[62,74976,74977],{"class":122}," MySQL5InnoDBDialect",[62,74979,126],{"class":72},[62,74981,74982],{"class":64,"line":107},[62,74983,79],{"emptyLinePlaceholder":13},[62,74985,74986,74989],{"class":64,"line":113},[62,74987,74988],{"class":122}," MyCustomMySQL5InnoDBDialect",[62,74990,206],{"class":72},[62,74992,74993,74996,74999,75002,75004,75007],{"class":64,"line":129},[62,74994,74995],{"class":72}," registerColumnType(",[62,74997,74998],{"class":68},"Types.",[62,75000,75001],{"class":149},"BIT",[62,75003,976],{"class":72},[62,75005,75006],{"class":1675},"'boolean'",[62,75008,2212],{"class":72},[62,75010,75011],{"class":64,"line":134},[62,75012,223],{"class":72},[62,75014,75015],{"class":64,"line":156},[62,75016,79],{"emptyLinePlaceholder":13},[62,75018,75019],{"class":64,"line":161},[62,75020,379],{"class":72},[22,75022,75023],{},"Open up grails-app/conf/DataSource.groovy and add your new custom dialect to whatever environment you're in.",[52,75025,75027],{"className":73062,"code":75026,"language":53536,"meta":57,"style":57},"development {\n dataSource {\n dbCreate = \"create-drop\"\n url = \"jdbc:mysql://localhost:3306/sneakerhead\"\n username = \"sneakerhead\"\n password = \"\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\"\n dialect = com.vega.sneakerhead.MyCustomMySQL5InnoDBDialect\n logsql = true\n }\n}\n",[59,75028,75029,75034,75039,75049,75058,75068,75084,75094,75103,75107],{"__ignoreMap":57},[62,75030,75031],{"class":64,"line":65},[62,75032,75033],{"class":72},"development {\n",[62,75035,75036],{"class":64,"line":76},[62,75037,75038],{"class":72}," dataSource {\n",[62,75040,75041,75044,75046],{"class":64,"line":82},[62,75042,75043],{"class":72}," dbCreate ",[62,75045,146],{"class":68},[62,75047,75048],{"class":1675}," \"create-drop\"\n",[62,75050,75051,75053,75055],{"class":64,"line":89},[62,75052,54083],{"class":72},[62,75054,146],{"class":68},[62,75056,75057],{"class":1675}," \"jdbc:mysql://localhost:3306/sneakerhead\"\n",[62,75059,75060,75063,75065],{"class":64,"line":95},[62,75061,75062],{"class":72}," username ",[62,75064,146],{"class":68},[62,75066,75067],{"class":1675}," \"sneakerhead\"\n",[62,75069,75070,75073,75075,75078,75081],{"class":64,"line":101},[62,75071,75072],{"class":72}," password ",[62,75074,146],{"class":68},[62,75076,75077],{"class":1675}," \"",[62,75079,75080],{"class":149},"\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*",[62,75082,75083],{"class":1675},"\"\n",[62,75085,75086,75089,75091],{"class":64,"line":107},[62,75087,75088],{"class":72}," dialect ",[62,75090,146],{"class":68},[62,75092,75093],{"class":68}," com.vega.sneakerhead.MyCustomMySQL5InnoDBDialect\n",[62,75095,75096,75099,75101],{"class":64,"line":113},[62,75097,75098],{"class":72}," logsql ",[62,75100,146],{"class":68},[62,75102,69203],{"class":149},[62,75104,75105],{"class":64,"line":129},[62,75106,223],{"class":72},[62,75108,75109],{"class":64,"line":134},[62,75110,379],{"class":72},[22,75112,75113,75114],{},"That is all there is to it. The next time we fire up the up the application we will have that field correctly using a TINYINT type and a value of 1 or 0. ",[653,75115],{"alt":57,"src":75116},"./tinyint_design.png",[22,75118,75119],{},[653,75120],{"alt":57,"src":75121},"./tinyint_content.png",[1527,75123,75124],{},"html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}",{"title":57,"searchDepth":76,"depth":76,"links":75126},[],{"_id":75128,"path":75129,"title":75130,"description":75130,"meta":75131,"body":75135},"content/blog/2013/08/27/grails-interactive-mode-use-it.md","/blog/2013/08/27/grails-interactive-mode-use-it","Grails Interactive Mode - USE IT",{"slug":75132,"date":75133,"published":13,"tags":75134,"author":-1,"cover":73331,"excerpt":-1},"grails-interactive-mode-use-it","2013-08-27T15:08:00-04:00",[73330],{"type":19,"value":75136,"toc":75162},[75137,75140,75143,75148,75151,75156,75159],[22,75138,75139],{},"When you first start playing around with grails you may have the IDE execute commands for you or you may end up running the commands yourself from a shell. I have been running commands in terminal and for the sake of playing around it was fine. Now that I am working on a project and constantly developing running commands using the traditional grails run-app syntax just won't work.",[22,75141,75142],{},"This process is very slow and there is a reason for that. Every single time you run grails {insert_command_here} you are firing up a new JVM. You can use interactive mode by simply by typing the grails command from in your project. The advantage to this is that it keeps the JVM running and allows for quicker execution of commands. Now when you run a command you can omit the grails prefix.",[29685,75144,75145],{},[22,75146,75147],{},"run-app\nstop-app\ncreate-controller\ngenerate-all\netc...",[22,75149,75150],{},"Another great feature about interactive mode is that you get tab completion. Start typing something like create and click tab to see a list of available options. Want to see a list of all commands? Simply hit tab once you are in interactive mode.",[22,75152,75153],{},[653,75154],{"alt":57,"src":75155},"./grails_interactive_mode.png",[22,75157,75158],{},"Finally there are times when you still want to run an external process. I was trying to run clear from the terminal and grails kept complaining that it couldn't find a script with the name clear. Luckily they thought of everything over there and gave us a way to run external processes. Simply start the command with a !. This is ideal for external commands that operate on the file system such as 'clear', 'ls', 'cat', 'git', etc.",[22,75160,75161],{},"Get used to using interactive mode, its quick and intuitive and more fun than waiting for JVMs to load!",{"title":57,"searchDepth":76,"depth":76,"links":75163},[],{"_id":75165,"path":75166,"title":75167,"description":75167,"meta":75168,"body":75172},"content/blog/2013/08/23/intellij-spring-bean-injection-notification.md","/blog/2013/08/23/intellij-spring-bean-injection-notification","IntelliJ Spring Bean Injection Notification",{"slug":75169,"date":75170,"published":13,"tags":75171,"author":-1,"cover":73331,"excerpt":-1},"intellij-spring-bean-injection-notification","2013-08-23T08:08:00-04:00",[73330,53536],{"type":19,"value":75173,"toc":75190},[75174,75177,75180,75185],[22,75175,75176],{},"In my quest to learn Groovy and Grails I have been using Sublime because that is what I am familiar with. I have also been testing out IntelliJ on the recommendation from almost everyone. I have to agree that it is pretty darn awesome and I want to look at one smaller, but pretty cool feature today.",[22,75178,75179],{},"In the following PostController I have declared two properties. Each of these classes have already been instantiated and declared as a bean in spring. So rather than import them and create a new instance I will use what already exists. The cool thing is you can see the 2 bean icons in the gutter. This means that Spring has recognized these as valid beans and will inject them for us. When you hover over them it will let you know that Spring has injected the bean.",[22,75181,75182],{},[653,75183],{"alt":57,"src":75184},"./spring_bean_off.png",[22,75186,75187],{},[653,75188],{"alt":57,"src":75189},"./spring_bean_on.png",{"title":57,"searchDepth":76,"depth":76,"links":75191},[],{"_id":75193,"path":75194,"title":75195,"description":75195,"meta":75196,"body":75200},"content/blog/2013/08/22/groovy-ternary-operator.md","/blog/2013/08/22/groovy-ternary-operator","Groovy Ternary Operator",{"slug":75197,"date":75198,"published":13,"tags":75199,"author":-1,"cover":73331,"excerpt":-1},"groovy-ternary-operator","2013-08-22T13:08:00-04:00",[53536],{"type":19,"value":75201,"toc":75436},[75202,75205,75210,75213,75355,75358,75402,75405,75433],[22,75203,75204],{},"Another one of the great operators that more and more languages support is their ternary operator. The ternary is a conditional operator and often referred to as an inline if statement. Before we look at how to use it lets take a look at a common problem it helps us solve. There are often times when you want to set a variable to one value if an expression evaluates to true and another if its false. In pseudo code it might look something like this.",[29685,75206,75207],{},[22,75208,75209],{},"If expression is true; set value to a else set value to b",[22,75211,75212],{},"Say we are displaying the weather to a user. We know the users state because we require it in our application but maybe they haven't told us exactly what city they live in. In the following examples (ColdFusion & Groovy) we check to see if the city is null. If it is we set the location to the state and if it is not we will use that.",[52,75214,75216],{"className":73062,"code":75215,"language":53536,"meta":57,"style":57},"var location = '';\n\nif ( isNull(user.getCity()) ){\n location = user.getState();\n} else {\n location = user.getState();\n}\n\ndef location\n\nif( user.city == null ){\n location = user.state\n} else {\n location = user.city\n}\n",[59,75217,75218,75229,75233,75245,75260,75268,75280,75284,75288,75295,75299,75317,75330,75338,75351],{"__ignoreMap":57},[62,75219,75220,75223,75225,75227],{"class":64,"line":65},[62,75221,75222],{"class":72},"var location ",[62,75224,146],{"class":68},[62,75226,21087],{"class":1675},[62,75228,153],{"class":72},[62,75230,75231],{"class":64,"line":76},[62,75232,79],{"emptyLinePlaceholder":13},[62,75234,75235,75237,75240,75242],{"class":64,"line":82},[62,75236,34116],{"class":68},[62,75238,75239],{"class":72}," ( isNull(user",[62,75241,2755],{"class":68},[62,75243,75244],{"class":72},"getCity()) ){\n",[62,75246,75247,75250,75252,75255,75257],{"class":64,"line":89},[62,75248,75249],{"class":72}," location ",[62,75251,146],{"class":68},[62,75253,75254],{"class":72}," user",[62,75256,2755],{"class":68},[62,75258,75259],{"class":72},"getState();\n",[62,75261,75262,75264,75266],{"class":64,"line":95},[62,75263,63241],{"class":72},[62,75265,12783],{"class":68},[62,75267,126],{"class":72},[62,75269,75270,75272,75274,75276,75278],{"class":64,"line":101},[62,75271,75249],{"class":72},[62,75273,146],{"class":68},[62,75275,75254],{"class":72},[62,75277,2755],{"class":68},[62,75279,75259],{"class":72},[62,75281,75282],{"class":64,"line":107},[62,75283,379],{"class":72},[62,75285,75286],{"class":64,"line":113},[62,75287,79],{"emptyLinePlaceholder":13},[62,75289,75290,75292],{"class":64,"line":129},[62,75291,73070],{"class":68},[62,75293,75294],{"class":72}," location\n",[62,75296,75297],{"class":64,"line":134},[62,75298,79],{"emptyLinePlaceholder":13},[62,75300,75301,75303,75306,75308,75311,75313,75315],{"class":64,"line":156},[62,75302,34116],{"class":68},[62,75304,75305],{"class":72},"( user",[62,75307,2755],{"class":68},[62,75309,75310],{"class":72},"city ",[62,75312,28328],{"class":68},[62,75314,13324],{"class":149},[62,75316,69394],{"class":72},[62,75318,75319,75321,75323,75325,75327],{"class":64,"line":161},[62,75320,75249],{"class":72},[62,75322,146],{"class":68},[62,75324,75254],{"class":72},[62,75326,2755],{"class":68},[62,75328,75329],{"class":72},"state\n",[62,75331,75332,75334,75336],{"class":64,"line":167},[62,75333,63241],{"class":72},[62,75335,12783],{"class":68},[62,75337,126],{"class":72},[62,75339,75340,75342,75344,75346,75348],{"class":64,"line":173},[62,75341,75249],{"class":72},[62,75343,146],{"class":68},[62,75345,75254],{"class":72},[62,75347,2755],{"class":68},[62,75349,75350],{"class":72},"city\n",[62,75352,75353],{"class":64,"line":179},[62,75354,379],{"class":72},[22,75356,75357],{},"This is fine but its a lot of excess noise that we don't need. Both ColdFusion and Groovy support the ternary operator and I won't show it in both because its pretty easy to understand. If the expression before the ? evaluates to true state will be used and if it evaluates to false (we have a city) then city will be used.",[52,75359,75361],{"className":73062,"code":75360,"language":53536,"meta":57,"style":57},"def location = (user.city == null) ? user.state : user.city\n",[59,75362,75363],{"__ignoreMap":57},[62,75364,75365,75367,75370,75372,75375,75377,75379,75381,75383,75385,75387,75389,75391,75394,75396,75398,75400],{"class":64,"line":65},[62,75366,73070],{"class":68},[62,75368,75369],{"class":72}," location ",[62,75371,146],{"class":68},[62,75373,75374],{"class":72}," (user",[62,75376,2755],{"class":68},[62,75378,75310],{"class":72},[62,75380,28328],{"class":68},[62,75382,13324],{"class":149},[62,75384,5024],{"class":72},[62,75386,5668],{"class":68},[62,75388,75254],{"class":72},[62,75390,2755],{"class":68},[62,75392,75393],{"class":72},"state ",[62,75395,1266],{"class":68},[62,75397,75254],{"class":72},[62,75399,2755],{"class":68},[62,75401,75350],{"class":72},[22,75403,75404],{},"Groovy actually takes this one step further though . Most of the time we are doing checks like the one above. We have one value that we want to use (a default) but if it doesn't exist use another. In Groovy we can represent that default using ?:",[52,75406,75408],{"className":73062,"code":75407,"language":53536,"meta":57,"style":57},"def location = user.city ?: user.state\n",[59,75409,75410],{"__ignoreMap":57},[62,75411,75412,75414,75416,75418,75420,75422,75424,75427,75429,75431],{"class":64,"line":65},[62,75413,73070],{"class":68},[62,75415,75369],{"class":72},[62,75417,146],{"class":68},[62,75419,75254],{"class":72},[62,75421,2755],{"class":68},[62,75423,75310],{"class":72},[62,75425,75426],{"class":68},"?:",[62,75428,75254],{"class":72},[62,75430,2755],{"class":68},[62,75432,75329],{"class":72},[1527,75434,75435],{},"html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}",{"title":57,"searchDepth":76,"depth":76,"links":75437},[],{"_id":75439,"path":75440,"title":75441,"description":75441,"meta":75442,"body":75446},"content/blog/2013/08/21/groovy-collections-vs-my-current-thought-process.md","/blog/2013/08/21/groovy-collections-vs-my-current-thought-process","Groovy collections vs My Current Thought Process",{"slug":75443,"date":75444,"published":13,"tags":75445,"author":-1,"cover":73331,"excerpt":-1},"groovy-collections-vs-my-current-thought-process","2013-08-21T16:08:00-04:00",[73330,53536],{"type":19,"value":75447,"toc":76027},[75448,75451,75456,75459,75553,75556,75668,75671,75730,75733,75736,75800,75803,75827,75830,75853,75856,75870,75873,75955,75958,76021,76024],[22,75449,75450],{},"I have been a ColdFusion developer since before I can remember. What I have always loved about the language is that it made hard things really easy to do. Somewhere along the way though I think it lost some of this magic at the core of the language. Let's take a look at a pretty trivial problem (but one that might come up often) and look at how we can solve it in both ColdFusion and Groovy.",[29685,75452,75453],{},[22,75454,75455],{},"Given a collection of names : print each name to screen and display a comma in between each name. Make sure to ommit the comma after the last name in the collection.",[22,75457,75458],{},"This doesn't seem like that hard of a problem, lets take a stab at it. First we create an array of names. In our first attempt we decide to use a for in loop and loop over each name in the collection. During each iteration we print the name and a comma. The main glaring problem with this approach is that we are going to print a comma after the last element and that won't solve the problem at hand.",[52,75460,75462],{"className":73062,"code":75461,"language":53536,"meta":57,"style":57},"// a collection of names - ColdFusion Array\nnames = \\[\"Dan\",\"Sam\",\"Lance\",\"Brian\",\"Todd\",\"Joe\",\"Scott\"\\];\n\n// this won't work because we have a comma after last element\n// and we can put some kind of if statement here because we can't check the index\nfor( name in names ) {\n writeOutput(name & \", \");\n}\n",[59,75463,75464,75469,75513,75517,75522,75527,75539,75549],{"__ignoreMap":57},[62,75465,75466],{"class":64,"line":65},[62,75467,75468],{"class":85},"// a collection of names - ColdFusion Array\n",[62,75470,75471,75474,75476,75478,75480,75482,75485,75487,75490,75492,75495,75497,75500,75502,75505,75507,75510],{"class":64,"line":76},[62,75472,75473],{"class":72},"names ",[62,75475,146],{"class":68},[62,75477,58153],{"class":72},[62,75479,25684],{"class":1675},[62,75481,32225],{"class":72},[62,75483,75484],{"class":1675},"\"Sam\"",[62,75486,32225],{"class":72},[62,75488,75489],{"class":1675},"\"Lance\"",[62,75491,32225],{"class":72},[62,75493,75494],{"class":1675},"\"Brian\"",[62,75496,32225],{"class":72},[62,75498,75499],{"class":1675},"\"Todd\"",[62,75501,32225],{"class":72},[62,75503,75504],{"class":1675},"\"Joe\"",[62,75506,32225],{"class":72},[62,75508,75509],{"class":1675},"\"Scott\"",[62,75511,75512],{"class":72},"\\];\n",[62,75514,75515],{"class":64,"line":82},[62,75516,79],{"emptyLinePlaceholder":13},[62,75518,75519],{"class":64,"line":89},[62,75520,75521],{"class":85},"// this won't work because we have a comma after last element\n",[62,75523,75524],{"class":64,"line":95},[62,75525,75526],{"class":85},"// and we can put some kind of if statement here because we can't check the index\n",[62,75528,75529,75531,75533,75536],{"class":64,"line":101},[62,75530,741],{"class":68},[62,75532,63346],{"class":72},[62,75534,75535],{"class":68},"in",[62,75537,75538],{"class":72}," names ) {\n",[62,75540,75541,75544,75547],{"class":64,"line":107},[62,75542,75543],{"class":72}," writeOutput(name & ",[62,75545,75546],{"class":1675},"\", \"",[62,75548,1133],{"class":72},[62,75550,75551],{"class":64,"line":113},[62,75552,379],{"class":72},[22,75554,75555],{},"So we move on to our next attempt. We decide to use a for loop so that we can have the index of item that we are printing out. We will print out the name and a comma except for the last item where will not print out the comma.",[52,75557,75559],{"className":73062,"code":75558,"language":53536,"meta":57,"style":57},"// a collection of names - ColdFusion Array\nnames = \\[\"Dan\",\"Sam\",\"Lance\",\"Brian\",\"Todd\",\"Joe\",\"Scott\"\\];\n\n// Now you the index and an you use an if statement to see if you should output the comma\nfor( i=1; i\u003C=arrayLen(names); ++i ) {\n writeOutput(names\\[i\\]);\n if( i != arrayLen(names) ) {\n writeOutput(\", \");\n }\n}\n",[59,75560,75561,75565,75601,75605,75610,75634,75639,75651,75660,75664],{"__ignoreMap":57},[62,75562,75563],{"class":64,"line":65},[62,75564,75468],{"class":85},[62,75566,75567,75569,75571,75573,75575,75577,75579,75581,75583,75585,75587,75589,75591,75593,75595,75597,75599],{"class":64,"line":76},[62,75568,75473],{"class":72},[62,75570,146],{"class":68},[62,75572,58153],{"class":72},[62,75574,25684],{"class":1675},[62,75576,32225],{"class":72},[62,75578,75484],{"class":1675},[62,75580,32225],{"class":72},[62,75582,75489],{"class":1675},[62,75584,32225],{"class":72},[62,75586,75494],{"class":1675},[62,75588,32225],{"class":72},[62,75590,75499],{"class":1675},[62,75592,32225],{"class":72},[62,75594,75504],{"class":1675},[62,75596,32225],{"class":72},[62,75598,75509],{"class":1675},[62,75600,75512],{"class":72},[62,75602,75603],{"class":64,"line":82},[62,75604,79],{"emptyLinePlaceholder":13},[62,75606,75607],{"class":64,"line":89},[62,75608,75609],{"class":85},"// Now you the index and an you use an if statement to see if you should output the comma\n",[62,75611,75612,75614,75617,75619,75621,75624,75626,75629,75631],{"class":64,"line":95},[62,75613,741],{"class":68},[62,75615,75616],{"class":72},"( i",[62,75618,146],{"class":68},[62,75620,6689],{"class":149},[62,75622,75623],{"class":72},"; i",[62,75625,14198],{"class":68},[62,75627,75628],{"class":72},"arrayLen(names); ",[62,75630,215],{"class":68},[62,75632,75633],{"class":72},"i ) {\n",[62,75635,75636],{"class":64,"line":101},[62,75637,75638],{"class":72}," writeOutput(names\\[i\\]);\n",[62,75640,75641,75643,75646,75648],{"class":64,"line":107},[62,75642,14187],{"class":68},[62,75644,75645],{"class":72},"( i ",[62,75647,13321],{"class":68},[62,75649,75650],{"class":72}," arrayLen(names) ) {\n",[62,75652,75653,75656,75658],{"class":64,"line":113},[62,75654,75655],{"class":72}," writeOutput(",[62,75657,75546],{"class":1675},[62,75659,1133],{"class":72},[62,75661,75662],{"class":64,"line":129},[62,75663,223],{"class":72},[62,75665,75666],{"class":64,"line":134},[62,75667,379],{"class":72},[22,75669,75670],{},"While this will work it isn't a very clean solution. All this work to solve a pretty simple problem. Finally we do some looking around and remember these a special function in the language to accomplish this. ArrayToList will actually take an array and create a list with a delimiter. I could of actually left out the comma here as it is the default but I just wanted to make sure it was clear what was going on.",[52,75672,75674],{"className":73062,"code":75673,"language":53536,"meta":57,"style":57},"// a collection of names - ColdFusion Array\nnames = \\[\"Dan\",\"Sam\",\"Lance\",\"Brian\",\"Todd\",\"Joe\",\"Scott\"\\];\n\nwriteOutput(arrayToList(names, \",\"));\n",[59,75675,75676,75680,75716,75720],{"__ignoreMap":57},[62,75677,75678],{"class":64,"line":65},[62,75679,75468],{"class":85},[62,75681,75682,75684,75686,75688,75690,75692,75694,75696,75698,75700,75702,75704,75706,75708,75710,75712,75714],{"class":64,"line":76},[62,75683,75473],{"class":72},[62,75685,146],{"class":68},[62,75687,58153],{"class":72},[62,75689,25684],{"class":1675},[62,75691,32225],{"class":72},[62,75693,75484],{"class":1675},[62,75695,32225],{"class":72},[62,75697,75489],{"class":1675},[62,75699,32225],{"class":72},[62,75701,75494],{"class":1675},[62,75703,32225],{"class":72},[62,75705,75499],{"class":1675},[62,75707,32225],{"class":72},[62,75709,75504],{"class":1675},[62,75711,32225],{"class":72},[62,75713,75509],{"class":1675},[62,75715,75512],{"class":72},[62,75717,75718],{"class":64,"line":82},[62,75719,79],{"emptyLinePlaceholder":13},[62,75721,75722,75725,75728],{"class":64,"line":89},[62,75723,75724],{"class":72},"writeOutput(arrayToList(names, ",[62,75726,75727],{"class":1675},"\",\"",[62,75729,6979],{"class":72},[22,75731,75732],{},"So I asked another dev to look at the same problem and he pretty much took the same thought process I did. I don't know about you but I feel like I write more loops in ColdFusion than anything so its alway my first instinct. Unless you know ever function the language has to offer the solution may not of been apparent to you right away. Be honest, did arrayToList cross your mind before you got to the solution?",[22,75734,75735],{},"Now let's look at the Groovy solution. I am pretty new to the language but one thing I have noticed is that working with collections is a joy to do. First lets look at the solution and then we will talk through our thought process. The join method will \"join\" each element in the list and separate them with the delimiter you provide.",[52,75737,75739],{"className":73062,"code":75738,"language":53536,"meta":57,"style":57},"def names = \\[\"Dan\",\"Sam\",\"Lance\",\"Brian\",\"Todd\",\"Joe\",\"Scott\"\\]\n\nprintln names.join(', ')\n",[59,75740,75741,75780,75784],{"__ignoreMap":57},[62,75742,75743,75745,75748,75750,75752,75754,75756,75758,75760,75762,75764,75766,75768,75770,75772,75774,75776,75778],{"class":64,"line":65},[62,75744,73070],{"class":68},[62,75746,75747],{"class":72}," names ",[62,75749,146],{"class":68},[62,75751,58153],{"class":72},[62,75753,25684],{"class":1675},[62,75755,32225],{"class":72},[62,75757,75484],{"class":1675},[62,75759,32225],{"class":72},[62,75761,75489],{"class":1675},[62,75763,32225],{"class":72},[62,75765,75494],{"class":1675},[62,75767,32225],{"class":72},[62,75769,75499],{"class":1675},[62,75771,32225],{"class":72},[62,75773,75504],{"class":1675},[62,75775,32225],{"class":72},[62,75777,75509],{"class":1675},[62,75779,50699],{"class":72},[62,75781,75782],{"class":64,"line":76},[62,75783,79],{"emptyLinePlaceholder":13},[62,75785,75786,75788,75790,75792,75795,75798],{"class":64,"line":82},[62,75787,2244],{"class":149},[62,75789,72533],{"class":72},[62,75791,2755],{"class":68},[62,75793,75794],{"class":72},"join(",[62,75796,75797],{"class":1675},"', '",[62,75799,2212],{"class":72},[22,75801,75802],{},"So what is the difference. The difference here is the join() is a first class member function. This means that I can call it directly on the collection itself. I don't need to start thinking about what functions are available to me and what the arguments are (and what order the come in) I just know that I can call a method to accomplish this. I know that if I want to iterate over the collection of data I can call",[52,75804,75806],{"className":73062,"code":75805,"language":53536,"meta":57,"style":57},"names.each()\nnames.eachWithIndex()\n",[59,75807,75808,75818],{"__ignoreMap":57},[62,75809,75810,75813,75815],{"class":64,"line":65},[62,75811,75812],{"class":72},"names",[62,75814,2755],{"class":68},[62,75816,75817],{"class":72},"each()\n",[62,75819,75820,75822,75824],{"class":64,"line":76},[62,75821,75812],{"class":72},[62,75823,2755],{"class":68},[62,75825,75826],{"class":72},"eachWithIndex()\n",[22,75828,75829],{},"I know that if I am looking for something within the collection I can call",[52,75831,75833],{"className":73062,"code":75832,"language":53536,"meta":57,"style":57},"names.find()\nnames.findAll()\n",[59,75834,75835,75844],{"__ignoreMap":57},[62,75836,75837,75839,75841],{"class":64,"line":65},[62,75838,75812],{"class":72},[62,75840,2755],{"class":68},[62,75842,75843],{"class":72},"find()\n",[62,75845,75846,75848,75850],{"class":64,"line":76},[62,75847,75812],{"class":72},[62,75849,2755],{"class":68},[62,75851,75852],{"class":72},"findAll()\n",[22,75854,75855],{},"I know that if I need the size of the collection I can call",[52,75857,75859],{"className":73062,"code":75858,"language":53536,"meta":57,"style":57},"names.size()\n",[59,75860,75861],{"__ignoreMap":57},[62,75862,75863,75865,75867],{"class":64,"line":65},[62,75864,75812],{"class":72},[62,75866,2755],{"class":68},[62,75868,75869],{"class":72},"size()\n",[22,75871,75872],{},"Finally what about a scenario where you want to create a new array that holds the string length of each of our names. Our first thought again is we need to loop over the current collection and create a new one. To do so we need to initialize an empty array, loop over each name and then add a new element using a function that takes the array name and value.",[52,75874,75876],{"className":73062,"code":75875,"language":53536,"meta":57,"style":57},"names = \\[\"Dan\",\"Sam\",\"Lance\",\"Brian\",\"Todd\",\"Joe\",\"Scott\"\\];\nnameLengths = \\[\\];\n\nfor( name in names ) {\n arrayAppend( nameLengths, len(name) );\n}\n\nwriteDump(nameLengths);\n",[59,75877,75878,75914,75923,75927,75937,75942,75946,75950],{"__ignoreMap":57},[62,75879,75880,75882,75884,75886,75888,75890,75892,75894,75896,75898,75900,75902,75904,75906,75908,75910,75912],{"class":64,"line":65},[62,75881,75473],{"class":72},[62,75883,146],{"class":68},[62,75885,58153],{"class":72},[62,75887,25684],{"class":1675},[62,75889,32225],{"class":72},[62,75891,75484],{"class":1675},[62,75893,32225],{"class":72},[62,75895,75489],{"class":1675},[62,75897,32225],{"class":72},[62,75899,75494],{"class":1675},[62,75901,32225],{"class":72},[62,75903,75499],{"class":1675},[62,75905,32225],{"class":72},[62,75907,75504],{"class":1675},[62,75909,32225],{"class":72},[62,75911,75509],{"class":1675},[62,75913,75512],{"class":72},[62,75915,75916,75919,75921],{"class":64,"line":76},[62,75917,75918],{"class":72},"nameLengths ",[62,75920,146],{"class":68},[62,75922,58194],{"class":72},[62,75924,75925],{"class":64,"line":82},[62,75926,79],{"emptyLinePlaceholder":13},[62,75928,75929,75931,75933,75935],{"class":64,"line":89},[62,75930,741],{"class":68},[62,75932,63346],{"class":72},[62,75934,75535],{"class":68},[62,75936,75538],{"class":72},[62,75938,75939],{"class":64,"line":95},[62,75940,75941],{"class":72}," arrayAppend( nameLengths, len(name) );\n",[62,75943,75944],{"class":64,"line":101},[62,75945,379],{"class":72},[62,75947,75948],{"class":64,"line":107},[62,75949,79],{"emptyLinePlaceholder":13},[62,75951,75952],{"class":64,"line":113},[62,75953,75954],{"class":72},"writeDump(nameLengths);\n",[22,75956,75957],{},"In Groovy we again know that the collection has a 1st class function to do this using collect. This will return a new collection by manipulating the existing collection. This to me is a very clean and elegant solution.",[52,75959,75961],{"className":73062,"code":75960,"language":53536,"meta":57,"style":57},"def names = \\[\"Dan\",\"Sam\",\"Lance\",\"Brian\",\"Todd\",\"Joe\",\"Scott\"\\]\n\nprintln names.collect { it.length() }\n",[59,75962,75963,76001,76005],{"__ignoreMap":57},[62,75964,75965,75967,75969,75971,75973,75975,75977,75979,75981,75983,75985,75987,75989,75991,75993,75995,75997,75999],{"class":64,"line":65},[62,75966,73070],{"class":68},[62,75968,75747],{"class":72},[62,75970,146],{"class":68},[62,75972,58153],{"class":72},[62,75974,25684],{"class":1675},[62,75976,32225],{"class":72},[62,75978,75484],{"class":1675},[62,75980,32225],{"class":72},[62,75982,75489],{"class":1675},[62,75984,32225],{"class":72},[62,75986,75494],{"class":1675},[62,75988,32225],{"class":72},[62,75990,75499],{"class":1675},[62,75992,32225],{"class":72},[62,75994,75504],{"class":1675},[62,75996,32225],{"class":72},[62,75998,75509],{"class":1675},[62,76000,50699],{"class":72},[62,76002,76003],{"class":64,"line":76},[62,76004,79],{"emptyLinePlaceholder":13},[62,76006,76007,76009,76011,76013,76016,76018],{"class":64,"line":82},[62,76008,2244],{"class":149},[62,76010,72533],{"class":72},[62,76012,2755],{"class":68},[62,76014,76015],{"class":72},"collect { it",[62,76017,2755],{"class":68},[62,76019,76020],{"class":72},"length() }\n",[22,76022,76023],{},"I hope nobody gets the wrong impression about ColdFusion here because many of these same approaches are the same in many languages. I just wanted to point out how much I enjoy working with collections in Groovy. They make me think a different way when dealing with data and I am finding myself writing a lot more boring iteration loops these days.",[1527,76025,76026],{},"html pre.shiki code .s4-Ip, html code.shiki .s4-Ip{--shiki-default:#6A737D;--shiki-dark:#6A737D;--shiki-sepia:#6A737D}html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}",{"title":57,"searchDepth":76,"depth":76,"links":76028},[],{"_id":76030,"path":76031,"title":76032,"description":76032,"meta":76033,"body":76037},"content/blog/2013/08/21/writing-sql-in-a-grails-application.md","/blog/2013/08/21/writing-sql-in-a-grails-application","Writing SQL in a Grails Application",{"slug":76034,"date":76035,"published":13,"tags":76036,"author":-1,"cover":73331,"excerpt":-1},"writing-sql-in-a-grails-application","2013-08-21T12:08:00-04:00",[73330],{"type":19,"value":76038,"toc":76219},[76039,76042,76045,76208,76211,76216],[22,76040,76041],{},"I have been using Hibernate for a couple years now and I love it. Every now and then though there are certain times when you are going to have to write some raw SQL. Maybe there is some performance issue you are trying to solve or maybe there are some custom functions that you just can't access. Whatever the case may be there are times when you will need to write some plain old SQL. The question came up on the Grails mailing list so I thought I would share it here.",[22,76043,76044],{},"First import the Sql class. Next define a property called dataSource. Spring will auto wire this dependency for us by name and with that we are ready to go. After we create a new instance of the sql class we can call a method on it called rows that can take a SQL statement. In this simple example we are just doing a select of a name property and this query brings back 3 rows. If you were to dump the class name of the rows variable you will see that its nothing more than an ArrayList. We can use the each method to loop over our collection and make sure it worked correctly. Now in the real world you world probably stick this in a service and return the rows to a view but you get the idea.",[52,76046,76048],{"className":73062,"code":76047,"language":53536,"meta":57,"style":57},"import groovy.sql.Sql\n\nclass UserController {\n\n def dataSource\n\n def runQuery(){\n def sql = new Sql(dataSource)\n def rows = sql.rows(\"SELECT name from brand\")\n\n rows.each { row ->\n log.debug row.name\n }\n\n sql.close()\n\n render \"Query Executed\"\n }\n\n}\n",[59,76049,76050,76057,76061,76069,76073,76080,76084,76093,76110,76131,76135,76151,76166,76170,76174,76184,76188,76196,76200,76204],{"__ignoreMap":57},[62,76051,76052,76054],{"class":64,"line":65},[62,76053,27875],{"class":68},[62,76055,76056],{"class":72}," groovy.sql.Sql\n",[62,76058,76059],{"class":64,"line":76},[62,76060,79],{"emptyLinePlaceholder":13},[62,76062,76063,76065,76067],{"class":64,"line":82},[62,76064,11671],{"class":68},[62,76066,22436],{"class":122},[62,76068,126],{"class":72},[62,76070,76071],{"class":64,"line":89},[62,76072,79],{"emptyLinePlaceholder":13},[62,76074,76075,76077],{"class":64,"line":95},[62,76076,73555],{"class":68},[62,76078,76079],{"class":72}," dataSource\n",[62,76081,76082],{"class":64,"line":101},[62,76083,79],{"emptyLinePlaceholder":13},[62,76085,76086,76088,76091],{"class":64,"line":107},[62,76087,73555],{"class":68},[62,76089,76090],{"class":122}," runQuery",[62,76092,57791],{"class":72},[62,76094,76095,76098,76100,76102,76104,76107],{"class":64,"line":113},[62,76096,76097],{"class":68}," def",[62,76099,42257],{"class":72},[62,76101,146],{"class":68},[62,76103,466],{"class":68},[62,76105,76106],{"class":68}," Sql",[62,76108,76109],{"class":72},"(dataSource)\n",[62,76111,76112,76114,76117,76119,76121,76123,76126,76129],{"class":64,"line":129},[62,76113,76097],{"class":68},[62,76115,76116],{"class":72}," rows ",[62,76118,146],{"class":68},[62,76120,73095],{"class":72},[62,76122,2755],{"class":68},[62,76124,76125],{"class":72},"rows(",[62,76127,76128],{"class":1675},"\"SELECT name from brand\"",[62,76130,2212],{"class":72},[62,76132,76133],{"class":64,"line":134},[62,76134,79],{"emptyLinePlaceholder":13},[62,76136,76137,76140,76142,76145,76148],{"class":64,"line":156},[62,76138,76139],{"class":72}," rows",[62,76141,2755],{"class":68},[62,76143,76144],{"class":72},"each { ",[62,76146,76147],{"class":889},"row",[62,76149,76150],{"class":68}," ->\n",[62,76152,76153,76156,76158,76161,76163],{"class":64,"line":161},[62,76154,76155],{"class":72}," log",[62,76157,2755],{"class":68},[62,76159,76160],{"class":72},"debug row",[62,76162,2755],{"class":68},[62,76164,76165],{"class":72},"name\n",[62,76167,76168],{"class":64,"line":167},[62,76169,533],{"class":72},[62,76171,76172],{"class":64,"line":173},[62,76173,79],{"emptyLinePlaceholder":13},[62,76175,76176,76179,76181],{"class":64,"line":179},[62,76177,76178],{"class":72}," sql",[62,76180,2755],{"class":68},[62,76182,76183],{"class":72},"close()\n",[62,76185,76186],{"class":64,"line":185},[62,76187,79],{"emptyLinePlaceholder":13},[62,76189,76190,76193],{"class":64,"line":191},[62,76191,76192],{"class":72}," render ",[62,76194,76195],{"class":1675},"\"Query Executed\"\n",[62,76197,76198],{"class":64,"line":209},[62,76199,223],{"class":72},[62,76201,76202],{"class":64,"line":220},[62,76203,79],{"emptyLinePlaceholder":13},[62,76205,76206],{"class":64,"line":226},[62,76207,379],{"class":72},[22,76209,76210],{},"Pretty simple right? One last thing is we want to make sure we close the connection via the docs recommendations.",[29685,76212,76213],{},[22,76214,76215],{},"Constructs an SQL instance using the given Connection. It is the caller's responsibility to close the Connection after the Sql instance has been used. Depending on which features you are using, you may be able to do this on the connection object directly but the preferred approach is to call the close() method which will close the connection but also free any caches resources.",[1527,76217,76218],{},"html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}html pre.shiki code .spoOn, html code.shiki .spoOn{--shiki-default:#E36209;--shiki-dark:#FFAB70;--shiki-sepia:#E36209}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}",{"title":57,"searchDepth":76,"depth":76,"links":76220},[],{"_id":76222,"path":76223,"title":76224,"description":76224,"meta":76225,"body":76229},"content/blog/2013/08/20/grails-hibernate-logging.md","/blog/2013/08/20/grails-hibernate-logging","Grails Hibernate Logging",{"slug":76226,"date":76227,"published":13,"tags":76228,"author":-1,"cover":73331,"excerpt":-1},"grails-hibernate-logging","2013-08-20T17:08:00-04:00",[73330],{"type":19,"value":76230,"toc":76263},[76231,76239,76242,76247,76250,76255,76258],[22,76232,76233,76234,2755],{},"As a little bit of a Hibernate veteran at this point I know the value of logging when it comes to debugging issues. So before I even get my first application going here I figured I would setup hibernate logging. I am not going to go through everything you can do with logging, you can ",[677,76235,76238],{"href":76236,"rel":76237},"http://grails.org/doc/latest/guide/single.html#logging",[681],"check out the docs for that",[22,76240,76241],{},"Enabling Hibernate logging is actually really simple. Open up your datasource configuration file and add logsql to the environment you wish to enable logging in. You can add it to the hibernate settings but its a safe bet to just add it to your development environment. I also added the format_sql setting which gives you some nice formatting of the generated SQL.",[22,76243,76244],{},[653,76245],{"alt":57,"src":76246},"./logging_datasource.png",[22,76248,76249],{},"Next we need to add a couple of lines to the log4j settings. Open up your Config.groovy file and go down to the log4j section. We need to add a debug line for 'org.hibernate.SQL'. This is actually all we need to do to log statements. Sometimes though its nice to see what parameters are being binding to queries. To see these we need that trace statement. Make sure you get specific here, if you just log 'org.hibernate.type' you will get a lot of info that you really don't care to have.",[22,76251,76252],{},[653,76253],{"alt":57,"src":76254},"./logging_config.png",[22,76256,76257],{},"That is all there is to it folks, pretty easy stuff. Below is a console view of an insert into the user table followed by a select. We can see what values are being inserted and the nice thing is it shows each type. Notice the nice formatting for the SQL as well.",[22,76259,76260],{},[653,76261],{"alt":57,"src":76262},"./logging_sql.png",{"title":57,"searchDepth":76,"depth":76,"links":76264},[],{"_id":76266,"path":76267,"title":76268,"description":76268,"meta":76269,"body":76273},"content/blog/2013/08/20/groovy-primitive-data-types-vs-classes.md","/blog/2013/08/20/groovy-primitive-data-types-vs-classes","Groovy Primitive Data Types vs Objects",{"slug":76270,"date":76271,"published":13,"tags":76272,"author":-1,"cover":73331,"excerpt":-1},"groovy-primitive-data-types-vs-classes","2013-08-20T12:08:00-04:00",[73330,53536],{"type":19,"value":76274,"toc":76528},[76275,76284,76388,76391,76394,76417,76420,76525],[22,76276,76277,76278,76283],{},"A message ",[677,76279,76282],{"href":76280,"rel":76281},"http://grails.1312388.n4.nabble.com/Boolean-vs-ENUM-td4648155.html",[681],"I posted on the Grails User"," List spawned another pretty interesting conversation that I would like to share with you. The original question had to do with a declaring a boolean property. Say we have a user domain object and we want to store a bit flag describing whether or not the user was active.",[52,76285,76287],{"className":73062,"code":76286,"language":53536,"meta":57,"style":57},"class User {\n\n String first\n String last\n boolean active1\n boolean active2 = Boolean.TRUE\n Boolean active3\n Boolean active4 = Boolean.FALSE\n\n static constraints = {\n\n }\n\n}\n",[59,76288,76289,76297,76301,76308,76315,76322,76337,76344,76358,76362,76372,76376,76380,76384],{"__ignoreMap":57},[62,76290,76291,76293,76295],{"class":64,"line":65},[62,76292,11671],{"class":68},[62,76294,22289],{"class":122},[62,76296,126],{"class":72},[62,76298,76299],{"class":64,"line":76},[62,76300,79],{"emptyLinePlaceholder":13},[62,76302,76303,76305],{"class":64,"line":82},[62,76304,73404],{"class":68},[62,76306,76307],{"class":72}," first\n",[62,76309,76310,76312],{"class":64,"line":89},[62,76311,73404],{"class":68},[62,76313,76314],{"class":72}," last\n",[62,76316,76317,76319],{"class":64,"line":95},[62,76318,74787],{"class":68},[62,76320,76321],{"class":72}," active1\n",[62,76323,76324,76326,76329,76331,76334],{"class":64,"line":101},[62,76325,74787],{"class":68},[62,76327,76328],{"class":72}," active2 ",[62,76330,146],{"class":68},[62,76332,76333],{"class":68}," Boolean.",[62,76335,76336],{"class":149},"TRUE\n",[62,76338,76339,76341],{"class":64,"line":107},[62,76340,73439],{"class":68},[62,76342,76343],{"class":72}," active3\n",[62,76345,76346,76348,76351,76353,76355],{"class":64,"line":113},[62,76347,73439],{"class":68},[62,76349,76350],{"class":72}," active4 ",[62,76352,146],{"class":68},[62,76354,76333],{"class":68},[62,76356,76357],{"class":149},"FALSE\n",[62,76359,76360],{"class":64,"line":129},[62,76361,79],{"emptyLinePlaceholder":13},[62,76363,76364,76366,76368,76370],{"class":64,"line":134},[62,76365,70061],{"class":68},[62,76367,73489],{"class":72},[62,76369,146],{"class":68},[62,76371,126],{"class":72},[62,76373,76374],{"class":64,"line":156},[62,76375,79],{"emptyLinePlaceholder":13},[62,76377,76378],{"class":64,"line":161},[62,76379,223],{"class":72},[62,76381,76382],{"class":64,"line":167},[62,76383,79],{"emptyLinePlaceholder":13},[62,76385,76386],{"class":64,"line":173},[62,76387,379],{"class":72},[22,76389,76390],{},"Here are a couple ways that we could define this active flag. You will notice the 1st 2 use lowercase boolean, this means that its a primitive type. The 2nd two use the Boolean class to define its type. The odd 2 also show an easy way to setup a default value for the property.",[22,76392,76393],{},"So the question came up when is it appropriate to use the primitive data type (lowercase) and when should we use the Class type. Burt Beckwith actually gave a response on the list and I would like to share his response here.",[29685,76395,76396,76399,76402,76405,76408,76411,76414],{},[22,76397,76398],{},"The core problem is that primitives can't be null. Groovy fakes that out with autoboxing, but in Java (which is what Hibernate assumes we're using) that's not realistic. If you store a null value in a boolean or numeric column, you can't store that in a boolean/int/long/etc field. It's not correct to convert a null boolean to false, or a null number to 0, since those might be valid values. Null means that no value or choice has been made yet.",[22,76400,76401],{},"For example consider an \"int yearsOfExperience\" field in a domain class. 0 is a valid value, but even without an initializer the domain class instance will be initialized to 0 by the compiler (both in Java and Groovy). So if the user enters nothing, there will be a value set and you miss your chance to reject the submission. If it's \"Integer yearsOfExperience\" it's not initialized, and since there's a default not-null constraint on all fields, you'll see that the user didn't make a choice and you can reject. They can then choose 0 or whatever, but now you have a way to ensure that they enter valid values.",[22,76403,76404],{},"And in fact even if you try to make a primitive nullable in GORM, Hibernate won't let you. As an example create this domain class in a test app and run \"grails schema-export\":",[22,76406,76407],{},"class Thing {\nString name\nint foo\nboolean bar\nstatic constraints = {\nname nullable: true\nfoo nullable: true\nbar nullable: true\n}\n}",[22,76409,76410],{},"Looking at the generated DDL in target/ddl.sql you'll see that the String column is nullable, but the others aren't. So it works the way you want, but the domain class and database are inconsistent.",[22,76412,76413],{},"create table thing (\nid bigint generated by default as identity,\nversion bigint not null,\nbar boolean not null,\nfoo integer not null,\nname varchar(255),\nprimary key (id)\n);",[22,76415,76416],{},"Burt",[22,76418,76419],{},"A big thank you to Burt for clearing that up for me. So in the case of our bit flag on our user object we can simply use a primitive type and set the default to TRUE. Here is a quick list of primitive types (and their defaults) and their wrapper objects. The only caveat to this list is String, which is actually an object.",[11922,76421,76422,76435],{},[11925,76423,76424],{},[11928,76425,76426,76429,76432],{},[11931,76427,76428],{},"Data Type",[11931,76430,76431],{},"Default Value (for fields)",[11931,76433,76434],{},"Wrapper Type",[11941,76436,76437,76446,76456,76464,76473,76484,76494,76505,76515],{},[11928,76438,76439,76441,76443],{},[11946,76440,13267],{},[11946,76442,1130],{},[11946,76444,76445],{},"Byte",[11928,76447,76448,76451,76453],{},[11946,76449,76450],{},"short",[11946,76452,1130],{},[11946,76454,76455],{},"Short",[11928,76457,76458,76460,76462],{},[11946,76459,747],{},[11946,76461,1130],{},[11946,76463,979],{},[11928,76465,76466,76468,76471],{},[11946,76467,27552],{},[11946,76469,76470],{},"0L",[11946,76472,6850],{},[11928,76474,76475,76478,76481],{},[11946,76476,76477],{},"float",[11946,76479,76480],{},"0.0f",[11946,76482,76483],{},"Float",[11928,76485,76486,76488,76491],{},[11946,76487,6930],{},[11946,76489,76490],{},"0.0d",[11946,76492,76493],{},"Double",[11928,76495,76496,76499,76502],{},[11946,76497,76498],{},"char",[11946,76500,76501],{},"‘\\u0000’",[11946,76503,76504],{},"Character",[11928,76506,76507,76510,76512],{},[11946,76508,76509],{},"boolean",[11946,76511,50950],{},[11946,76513,76514],{},"Boolean",[11928,76516,76517,76520,76522],{},[11946,76518,76519],{},"String (or any object)",[11946,76521,3256],{},[11946,76523,76524],{},"n/a",[1527,76526,76527],{},"html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}",{"title":57,"searchDepth":76,"depth":76,"links":76529},[],{"_id":76531,"path":76532,"title":76533,"description":76533,"meta":76534,"body":76538},"content/blog/2013/08/20/groovys-null-safe-operator.md","/blog/2013/08/20/groovys-null-safe-operator","Groovy's null safe operator",{"slug":76535,"date":76536,"published":13,"tags":76537,"author":-1,"cover":73331,"excerpt":-1},"groovys-null-safe-operator","2013-08-20T06:08:00-04:00",[73330,53536],{"type":19,"value":76539,"toc":76723},[76540,76543,76672,76675,76718,76721],[22,76541,76542],{},"The more years I get under my belt as a programmer the more I appreciate the smaller things a language has to offer. Today I want to look at Groovy's null safe operator. When you want to crawl an object graph you always have to be worried about the dreaded null pointer exception. First I want to take a look at this example in ColdFusion. In this example we are looking at the body of a method where we load up a user object. We want to grab the city of the user object but in each step of the object graph we need to make sure that call is not null before preceding.",[52,76544,76546],{"className":73062,"code":76545,"language":53536,"meta":57,"style":57},"var city = \"\";\nvar user = entityLoadByPK(\"User\",10);\n\nif ( !isNull(user) ){\n if( !isNull( user.getAddress() ) ) {\n if( !isNull(user.getAddress().getCity() ) ) {\n city = user.getAddress().getCity();\n }\n }\n}\n\nreturn city;\n",[59,76547,76548,76559,76578,76582,76594,76610,76631,76649,76653,76657,76661,76665],{"__ignoreMap":57},[62,76549,76550,76553,76555,76557],{"class":64,"line":65},[62,76551,76552],{"class":72},"var city ",[62,76554,146],{"class":68},[62,76556,51048],{"class":1675},[62,76558,153],{"class":72},[62,76560,76561,76564,76566,76569,76572,76574,76576],{"class":64,"line":76},[62,76562,76563],{"class":72},"var user ",[62,76565,146],{"class":68},[62,76567,76568],{"class":72}," entityLoadByPK(",[62,76570,76571],{"class":1675},"\"User\"",[62,76573,32225],{"class":72},[62,76575,26752],{"class":149},[62,76577,1133],{"class":72},[62,76579,76580],{"class":64,"line":82},[62,76581,79],{"emptyLinePlaceholder":13},[62,76583,76584,76586,76589,76591],{"class":64,"line":89},[62,76585,34116],{"class":68},[62,76587,76588],{"class":72}," ( ",[62,76590,6277],{"class":68},[62,76592,76593],{"class":72},"isNull(user) ){\n",[62,76595,76596,76598,76600,76602,76605,76607],{"class":64,"line":95},[62,76597,14187],{"class":68},[62,76599,52630],{"class":72},[62,76601,6277],{"class":68},[62,76603,76604],{"class":72},"isNull( user",[62,76606,2755],{"class":68},[62,76608,76609],{"class":72},"getAddress() ) ) {\n",[62,76611,76612,76614,76616,76618,76621,76623,76626,76628],{"class":64,"line":101},[62,76613,12741],{"class":68},[62,76615,52630],{"class":72},[62,76617,6277],{"class":68},[62,76619,76620],{"class":72},"isNull(user",[62,76622,2755],{"class":68},[62,76624,76625],{"class":72},"getAddress()",[62,76627,2755],{"class":68},[62,76629,76630],{"class":72},"getCity() ) ) {\n",[62,76632,76633,76636,76638,76640,76642,76644,76646],{"class":64,"line":107},[62,76634,76635],{"class":72}," city ",[62,76637,146],{"class":68},[62,76639,75254],{"class":72},[62,76641,2755],{"class":68},[62,76643,76625],{"class":72},[62,76645,2755],{"class":68},[62,76647,76648],{"class":72},"getCity();\n",[62,76650,76651],{"class":64,"line":113},[62,76652,533],{"class":72},[62,76654,76655],{"class":64,"line":129},[62,76656,223],{"class":72},[62,76658,76659],{"class":64,"line":134},[62,76660,379],{"class":72},[62,76662,76663],{"class":64,"line":156},[62,76664,79],{"emptyLinePlaceholder":13},[62,76666,76667,76669],{"class":64,"line":161},[62,76668,22008],{"class":68},[62,76670,76671],{"class":72}," city;\n",[22,76673,76674],{},"In Groovy we can do this shorthand by using the null safe operator (?.). If the variable before the question mark is null it will not proceed and actually returns null for you. We could even shorten this statement more but you get the idea.",[52,76676,76678],{"className":73062,"code":76677,"language":53536,"meta":57,"style":57},"def user = User.get(10)\n\nreturn user?.address?.city\n",[59,76679,76680,76698,76702],{"__ignoreMap":57},[62,76681,76682,76684,76687,76689,76691,76694,76696],{"class":64,"line":65},[62,76683,73070],{"class":68},[62,76685,76686],{"class":72}," user ",[62,76688,146],{"class":68},[62,76690,15681],{"class":68},[62,76692,76693],{"class":72},"get(",[62,76695,26752],{"class":149},[62,76697,2212],{"class":72},[62,76699,76700],{"class":64,"line":76},[62,76701,79],{"emptyLinePlaceholder":13},[62,76703,76704,76706,76708,76711,76714,76716],{"class":64,"line":82},[62,76705,22008],{"class":68},[62,76707,75254],{"class":72},[62,76709,76710],{"class":68},"?.",[62,76712,76713],{"class":72},"address",[62,76715,76710],{"class":68},[62,76717,75350],{"class":72},[22,76719,76720],{},"The Groovy language has so many little things like this that I am really starting to love. What are some of your favorite features in the language?",[1527,76722,75435],{},{"title":57,"searchDepth":76,"depth":76,"links":76724},[],{"_id":76726,"path":76727,"title":76728,"description":76729,"meta":76730,"body":76735},"content/blog/2010/08/04/create-sql-insert-statements-from-a-spreadsheet.md","/blog/2010/08/04/create-sql-insert-statements-from-a-spreadsheet","Create SQL Insert Statements from a Spreadsheet","In this article, I will show you how to create SQL insert statements from a spreadsheet",{"slug":76731,"date":76732,"published":13,"tags":76733,"author":17,"cover":76734,"excerpt":-1},"create-sql-insert-statements-from-a-spreadsheet","2010-08-04T15:08:00.000Z",[38716],"./sql-cover.png",{"type":19,"value":76736,"toc":76857},[76737,76740,76743,76749,76752,76780,76783,76789,76792,76845,76848,76854],[22,76738,76739],{},"I know this is probably old news to most but I was helping a friend out yesterday who didn’t know this little trick so I thought I would share it. While some of you may have access to production databases its pretty common that these servers are guarded by a DBA. If I get a huge spread sheet of data I can’t really import the data I need to send the sql statements to the dba and the script is run against the production database.",[22,76741,76742],{},"In this example I just got a spread sheet of 5 users that need to be imported into our users table.",[22,76744,76745,2755],{},[653,76746],{"alt":76747,"src":76748},"Spreadsheet Data","/images/blog/2010/08/04/spreadsheet_data.png",[22,76750,76751],{},"Now for 5 users this is not a big deal but what about 100 or even a 1000 users. There is actually an easy way to create your insert statements using excel. First we will mark our D column as SQL. Next place click on the cell D1. What we are going to do is write a sql statement that will grab data from the columns a,b and c. First we write our normal insert statement but for the values we can evaluate the data in a cell using the following formula. The & is just used for concatenating.",[52,76753,76755],{"className":41824,"code":76754,"language":38716,"meta":57,"style":57},"=\"Insert into Users (userid,first,last) VALUES (\"& A2 & \",\"& B2 &\", \"& C2 &\");\"\n",[59,76756,76757],{"__ignoreMap":57},[62,76758,76759,76761,76764,76767,76769,76772,76774,76777],{"class":64,"line":65},[62,76760,146],{"class":68},[62,76762,76763],{"class":1675},"\"Insert into Users (userid,first,last) VALUES (\"",[62,76765,76766],{"class":72},"& A2 & ",[62,76768,75727],{"class":1675},[62,76770,76771],{"class":72},"& B2 &",[62,76773,75546],{"class":1675},[62,76775,76776],{"class":72},"& C2 &",[62,76778,76779],{"class":1675},"\");\"\n",[22,76781,76782],{},"Then we can use that same formula for every row in our sheet. Simply copy and then past that formula all the way down for as many records as you have and you will end up with something like this.",[22,76784,76785],{},[653,76786],{"alt":76787,"src":76788},"Insert for every row","/images/blog/2010/08/04/insert_sql_2.png",[22,76790,76791],{},"While this is great you should see the issue. The user id field is fine but we have no single quotes around our strings, but we can fix that using the concatenate function.",[52,76793,76795],{"className":41824,"code":76794,"language":38716,"meta":57,"style":57},"=\"Insert into Users (userid,first,last) VALUES (\"& A2 & \",\"& CONCATENATE(\"'\",B2,\"'\") &\", \"& CONCATENATE(\"'\",C2,\"'\") &\");\"\n",[59,76796,76797],{"__ignoreMap":57},[62,76798,76799,76801,76803,76805,76807,76810,76813,76815,76818,76821,76823,76826,76828,76830,76832,76834,76836,76839,76841,76843],{"class":64,"line":65},[62,76800,146],{"class":68},[62,76802,76763],{"class":1675},[62,76804,76766],{"class":72},[62,76806,75727],{"class":1675},[62,76808,76809],{"class":72},"& ",[62,76811,76812],{"class":68},"CONCATENATE",[62,76814,2109],{"class":72},[62,76816,76817],{"class":1675},"\"'\"",[62,76819,76820],{"class":72},",B2,",[62,76822,76817],{"class":1675},[62,76824,76825],{"class":72},") &",[62,76827,75546],{"class":1675},[62,76829,76809],{"class":72},[62,76831,76812],{"class":68},[62,76833,2109],{"class":72},[62,76835,76817],{"class":1675},[62,76837,76838],{"class":72},",C2,",[62,76840,76817],{"class":1675},[62,76842,76825],{"class":72},[62,76844,76779],{"class":1675},[22,76846,76847],{},"Now our insert statements look a lot better. Again, not the coolest thing in the world but it really helps out nicely in this spot.",[22,76849,76850],{},[653,76851],{"alt":76852,"src":76853},"Final Insert Statements","/images/blog/2010/08/04/insert_sql_3.png",[1527,76855,76856],{},"html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}",{"title":57,"searchDepth":76,"depth":76,"links":76858},[],{"_id":76860,"path":76861,"title":76862,"description":76862,"meta":76863,"body":76868},"content/blog/2014/02/23/creating-and-testing-your-first-grails-tag-library.md","/blog/2014/02/23/creating-and-testing-your-first-grails-tag-library","Creating and testing your first Grails Tag Library",{"slug":76864,"date":76865,"published":13,"tags":76866,"author":-1,"cover":76867,"excerpt":-1},"creating-and-testing-your-first-grails-tag-library","2014-02-23T07:02:00-05",[73330,53536],"./testing-cover.jpg",{"type":19,"value":76869,"toc":77901},[76870,76873,76878,77151,77156,77898],[22,76871,76872],{},"I created a quick screencast to walk you through creating and testing your first tag lib",[22,76874,76875],{},[646,76876,76877],{},"BootstrapTagLib.groovy",[52,76879,76881],{"className":73062,"code":76880,"language":53536,"meta":57,"style":57},"package com.tagdemo\n\nclass BootstrapTagLib {\n\n static namespace = \"bs\"\n\n def container = { attrs, body ->\n def containerClass = attrs.fluid ? 'container-fluid' : 'container'\n out \u003C\u003C '\n\n' + body() + '\n\n'\n }\n\n def row = { attrs,body ->\n out \u003C\u003C '\n\n' + body() + '\n\n'\n }\n\n def grid = { attrs, body ->\n attrs.cols ?: 12\n out \u003C\u003C '\n\n' + body() + '\n\n'\n }\n\n}\n",[59,76882,76883,76890,76894,76903,76907,76919,76923,76943,76970,76981,76985,76998,77002,77006,77010,77014,77033,77041,77045,77057,77061,77065,77069,77073,77092,77107,77115,77119,77131,77135,77139,77143,77147],{"__ignoreMap":57},[62,76884,76885,76887],{"class":64,"line":65},[62,76886,69],{"class":68},[62,76888,76889],{"class":72}," com.tagdemo\n",[62,76891,76892],{"class":64,"line":76},[62,76893,79],{"emptyLinePlaceholder":13},[62,76895,76896,76898,76901],{"class":64,"line":82},[62,76897,11671],{"class":68},[62,76899,76900],{"class":122}," BootstrapTagLib",[62,76902,126],{"class":72},[62,76904,76905],{"class":64,"line":89},[62,76906,79],{"emptyLinePlaceholder":13},[62,76908,76909,76911,76914,76916],{"class":64,"line":95},[62,76910,70061],{"class":68},[62,76912,76913],{"class":72}," namespace ",[62,76915,146],{"class":68},[62,76917,76918],{"class":1675}," \"bs\"\n",[62,76920,76921],{"class":64,"line":101},[62,76922,79],{"emptyLinePlaceholder":13},[62,76924,76925,76927,76930,76932,76934,76937,76939,76941],{"class":64,"line":107},[62,76926,73555],{"class":68},[62,76928,76929],{"class":72}," container ",[62,76931,146],{"class":68},[62,76933,21785],{"class":72},[62,76935,76936],{"class":889},"attrs",[62,76938,976],{"class":72},[62,76940,11414],{"class":889},[62,76942,76150],{"class":68},[62,76944,76945,76947,76950,76952,76955,76957,76960,76962,76965,76967],{"class":64,"line":113},[62,76946,76097],{"class":68},[62,76948,76949],{"class":72}," containerClass ",[62,76951,146],{"class":68},[62,76953,76954],{"class":72}," attrs",[62,76956,2755],{"class":68},[62,76958,76959],{"class":72},"fluid ",[62,76961,5668],{"class":68},[62,76963,76964],{"class":1675}," 'container-fluid'",[62,76966,57090],{"class":68},[62,76968,76969],{"class":1675}," 'container'\n",[62,76971,76972,76975,76978],{"class":64,"line":129},[62,76973,76974],{"class":72}," out ",[62,76976,76977],{"class":68},"\u003C\u003C",[62,76979,76980],{"class":1675}," '\n",[62,76982,76983],{"class":64,"line":134},[62,76984,79],{"emptyLinePlaceholder":13},[62,76986,76987,76989,76991,76994,76996],{"class":64,"line":156},[62,76988,54412],{"class":1675},[62,76990,4507],{"class":68},[62,76992,76993],{"class":72}," body() ",[62,76995,1148],{"class":68},[62,76997,76980],{"class":1675},[62,76999,77000],{"class":64,"line":161},[62,77001,79],{"emptyLinePlaceholder":13},[62,77003,77004],{"class":64,"line":167},[62,77005,54230],{"class":1675},[62,77007,77008],{"class":64,"line":173},[62,77009,223],{"class":72},[62,77011,77012],{"class":64,"line":179},[62,77013,79],{"emptyLinePlaceholder":13},[62,77015,77016,77018,77021,77023,77025,77027,77029,77031],{"class":64,"line":185},[62,77017,73555],{"class":68},[62,77019,77020],{"class":72}," row ",[62,77022,146],{"class":68},[62,77024,21785],{"class":72},[62,77026,76936],{"class":889},[62,77028,32225],{"class":72},[62,77030,11414],{"class":889},[62,77032,76150],{"class":68},[62,77034,77035,77037,77039],{"class":64,"line":191},[62,77036,76974],{"class":72},[62,77038,76977],{"class":68},[62,77040,76980],{"class":1675},[62,77042,77043],{"class":64,"line":209},[62,77044,79],{"emptyLinePlaceholder":13},[62,77046,77047,77049,77051,77053,77055],{"class":64,"line":220},[62,77048,54412],{"class":1675},[62,77050,4507],{"class":68},[62,77052,76993],{"class":72},[62,77054,1148],{"class":68},[62,77056,76980],{"class":1675},[62,77058,77059],{"class":64,"line":226},[62,77060,79],{"emptyLinePlaceholder":13},[62,77062,77063],{"class":64,"line":231},[62,77064,54230],{"class":1675},[62,77066,77067],{"class":64,"line":236},[62,77068,223],{"class":72},[62,77070,77071],{"class":64,"line":242},[62,77072,79],{"emptyLinePlaceholder":13},[62,77074,77075,77077,77080,77082,77084,77086,77088,77090],{"class":64,"line":247},[62,77076,73555],{"class":68},[62,77078,77079],{"class":72}," grid ",[62,77081,146],{"class":68},[62,77083,21785],{"class":72},[62,77085,76936],{"class":889},[62,77087,976],{"class":72},[62,77089,11414],{"class":889},[62,77091,76150],{"class":68},[62,77093,77094,77097,77099,77102,77104],{"class":64,"line":252},[62,77095,77096],{"class":72}," attrs",[62,77098,2755],{"class":68},[62,77100,77101],{"class":72},"cols ",[62,77103,75426],{"class":68},[62,77105,77106],{"class":149}," 12\n",[62,77108,77109,77111,77113],{"class":64,"line":257},[62,77110,76974],{"class":72},[62,77112,76977],{"class":68},[62,77114,76980],{"class":1675},[62,77116,77117],{"class":64,"line":271},[62,77118,79],{"emptyLinePlaceholder":13},[62,77120,77121,77123,77125,77127,77129],{"class":64,"line":281},[62,77122,54412],{"class":1675},[62,77124,4507],{"class":68},[62,77126,76993],{"class":72},[62,77128,1148],{"class":68},[62,77130,76980],{"class":1675},[62,77132,77133],{"class":64,"line":286},[62,77134,79],{"emptyLinePlaceholder":13},[62,77136,77137],{"class":64,"line":291},[62,77138,54230],{"class":1675},[62,77140,77141],{"class":64,"line":296},[62,77142,223],{"class":72},[62,77144,77145],{"class":64,"line":302},[62,77146,79],{"emptyLinePlaceholder":13},[62,77148,77149],{"class":64,"line":308},[62,77150,379],{"class":72},[22,77152,77153],{},[646,77154,77155],{},"BootstrapTagLibSpec.groovy",[52,77157,77159],{"className":73062,"code":77158,"language":53536,"meta":57,"style":57},"package com.tagdemo\n\nimport grails.test.mixin.TestFor\nimport spock.lang.Specification\nimport spock.lang.Unroll\n\n@TestFor(BootstrapTagLib)\nclass BootstrapTagLibSpec extends Specification {\n\n void \"No arguments passed to container results in class of container\"() {\n given:\n def output = applyTemplate('body')\n\n expect:\n output == '\n\nbody\n\n'\n }\n\n void \"When container argument fluid is true class should equal container-fluid\"(){\n when:\n def output = applyTemplate('body\\_goes\\_here',\\[fluid\\_var:true\\])\n\n then:\n output == '\n\nbody\\_goes\\_here\n\n'\n }\n\n void \"Check that our row tag creates the correct output\"(){\n expect:\n applyTemplate('') == '\n\n'\n }\n\n @Unroll\n void \"Test all possible values in our grid\"(){\n expect:\n applyTemplate(\"\") == cssClass\n\n where:\n cols | cssClass\n 1 | '\n\n'\n 2 | '\n\n'\n 3 | '\n\n'\n 4 | '\n\n'\n 5 | '\n\n'\n 6 | '\n\n'\n 7 | '\n\n'\n 8 | '\n\n'\n 9 | '\n\n'\n 10 | '\n\n'\n 11 | '\n\n'\n 12 | '\n\n'\n }\n\n void \"When we have a container, row and full layout it is what we expect\"(){\n given:\n def tpl = '''\n Main Content\n Right Sidebar\n '''\n def output = applyTemplate(tpl)\n\n expect:\n output == '''\n\n\n\n\n\n\n\n\nMain Content\n\n\n\n\n\n\n\nRight Sidebar\n\n\n\n\n\n\n\n '''\n }\n}\n",[59,77160,77161,77167,77171,77178,77185,77192,77196,77208,77222,77226,77235,77242,77260,77264,77271,77280,77284,77289,77293,77297,77301,77305,77314,77321,77357,77361,77368,77376,77380,77393,77397,77401,77405,77409,77418,77424,77438,77442,77446,77450,77454,77459,77468,77474,77487,77491,77498,77503,77513,77517,77521,77530,77534,77538,77547,77551,77555,77564,77568,77572,77581,77585,77589,77598,77602,77606,77615,77619,77623,77632,77636,77640,77649,77653,77657,77667,77671,77675,77684,77688,77692,77701,77705,77709,77713,77717,77726,77732,77744,77749,77754,77759,77770,77774,77780,77788,77792,77796,77800,77804,77808,77812,77816,77820,77825,77829,77833,77837,77841,77845,77849,77853,77858,77862,77866,77870,77874,77878,77882,77886,77890,77894],{"__ignoreMap":57},[62,77162,77163,77165],{"class":64,"line":65},[62,77164,69],{"class":68},[62,77166,76889],{"class":72},[62,77168,77169],{"class":64,"line":76},[62,77170,79],{"emptyLinePlaceholder":13},[62,77172,77173,77175],{"class":64,"line":82},[62,77174,27875],{"class":68},[62,77176,77177],{"class":72}," grails.test.mixin.TestFor\n",[62,77179,77180,77182],{"class":64,"line":89},[62,77181,27875],{"class":68},[62,77183,77184],{"class":72}," spock.lang.Specification\n",[62,77186,77187,77189],{"class":64,"line":95},[62,77188,27875],{"class":68},[62,77190,77191],{"class":72}," spock.lang.Unroll\n",[62,77193,77194],{"class":64,"line":101},[62,77195,79],{"emptyLinePlaceholder":13},[62,77197,77198,77201,77203,77206],{"class":64,"line":107},[62,77199,77200],{"class":68},"@TestFor",[62,77202,2109],{"class":72},[62,77204,77205],{"class":68},"BootstrapTagLib",[62,77207,2212],{"class":72},[62,77209,77210,77212,77215,77217,77220],{"class":64,"line":113},[62,77211,11671],{"class":68},[62,77213,77214],{"class":122}," BootstrapTagLibSpec",[62,77216,8537],{"class":68},[62,77218,77219],{"class":122}," Specification",[62,77221,126],{"class":72},[62,77223,77224],{"class":64,"line":129},[62,77225,79],{"emptyLinePlaceholder":13},[62,77227,77228,77230,77233],{"class":64,"line":134},[62,77229,11710],{"class":68},[62,77231,77232],{"class":1675}," \"No arguments passed to container results in class of container\"",[62,77234,206],{"class":72},[62,77236,77237,77240],{"class":64,"line":156},[62,77238,77239],{"class":149}," given",[62,77241,11005],{"class":72},[62,77243,77244,77247,77250,77252,77255,77258],{"class":64,"line":161},[62,77245,77246],{"class":68}," def",[62,77248,77249],{"class":72}," output ",[62,77251,146],{"class":68},[62,77253,77254],{"class":72}," applyTemplate(",[62,77256,77257],{"class":1675},"'body'",[62,77259,2212],{"class":72},[62,77261,77262],{"class":64,"line":167},[62,77263,79],{"emptyLinePlaceholder":13},[62,77265,77266,77269],{"class":64,"line":173},[62,77267,77268],{"class":149}," expect",[62,77270,11005],{"class":72},[62,77272,77273,77276,77278],{"class":64,"line":179},[62,77274,77275],{"class":72}," output ",[62,77277,28328],{"class":68},[62,77279,76980],{"class":1675},[62,77281,77282],{"class":64,"line":185},[62,77283,79],{"emptyLinePlaceholder":13},[62,77285,77286],{"class":64,"line":191},[62,77287,77288],{"class":1675},"body\n",[62,77290,77291],{"class":64,"line":209},[62,77292,79],{"emptyLinePlaceholder":13},[62,77294,77295],{"class":64,"line":220},[62,77296,54230],{"class":1675},[62,77298,77299],{"class":64,"line":226},[62,77300,223],{"class":72},[62,77302,77303],{"class":64,"line":231},[62,77304,79],{"emptyLinePlaceholder":13},[62,77306,77307,77309,77312],{"class":64,"line":236},[62,77308,11710],{"class":68},[62,77310,77311],{"class":1675}," \"When container argument fluid is true class should equal container-fluid\"",[62,77313,57791],{"class":72},[62,77315,77316,77319],{"class":64,"line":242},[62,77317,77318],{"class":149}," when",[62,77320,11005],{"class":72},[62,77322,77323,77325,77327,77329,77331,77334,77336,77339,77341,77344,77347,77350,77352,77354],{"class":64,"line":247},[62,77324,77246],{"class":68},[62,77326,77249],{"class":72},[62,77328,146],{"class":68},[62,77330,77254],{"class":72},[62,77332,77333],{"class":1675},"'body",[62,77335,54363],{"class":149},[62,77337,77338],{"class":1675},"goes",[62,77340,54363],{"class":149},[62,77342,77343],{"class":1675},"here'",[62,77345,77346],{"class":72},",\\[fluid\\",[62,77348,77349],{"class":149},"_var",[62,77351,1266],{"class":72},[62,77353,21775],{"class":149},[62,77355,77356],{"class":72},"\\])\n",[62,77358,77359],{"class":64,"line":252},[62,77360,79],{"emptyLinePlaceholder":13},[62,77362,77363,77366],{"class":64,"line":257},[62,77364,77365],{"class":149}," then",[62,77367,11005],{"class":72},[62,77369,77370,77372,77374],{"class":64,"line":271},[62,77371,77275],{"class":72},[62,77373,28328],{"class":68},[62,77375,76980],{"class":1675},[62,77377,77378],{"class":64,"line":281},[62,77379,79],{"emptyLinePlaceholder":13},[62,77381,77382,77384,77386,77388,77390],{"class":64,"line":286},[62,77383,11414],{"class":1675},[62,77385,54363],{"class":149},[62,77387,77338],{"class":1675},[62,77389,54363],{"class":149},[62,77391,77392],{"class":1675},"here\n",[62,77394,77395],{"class":64,"line":291},[62,77396,79],{"emptyLinePlaceholder":13},[62,77398,77399],{"class":64,"line":296},[62,77400,54230],{"class":1675},[62,77402,77403],{"class":64,"line":302},[62,77404,223],{"class":72},[62,77406,77407],{"class":64,"line":308},[62,77408,79],{"emptyLinePlaceholder":13},[62,77410,77411,77413,77416],{"class":64,"line":314},[62,77412,11710],{"class":68},[62,77414,77415],{"class":1675}," \"Check that our row tag creates the correct output\"",[62,77417,57791],{"class":72},[62,77419,77420,77422],{"class":64,"line":320},[62,77421,77268],{"class":149},[62,77423,11005],{"class":72},[62,77425,77426,77429,77432,77434,77436],{"class":64,"line":326},[62,77427,77428],{"class":72}," applyTemplate(",[62,77430,77431],{"class":1675},"''",[62,77433,5024],{"class":72},[62,77435,28328],{"class":68},[62,77437,76980],{"class":1675},[62,77439,77440],{"class":64,"line":338},[62,77441,79],{"emptyLinePlaceholder":13},[62,77443,77444],{"class":64,"line":343},[62,77445,54230],{"class":1675},[62,77447,77448],{"class":64,"line":357},[62,77449,223],{"class":72},[62,77451,77452],{"class":64,"line":366},[62,77453,79],{"emptyLinePlaceholder":13},[62,77455,77456],{"class":64,"line":371},[62,77457,77458],{"class":68}," @Unroll\n",[62,77460,77461,77463,77466],{"class":64,"line":376},[62,77462,11710],{"class":68},[62,77464,77465],{"class":1675}," \"Test all possible values in our grid\"",[62,77467,57791],{"class":72},[62,77469,77470,77472],{"class":64,"line":16333},[62,77471,77268],{"class":149},[62,77473,11005],{"class":72},[62,77475,77476,77478,77480,77482,77484],{"class":64,"line":16349},[62,77477,77428],{"class":72},[62,77479,25895],{"class":1675},[62,77481,5024],{"class":72},[62,77483,28328],{"class":68},[62,77485,77486],{"class":72}," cssClass\n",[62,77488,77489],{"class":64,"line":16365},[62,77490,79],{"emptyLinePlaceholder":13},[62,77492,77493,77496],{"class":64,"line":16381},[62,77494,77495],{"class":149}," where",[62,77497,11005],{"class":72},[62,77499,77500],{"class":64,"line":16402},[62,77501,77502],{"class":72}," cols | cssClass\n",[62,77504,77505,77508,77511],{"class":64,"line":16411},[62,77506,77507],{"class":149}," 1",[62,77509,77510],{"class":72}," | ",[62,77512,54230],{"class":1675},[62,77514,77515],{"class":64,"line":16427},[62,77516,79],{"emptyLinePlaceholder":13},[62,77518,77519],{"class":64,"line":16448},[62,77520,54230],{"class":1675},[62,77522,77523,77526,77528],{"class":64,"line":16457},[62,77524,77525],{"class":149}," 2",[62,77527,77510],{"class":72},[62,77529,54230],{"class":1675},[62,77531,77532],{"class":64,"line":16466},[62,77533,79],{"emptyLinePlaceholder":13},[62,77535,77536],{"class":64,"line":16471},[62,77537,54230],{"class":1675},[62,77539,77540,77543,77545],{"class":64,"line":16487},[62,77541,77542],{"class":149}," 3",[62,77544,77510],{"class":72},[62,77546,54230],{"class":1675},[62,77548,77549],{"class":64,"line":16504},[62,77550,79],{"emptyLinePlaceholder":13},[62,77552,77553],{"class":64,"line":16517},[62,77554,54230],{"class":1675},[62,77556,77557,77560,77562],{"class":64,"line":16523},[62,77558,77559],{"class":149}," 4",[62,77561,77510],{"class":72},[62,77563,54230],{"class":1675},[62,77565,77566],{"class":64,"line":16532},[62,77567,79],{"emptyLinePlaceholder":13},[62,77569,77570],{"class":64,"line":16546},[62,77571,54230],{"class":1675},[62,77573,77574,77577,77579],{"class":64,"line":16557},[62,77575,77576],{"class":149}," 5",[62,77578,77510],{"class":72},[62,77580,54230],{"class":1675},[62,77582,77583],{"class":64,"line":16563},[62,77584,79],{"emptyLinePlaceholder":13},[62,77586,77587],{"class":64,"line":16572},[62,77588,54230],{"class":1675},[62,77590,77591,77594,77596],{"class":64,"line":16581},[62,77592,77593],{"class":149}," 6",[62,77595,77510],{"class":72},[62,77597,54230],{"class":1675},[62,77599,77600],{"class":64,"line":16590},[62,77601,79],{"emptyLinePlaceholder":13},[62,77603,77604],{"class":64,"line":16599},[62,77605,54230],{"class":1675},[62,77607,77608,77611,77613],{"class":64,"line":22013},[62,77609,77610],{"class":149}," 7",[62,77612,77510],{"class":72},[62,77614,54230],{"class":1675},[62,77616,77617],{"class":64,"line":22023},[62,77618,79],{"emptyLinePlaceholder":13},[62,77620,77621],{"class":64,"line":22049},[62,77622,54230],{"class":1675},[62,77624,77625,77628,77630],{"class":64,"line":22054},[62,77626,77627],{"class":149}," 8",[62,77629,77510],{"class":72},[62,77631,54230],{"class":1675},[62,77633,77634],{"class":64,"line":22059},[62,77635,79],{"emptyLinePlaceholder":13},[62,77637,77638],{"class":64,"line":22064},[62,77639,54230],{"class":1675},[62,77641,77642,77645,77647],{"class":64,"line":22073},[62,77643,77644],{"class":149}," 9",[62,77646,77510],{"class":72},[62,77648,54230],{"class":1675},[62,77650,77651],{"class":64,"line":22082},[62,77652,79],{"emptyLinePlaceholder":13},[62,77654,77655],{"class":64,"line":30770},[62,77656,54230],{"class":1675},[62,77658,77659,77662,77665],{"class":64,"line":30775},[62,77660,77661],{"class":149}," 10",[62,77663,77664],{"class":72}," | ",[62,77666,54230],{"class":1675},[62,77668,77669],{"class":64,"line":30791},[62,77670,79],{"emptyLinePlaceholder":13},[62,77672,77673],{"class":64,"line":30808},[62,77674,54230],{"class":1675},[62,77676,77677,77680,77682],{"class":64,"line":30822},[62,77678,77679],{"class":149}," 11",[62,77681,77664],{"class":72},[62,77683,54230],{"class":1675},[62,77685,77686],{"class":64,"line":30836},[62,77687,79],{"emptyLinePlaceholder":13},[62,77689,77690],{"class":64,"line":30845},[62,77691,54230],{"class":1675},[62,77693,77694,77697,77699],{"class":64,"line":30850},[62,77695,77696],{"class":149}," 12",[62,77698,77664],{"class":72},[62,77700,54230],{"class":1675},[62,77702,77703],{"class":64,"line":30866},[62,77704,79],{"emptyLinePlaceholder":13},[62,77706,77707],{"class":64,"line":30889},[62,77708,54230],{"class":1675},[62,77710,77711],{"class":64,"line":30911},[62,77712,223],{"class":72},[62,77714,77715],{"class":64,"line":30933},[62,77716,79],{"emptyLinePlaceholder":13},[62,77718,77719,77721,77724],{"class":64,"line":30955},[62,77720,11710],{"class":68},[62,77722,77723],{"class":1675}," \"When we have a container, row and full layout it is what we expect\"",[62,77725,57791],{"class":72},[62,77727,77728,77730],{"class":64,"line":30977},[62,77729,77239],{"class":149},[62,77731,11005],{"class":72},[62,77733,77734,77736,77739,77741],{"class":64,"line":30986},[62,77735,77246],{"class":68},[62,77737,77738],{"class":72}," tpl ",[62,77740,146],{"class":68},[62,77742,77743],{"class":1675}," '''\n",[62,77745,77746],{"class":64,"line":30991},[62,77747,77748],{"class":1675}," Main Content\n",[62,77750,77751],{"class":64,"line":31007},[62,77752,77753],{"class":1675}," Right Sidebar\n",[62,77755,77756],{"class":64,"line":31021},[62,77757,77758],{"class":1675}," '''\n",[62,77760,77761,77763,77765,77767],{"class":64,"line":31037},[62,77762,77246],{"class":68},[62,77764,77249],{"class":72},[62,77766,146],{"class":68},[62,77768,77769],{"class":72}," applyTemplate(tpl)\n",[62,77771,77772],{"class":64,"line":31051},[62,77773,79],{"emptyLinePlaceholder":13},[62,77775,77776,77778],{"class":64,"line":31065},[62,77777,77268],{"class":149},[62,77779,11005],{"class":72},[62,77781,77782,77784,77786],{"class":64,"line":31085},[62,77783,77275],{"class":72},[62,77785,28328],{"class":68},[62,77787,77743],{"class":1675},[62,77789,77790],{"class":64,"line":31094},[62,77791,79],{"emptyLinePlaceholder":13},[62,77793,77794],{"class":64,"line":31100},[62,77795,79],{"emptyLinePlaceholder":13},[62,77797,77798],{"class":64,"line":31109},[62,77799,79],{"emptyLinePlaceholder":13},[62,77801,77802],{"class":64,"line":31114},[62,77803,79],{"emptyLinePlaceholder":13},[62,77805,77806],{"class":64,"line":31130},[62,77807,79],{"emptyLinePlaceholder":13},[62,77809,77810],{"class":64,"line":31144},[62,77811,79],{"emptyLinePlaceholder":13},[62,77813,77814],{"class":64,"line":31160},[62,77815,79],{"emptyLinePlaceholder":13},[62,77817,77818],{"class":64,"line":31174},[62,77819,79],{"emptyLinePlaceholder":13},[62,77821,77822],{"class":64,"line":31188},[62,77823,77824],{"class":1675},"Main Content\n",[62,77826,77827],{"class":64,"line":31202},[62,77828,79],{"emptyLinePlaceholder":13},[62,77830,77831],{"class":64,"line":31211},[62,77832,79],{"emptyLinePlaceholder":13},[62,77834,77835],{"class":64,"line":31217},[62,77836,79],{"emptyLinePlaceholder":13},[62,77838,77839],{"class":64,"line":31226},[62,77840,79],{"emptyLinePlaceholder":13},[62,77842,77843],{"class":64,"line":31235},[62,77844,79],{"emptyLinePlaceholder":13},[62,77846,77847],{"class":64,"line":31240},[62,77848,79],{"emptyLinePlaceholder":13},[62,77850,77851],{"class":64,"line":31249},[62,77852,79],{"emptyLinePlaceholder":13},[62,77854,77855],{"class":64,"line":31268},[62,77856,77857],{"class":1675},"Right Sidebar\n",[62,77859,77860],{"class":64,"line":31277},[62,77861,79],{"emptyLinePlaceholder":13},[62,77863,77864],{"class":64,"line":31286},[62,77865,79],{"emptyLinePlaceholder":13},[62,77867,77868],{"class":64,"line":64860},[62,77869,79],{"emptyLinePlaceholder":13},[62,77871,77872],{"class":64,"line":64883},[62,77873,79],{"emptyLinePlaceholder":13},[62,77875,77876],{"class":64,"line":64906},[62,77877,79],{"emptyLinePlaceholder":13},[62,77879,77880],{"class":64,"line":64947},[62,77881,79],{"emptyLinePlaceholder":13},[62,77883,77884],{"class":64,"line":64988},[62,77885,79],{"emptyLinePlaceholder":13},[62,77887,77888],{"class":64,"line":65029},[62,77889,77758],{"class":1675},[62,77891,77892],{"class":64,"line":65069},[62,77893,223],{"class":72},[62,77895,77896],{"class":64,"line":65102},[62,77897,379],{"class":72},[1527,77899,77900],{},"html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}html pre.shiki code .spoOn, html code.shiki .spoOn{--shiki-default:#E36209;--shiki-dark:#FFAB70;--shiki-sepia:#E36209}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}",{"title":57,"searchDepth":76,"depth":76,"links":77902},[],{"_id":77904,"path":77905,"title":77906,"description":77907,"meta":77908,"body":77912},"content/blog/2023/06/12/spring-http-interfaces-headers.md","/blog/2023/06/12/spring-http-interfaces-headers","How to add Request Headers using HTTP Interfaces in Spring Boot 3","In this tutorial you will learn how to add a request header when using HTTP Interfaces in Spring Boot 3. This can be useful when you need to add a header to a single request or every single request. You will also learn how to view the headers that are sent to make sure everything is working properly.",{"slug":77909,"date":48207,"published":13,"tags":77910,"author":17,"cover":77911,"excerpt":-1},"spring-http-interfaces-headers",[48209,2925],"./http-interface-request-headers.png",{"type":19,"value":77913,"toc":78073},[77914,77925,77933,77937,77944,77947,77957,77962,77966,77969,77983,77990,77996,78000,78008,78012,78015,78021,78030,78033,78036,78042,78045,78047,78054,78065,78070],[22,77915,77916,77917,77920,77921,77924],{},"In this article, we'll be exploring how to customize ",[646,77918,77919],{},"HTTP interfaces in Spring Boot 3",". This stemmed from a question that popped up during a recent presentation I gave: ",[4534,77922,77923],{},"How do you add headers to requests?"," Be it for a single request, where you may want to add one header and its value, or for multiple or all requests sent to a certain service – how do you accomplish that? This post aims to dive right into these questions!",[22,77926,77927,77928,77932],{},"If you’re new to HTTP Interfaces check out ",[677,77929,38515],{"href":77930,"rel":77931},"https://www.danvega.dev/blog/2023/06/14/spring-http-interfaces/",[681]," for an introduction to what they are and how to get started with them.",[26,77934,77936],{"id":77935},"building-a-demo-project","Building a Demo Project",[22,77938,77939,77940,77943],{},"Primarily, our goal here is to have a service within a Spring application, reaching out to another service in your network, be it a Microservice or a public API. Another question that typically arises in this context is: ",[4534,77941,77942],{},"How do you monitor which headers are being transmitted?"," In the case of a browser application, the DevTools allow us to see the request and response. We'll attempt to tackle this conundrum as well in our session.",[22,77945,77946],{},"Let's dive straight into the code.",[22,77948,77949,77950,77956],{},"Our project was initiated on ",[677,77951,77953],{"href":2901,"rel":77952},[681],[4534,77954,77955],{},"Start Spring IO"," with Maven as the build tool, Java as the language, and the most current version of Spring Boot (3.1.0). Our project name for the purpose of guiding you through this post is \"headers\". Our language of choice for this project is Java 17. The dependencies we have selected for the demo are Spring Web and Web Flux. Potentially, you might select Spring Web for constructing a REST API, but here we turn to Web Flux albeit not constructing a reactive stack as HTTP interfaces utilize the reactive web client under the hood in Spring Boot. This feature does not necessarily have to be blocking or non-blocking; we'll explore an example further down. We'll now generate our project and launch it in IntelliJ IDEA to get things started.",[22,77958,77959],{},[653,77960],{"alt":24606,"src":77961},"/images/blog/2023/06/14/request-headers-start-spring-io.png",[26,77963,77965],{"id":77964},"creating-a-model","Creating a Model",[22,77967,77968],{},"We'll be developing an application within this demo and for that, we need a model. We'll craft a new Java class named ‘Post’, serving as a record in the model package. We’re modelling a blog post here, which includes:",[915,77970,77971,77974,77977,77980],{},[37,77972,77973],{},"Integer ID",[37,77975,77976],{},"Integer user ID",[37,77978,77979],{},"String title",[37,77981,77982],{},"String body",[22,77984,77985,77986,77989],{},"The model was designed this way because the service we would be calling adheres to this context. Following the model creation, a new service package was generated. To further illustrate, our service in question is called ",[4534,77987,77988],{},"JSON Placeholder",". This API provides a collection of resources, one of which has a “/posts” endpoint that syncs well with our established record.",[22,77991,77992,77993,2755],{},"Now we’re all set to formulate a ",[4534,77994,77995],{},"JSON Placeholder Service",[26,77997,77999],{"id":77998},"initiation-of-jsonplaceholderservice","Initiation of JSONPlaceholderService",[22,78001,78002,78003,78007],{},"To get started, we created a Java class and named the interface as JSONPlaceholderService. The main base URL to make a call is \"",[677,78004,78005],{"href":78005,"rel":78006},"http://jsonplaceholder.typicode.com",[681],"\" and the endpoint to fetch posts is \"/posts\". In our simulation, we want to retrieve all posts. Hence, we establish a GET exchange to Posts, and based on the return type provided, we can decide whether our call is \"blocking\" or \"non-blocking\". Here, we don't have to be bothered with blocking or non-blocking status of calls. We set this up by providing a method that simply returns the list of posts when called \"FindAll\". But, the question arises here, how to send a specific header just for this method?",[26,78009,78011],{"id":78010},"including-request-headers","Including Request Headers",[22,78013,78014],{},"There are two possible scenarios; either you may want to send a request header alone with the request, or send a header and a value for all requests. To add a request header to the FindAll method, we need to add a request header annotation to the method and provide the headers that need to be sent with the request. We did that by creating a map of headers and passing it to the FindAll method.",[52,78016,78019],{"className":78017,"code":78018,"language":1727},[1725],"@GetExchange(\"/posts\")\nList\u003CPost> findAll(@RequestHeader Map\u003CString, String> headers);\n",[59,78020,78018],{"__ignoreMap":57},[29685,78022,78023],{},[22,78024,78025,78026,78029],{},"Blockquote: The ",[59,78027,78028],{},"@RequestHeader"," annotation indicates that a method parameter should be bound to a Web request header. The method parameter can be a Map\u003CString, String>, MultiValueMap\u003CString, String>, or HttpHeaders and then the map is populated with all header names and values.",[22,78031,78032],{},"Upon running the application, as we can see in the logs, the headers are getting passed to the request. Voila!",[22,78034,78035],{},"You might wonder, what if we want to pass some headers for all requests? The response to that is pretty simple. We just need to set a default header in the client configuration.",[52,78037,78040],{"className":78038,"code":78039,"language":1727},[1725],".defaultHeaders(headers -> headers.add(\"Spring-Boot-Version\",\"3 10\"))\n",[59,78041,78039],{"__ignoreMap":57},[22,78043,78044],{},"Upon re-running the application, as is evident in the logs, we are able to pass the header with request to all methods.",[26,78046,1499],{"id":1498},[22,78048,78049,78050,78053],{},"In this article we've looked into adding headers to requests, and more specifically, how to do it with ",[646,78051,78052],{},"HTTP clients interface in Spring Boot 3",". The key takeaways are:",[34,78055,78056,78059,78062],{},[37,78057,78058],{},"To add a header to each request, you can add a default header in client configuration.",[37,78060,78061],{},"To add a header to a specific request, you can add a request header annotation to the method along with the headers.",[37,78063,78064],{},"To monitor headers in your logs, change the logging level for the web to 'Trace'.",[22,78066,78067],{},[4534,78068,78069],{},"Note: Please note that the logging level should be reverted back for production.",[22,78071,78072],{},"Hope this post has been of assistance to you. Keep an eye out for more such informative posts. Until next time, happy coding!",{"title":57,"searchDepth":76,"depth":76,"links":78074},[78075,78076,78077,78078,78079],{"id":77935,"depth":76,"text":77936},{"id":77964,"depth":76,"text":77965},{"id":77998,"depth":76,"text":77999},{"id":78010,"depth":76,"text":78011},{"id":1498,"depth":76,"text":1499},{"_id":78081,"path":78082,"title":78083,"description":78084,"meta":78085,"body":78089},"content/blog/2023/05/19/aws-lambda-java-17.md","/blog/2023/05/19/aws-lambda-java-17","Java 17 🤝 AWS Lambda: Creating Serverless Functions in Java & Spring with Java 17","AWS has made a big announcement recently - Java 17 runtime is now supported on AWS Lambda. What this means is, you now have the freedom to write your lambda functions using Java in Spring and specifically target JDK 17.",{"slug":78086,"date":78087,"published":13,"tags":78088,"author":17,"cover":47078,"excerpt":-1},"aws-lambda-java-17","2023-05-19T08:00:00.000Z",[2925,47077,15],{"type":19,"value":78090,"toc":78651},[78091,78094,78097,78101,78104,78107,78113,78116,78119,78134,78137,78173,78176,78326,78332,78437,78449,78458,78462,78465,78471,78474,78477,78483,78486,78489,78493,78501,78504,78509,78519,78578,78587,78610,78621,78623,78626,78642,78648],[22,78092,78093],{},"AWS has made a big announcement recently: Java 17 runtime is now supported on AWS Lambda. What this means is, you now have the freedom to write your lambda functions using Java in Spring and specifically target JDK 17.",[22,78095,78096],{},"This announcement comes with a significant improvement as the base requirement for Spring Boot 3 is at least Java 17. So, prior to this, we couldn't use Spring Boot 3 for writing our serverless functions. But now, we can! Let's get started with writing some code!",[26,78098,78100],{"id":78099},"creating-a-new-project-using-java","Creating a New Project Using Java",[22,78102,78103],{},"I will first show you how to develop a new project utilizing Java only. Let's use the AWS Core Lambda library to create a new AWS Lambda function in Java and send the code to AWS Lambda. Fair warning, Java 17 works beautifully with AWS Lambda, but let's see it in action.",[22,78105,78106],{},"First, I will create a new project in my IntelliJ IDE (although, you can work with any IDE or text editor of your choice). This new project will be named 'Hello, Java 17' and I will save it in my downloads folder.",[22,78108,78109],{},[653,78110],{"alt":78111,"src":78112},"Hello Java 17 Project in IntelliJ","/images/blog/2023/05/19/hello-java-17-pom.png",[22,78114,78115],{},"This Java project is a Maven project that uses the Coretto 17 version of JDK. My group ID will be 'dev.danvega' and the artifact will be 'hello-java-17'. On creating this project, I will open it in IntelliJ.",[22,78117,78118],{},"Here is a cool thing. IntelliJ IDE automatically sets the Maven compiler source and target to Java 17. This is a nice a time-saver.",[52,78120,78122],{"className":1769,"code":78121,"language":1771,"meta":57,"style":57},"\u003Cmaven.compiler.source>17\u003C/maven.compiler.source>\n\u003Cmaven.compiler.target>17\u003C/maven.compiler.target>\n",[59,78123,78124,78129],{"__ignoreMap":57},[62,78125,78126],{"class":64,"line":65},[62,78127,78128],{},"\u003Cmaven.compiler.source>17\u003C/maven.compiler.source>\n",[62,78130,78131],{"class":64,"line":76},[62,78132,78133],{},"\u003Cmaven.compiler.target>17\u003C/maven.compiler.target>\n",[22,78135,78136],{},"Next, let's add a dependency block. This block will come from AWS Lambda Java Core.",[52,78138,78140],{"className":1769,"code":78139,"language":1771,"meta":57,"style":57},"\u003Cdependencies>\n \u003Cdependency>\n \u003CgroupId>com.amazonaws\u003C/groupId>\n \u003CartifactId>aws-lambda-java-core\u003C/artifactId>\n \u003Cversion>1.2.1\u003C/version>\n \u003C/dependency>\n\u003C/dependencies>\n",[59,78141,78142,78146,78150,78155,78160,78165,78169],{"__ignoreMap":57},[62,78143,78144],{"class":64,"line":65},[62,78145,56803],{},[62,78147,78148],{"class":64,"line":76},[62,78149,56808],{},[62,78151,78152],{"class":64,"line":82},[62,78153,78154],{}," \u003CgroupId>com.amazonaws\u003C/groupId>\n",[62,78156,78157],{"class":64,"line":89},[62,78158,78159],{}," \u003CartifactId>aws-lambda-java-core\u003C/artifactId>\n",[62,78161,78162],{"class":64,"line":95},[62,78163,78164],{}," \u003Cversion>1.2.1\u003C/version>\n",[62,78166,78167],{"class":64,"line":101},[62,78168,56823],{},[62,78170,78171],{"class":64,"line":107},[62,78172,56916],{},[22,78174,78175],{},"On refreshing Maven, you will notice that everything is now in place and we're ready to write some code. We can create a new Java class named 'SimpleHandler'. Here, I will implement the request handler from AWS and create methods to get the inputs.",[52,78177,78179],{"className":54,"code":78178,"language":56,"meta":57,"style":57},"package dev.danvega.hj17;\n\nimport com.amazonaws.services.lambda.runtime.Context;\nimport com.amazonaws.services.lambda.runtime.LambdaLogger;\nimport com.amazonaws.services.lambda.runtime.RequestHandler;\n\npublic class SimpleHandler implements RequestHandler\u003CString,String> {\n\n public String handleRequest(String input, Context context) {\n LambdaLogger logger = context.getLogger();\n logger.log(\"JDK Version: \" + System.getProperty(\"java.version\"));\n return input.toUpperCase();\n }\n\n}\n",[59,78180,78181,78188,78192,78199,78206,78213,78217,78241,78245,78265,78279,78303,78314,78318,78322],{"__ignoreMap":57},[62,78182,78183,78185],{"class":64,"line":65},[62,78184,69],{"class":68},[62,78186,78187],{"class":72}," dev.danvega.hj17;\n",[62,78189,78190],{"class":64,"line":76},[62,78191,79],{"emptyLinePlaceholder":13},[62,78193,78194,78196],{"class":64,"line":82},[62,78195,27875],{"class":68},[62,78197,78198],{"class":72}," com.amazonaws.services.lambda.runtime.Context;\n",[62,78200,78201,78203],{"class":64,"line":89},[62,78202,27875],{"class":68},[62,78204,78205],{"class":72}," com.amazonaws.services.lambda.runtime.LambdaLogger;\n",[62,78207,78208,78210],{"class":64,"line":95},[62,78209,27875],{"class":68},[62,78211,78212],{"class":72}," com.amazonaws.services.lambda.runtime.RequestHandler;\n",[62,78214,78215],{"class":64,"line":101},[62,78216,79],{"emptyLinePlaceholder":13},[62,78218,78219,78221,78223,78226,78228,78231,78233,78235,78237,78239],{"class":64,"line":107},[62,78220,116],{"class":68},[62,78222,119],{"class":68},[62,78224,78225],{"class":122}," SimpleHandler",[62,78227,13520],{"class":68},[62,78229,78230],{"class":122}," RequestHandler",[62,78232,760],{"class":72},[62,78234,973],{"class":68},[62,78236,32225],{"class":72},[62,78238,973],{"class":68},[62,78240,8552],{"class":72},[62,78242,78243],{"class":64,"line":113},[62,78244,79],{"emptyLinePlaceholder":13},[62,78246,78247,78249,78251,78253,78255,78257,78260,78263],{"class":64,"line":129},[62,78248,194],{"class":68},[62,78250,2469],{"class":72},[62,78252,48132],{"class":122},[62,78254,1049],{"class":72},[62,78256,8890],{"class":889},[62,78258,78259],{"class":72},", Context ",[62,78261,78262],{"class":889},"context",[62,78264,768],{"class":72},[62,78266,78267,78270,78272,78275,78277],{"class":64,"line":134},[62,78268,78269],{"class":72}," LambdaLogger logger ",[62,78271,146],{"class":68},[62,78273,78274],{"class":72}," context.",[62,78276,3069],{"class":122},[62,78278,822],{"class":72},[62,78280,78281,78283,78285,78287,78290,78292,78294,78296,78298,78301],{"class":64,"line":156},[62,78282,61964],{"class":72},[62,78284,58271],{"class":122},[62,78286,2109],{"class":72},[62,78288,78289],{"class":1675},"\"JDK Version: \"",[62,78291,4507],{"class":68},[62,78293,12713],{"class":72},[62,78295,14926],{"class":122},[62,78297,2109],{"class":72},[62,78299,78300],{"class":1675},"\"java.version\"",[62,78302,6979],{"class":72},[62,78304,78305,78307,78309,78312],{"class":64,"line":161},[62,78306,360],{"class":68},[62,78308,63117],{"class":72},[62,78310,78311],{"class":122},"toUpperCase",[62,78313,822],{"class":72},[62,78315,78316],{"class":64,"line":167},[62,78317,223],{"class":72},[62,78319,78320],{"class":64,"line":173},[62,78321,79],{"emptyLinePlaceholder":13},[62,78323,78324],{"class":64,"line":179},[62,78325,379],{"class":72},[22,78327,78328,78329,2755],{},"With the SimpleHandler created, we can use Maven to create the Java Archive (.jar) that we will then upload to AWS as a Lambda function. To do that, build the Maven Shade plugin by inputting the command - ",[59,78330,78331],{},"clean package",[52,78333,78335],{"className":1769,"code":78334,"language":1771,"meta":57,"style":57},"\u003Cbuild>\n \u003Cplugins>\n \u003Cplugin>\n \u003CgroupId>org.apache.maven.plugins\u003C/groupId>\n \u003CartifactId>maven-shade-plugin\u003C/artifactId>\n \u003Cversion>3.2.4\u003C/version>\n \u003Cconfiguration>\n \u003CcreateDependencyReducedPom>false\u003C/createDependencyReducedPom>\n \u003C/configuration>\n \u003Cexecutions>\n \u003Cexecution>\n \u003Cphase>package\u003C/phase>\n \u003Cgoals>\n \u003Cgoal>shade\u003C/goal>\n \u003C/goals>\n \u003C/execution>\n \u003C/executions>\n \u003C/plugin>\n \u003C/plugins>\n\u003C/build>\n",[59,78336,78337,78342,78347,78352,78357,78362,78367,78372,78377,78382,78387,78392,78397,78402,78407,78412,78417,78422,78427,78432],{"__ignoreMap":57},[62,78338,78339],{"class":64,"line":65},[62,78340,78341],{},"\u003Cbuild>\n",[62,78343,78344],{"class":64,"line":76},[62,78345,78346],{}," \u003Cplugins>\n",[62,78348,78349],{"class":64,"line":82},[62,78350,78351],{}," \u003Cplugin>\n",[62,78353,78354],{"class":64,"line":89},[62,78355,78356],{}," \u003CgroupId>org.apache.maven.plugins\u003C/groupId>\n",[62,78358,78359],{"class":64,"line":95},[62,78360,78361],{}," \u003CartifactId>maven-shade-plugin\u003C/artifactId>\n",[62,78363,78364],{"class":64,"line":101},[62,78365,78366],{}," \u003Cversion>3.2.4\u003C/version>\n",[62,78368,78369],{"class":64,"line":107},[62,78370,78371],{}," \u003Cconfiguration>\n",[62,78373,78374],{"class":64,"line":113},[62,78375,78376],{}," \u003CcreateDependencyReducedPom>false\u003C/createDependencyReducedPom>\n",[62,78378,78379],{"class":64,"line":129},[62,78380,78381],{}," \u003C/configuration>\n",[62,78383,78384],{"class":64,"line":134},[62,78385,78386],{}," \u003Cexecutions>\n",[62,78388,78389],{"class":64,"line":156},[62,78390,78391],{}," \u003Cexecution>\n",[62,78393,78394],{"class":64,"line":161},[62,78395,78396],{}," \u003Cphase>package\u003C/phase>\n",[62,78398,78399],{"class":64,"line":167},[62,78400,78401],{}," \u003Cgoals>\n",[62,78403,78404],{"class":64,"line":173},[62,78405,78406],{}," \u003Cgoal>shade\u003C/goal>\n",[62,78408,78409],{"class":64,"line":179},[62,78410,78411],{}," \u003C/goals>\n",[62,78413,78414],{"class":64,"line":185},[62,78415,78416],{}," \u003C/execution>\n",[62,78418,78419],{"class":64,"line":191},[62,78420,78421],{}," \u003C/executions>\n",[62,78423,78424],{"class":64,"line":209},[62,78425,78426],{}," \u003C/plugin>\n",[62,78428,78429],{"class":64,"line":220},[62,78430,78431],{}," \u003C/plugins>\n",[62,78433,78434],{"class":64,"line":226},[62,78435,78436],{},"\u003C/build>\n",[52,78438,78439],{"className":1663,"code":3632,"language":1665,"meta":57,"style":57},[59,78440,78441],{"__ignoreMap":57},[62,78442,78443,78445,78447],{"class":64,"line":65},[62,78444,3639],{"class":122},[62,78446,3642],{"class":1675},[62,78448,3645],{"class":1675},[22,78450,3521,78451,78454,78455,78457],{},[59,78452,78453],{},"clean"," command cleans the Maven project by deleting the target directory. The ",[59,78456,69],{}," command, on the other hand, packages compiled source code into its distributable format. The jar will be saved in the target directory as 'hello-java-17-1.0-SNAPSHOT.jar'.",[26,78459,78461],{"id":78460},"uploading-the-jar-to-aws-lambda","Uploading the Jar to AWS Lambda",[22,78463,78464],{},"Now that we have our jar ready, we can go ahead and upload it to AWS Lambda. Let's create a new function from scratch and name it 'Hello, Java 17'. The exciting part here is the runtime selection. Here, you can choose Java 17.",[22,78466,78467],{},[653,78468],{"alt":78469,"src":78470},"AWS Lambda Create Function","/images/blog/2023/05/19/aws-create-function.png",[22,78472,78473],{},"For the purposes of this tutorial, I'll keep the architecture at X86 - this is because if you want to use something like SnapStart, you’ll need to use X86 as it currently does not support ARM64. Having selected the runtime, create the function.",[22,78475,78476],{},"Next, let's upload the jar file using the 'Upload from .zip or .jar file' option and save it. Now, edit the runtime settings. Here, you will need to provide the handler as 'package name.class name::method name'.",[52,78478,78481],{"className":78479,"code":78480,"language":1727},[1725],"dev.danvega.HelloJava17.SimpleHandler::handleRequest\n",[59,78482,78480],{"__ignoreMap":57},[22,78484,78485],{},"Save the settings and test the code. Create a test event with a string (e.g., 'hello Java 17'). You should see your string returned in uppercase, proving that the function works and that yes, we are indeed running on Java 17.",[22,78487,78488],{},"With the simple Java application created successfully, let's try using Spring Boot.",[26,78490,78492],{"id":78491},"creating-a-project-using-spring-boot","Creating a Project Using Spring Boot",[22,78494,78495,78496,78500],{},"Next, is to create a new project using Spring Boot 3. To do so, we will head over to ",[677,78497,78499],{"href":2901,"rel":78498},[681],"'start.spring.io'",", a handy website for quickly setting up a Spring Boot Project.",[22,78502,78503],{},"On the website, we can select the necessary configurations for our project. For this project, we'll be utilizing Java 17 and Spring Boot 3. For dependencies, select 'Web' and 'Spring Cloud Function'.",[22,78505,78506],{},[653,78507],{"alt":24606,"src":78508},"/images/blog/2023/05/19/scf-spring-init.png",[22,78510,78511,78512,78515,78516,78518],{},"Once the project parameters are set up, you can generate the project. The resulting .zip file can be opened in your preferred IDE (for this project, we are using IntelliJ IDEA). In the application, the ",[59,78513,78514],{},"reverse"," function simply reverses the given input string. Here is the code for the ",[59,78517,78514],{}," function:",[52,78520,78522],{"className":54,"code":78521,"language":56,"meta":57,"style":57},"@Bean\npublic Function\u003CString, String> reverse() {\n return input -> new StringBuilder(input).reverse().toString();\n}\n",[59,78523,78524,78530,78549,78574],{"__ignoreMap":57},[62,78525,78526,78528],{"class":64,"line":65},[62,78527,942],{"class":72},[62,78529,2146],{"class":68},[62,78531,78532,78534,78537,78539,78542,78544,78547],{"class":64,"line":76},[62,78533,116],{"class":68},[62,78535,78536],{"class":72}," Function",[62,78538,760],{"class":68},[62,78540,78541],{"class":72},"String, String",[62,78543,2583],{"class":68},[62,78545,78546],{"class":122}," reverse",[62,78548,206],{"class":72},[62,78550,78551,78553,78556,78558,78560,78563,78566,78568,78570,78572],{"class":64,"line":82},[62,78552,2599],{"class":68},[62,78554,78555],{"class":72}," input ",[62,78557,800],{"class":68},[62,78559,466],{"class":68},[62,78561,78562],{"class":122}," StringBuilder",[62,78564,78565],{"class":72},"(input).",[62,78567,78514],{"class":122},[62,78569,3229],{"class":72},[62,78571,23175],{"class":122},[62,78573,822],{"class":72},[62,78575,78576],{"class":64,"line":89},[62,78577,379],{"class":72},[22,78579,78580,78581,78584,78585,2004],{},"This function creates a new string builder with the input string, reverses the string, and then converts it back to a string. Next, you need to build an artifact and target the AWS Lambda runtime. To do so, include the ",[59,78582,78583],{},"spring-cloud-function-adapter-aws"," in your ",[59,78586,1765],{},[52,78588,78590],{"className":1769,"code":78589,"language":1771,"meta":57,"style":57},"\u003Cdependency>\n \u003CgroupId>org.springframework.cloud\u003C/groupId>\n \u003CartifactId>spring-cloud-function-adapter-aws\u003C/artifactId>\n\u003C/dependency>\n",[59,78591,78592,78596,78601,78606],{"__ignoreMap":57},[62,78593,78594],{"class":64,"line":65},[62,78595,46425],{},[62,78597,78598],{"class":64,"line":76},[62,78599,78600],{}," \u003CgroupId>org.springframework.cloud\u003C/groupId>\n",[62,78602,78603],{"class":64,"line":82},[62,78604,78605],{}," \u003CartifactId>spring-cloud-function-adapter-aws\u003C/artifactId>\n",[62,78607,78608],{"class":64,"line":89},[62,78609,46445],{},[22,78611,78612,78613,78616,78617,78620],{},"With the Maven Shade plugin in place, you can run the ",[59,78614,78615],{},"mvn clean package"," command to build the JAR file. When this is complete, you should see a new JAR file with ",[59,78618,78619],{},"aws"," in the name, which is what you can upload to AWS. The process is the same as before, so we won't repeat it here.",[26,78622,1499],{"id":1498},[22,78624,78625],{},"If you're looking for the source code for both of these demos you can find them below:",[915,78627,78628,78635],{},[37,78629,78630],{},[677,78631,78634],{"href":78632,"rel":78633},"https://github.com/danvega/hello-java-17",[681],"GitHub Repo - Java 17",[37,78636,78637],{},[677,78638,78641],{"href":78639,"rel":78640},"https://github.com/danvega/scf-17",[681],"GitHub Repo - Spring Cloud Function",[22,78643,78644,78645,6277],{},"To sum it all up, the support for Java 17 on AWS Lambda opens up new opportunities for both Java and Spring development. It's really exciting to see what we will be able to build with this at our disposal. As always, ",[646,78646,78647],{},"Happy Coding Friends",[1527,78649,78650],{},"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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .spoOn, html code.shiki .spoOn{--shiki-default:#E36209;--shiki-dark:#FFAB70;--shiki-sepia:#E36209}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}",{"title":57,"searchDepth":76,"depth":76,"links":78652},[78653,78654,78655,78656],{"id":78099,"depth":76,"text":78100},{"id":78460,"depth":76,"text":78461},{"id":78491,"depth":76,"text":78492},{"id":1498,"depth":76,"text":1499},{"_id":78658,"path":78659,"title":78660,"description":78661,"meta":78662,"body":78667},"content/blog/2023/05/03/spring-session-introduction.md","/blog/2023/05/03/spring-session-introduction","Demystifying Spring Session: A Comprehensive Introduction for Java Developers!","In this tutorial, we'll explore the powerful features of Spring Session and learn how to effectively manage user sessions in your Java web applications.",{"slug":78663,"date":78664,"published":13,"tags":78665,"author":17,"cover":78666,"excerpt":-1},"spring-session-introduction","2023-05-03T08:00:00.000Z",[10914],"./spring-session-thumbnail.png",{"type":19,"value":78668,"toc":79251},[78669,78673,78676,78687,78690,78697,78701,78707,78722,78728,78731,78735,78744,78831,78838,78841,78844,78847,78851,78854,78857,78902,78905,78954,78957,78980,78986,79227,79230,79232,79235,79238,79246,79248],[26,78670,78672],{"id":78671},"understanding-spring-session-and-its-benefits","Understanding Spring Session and its Benefits",[22,78674,78675],{},"In this tutorial, we're going to discuss:",[915,78677,78678,78681,78684],{},[37,78679,78680],{},"What is a session on the web?",[37,78682,78683],{},"What is Spring Session?",[37,78685,78686],{},"Why might you want to use it in your next Spring Boot application?",[22,78688,78689],{},"This discussion was prompted by a recent episode of the Spring Office Hours podcast, where Spring Session was the featured topic. In this blog post, we'll explore some of the benefits that Spring Session brings to the table.",[22,78691,78692,78693,78696],{},"To understand Spring Session better, let's create a new Spring Boot application using ",[677,78694,24606],{"href":1744,"rel":78695},[681],". We'll begin with a simple application that features web security and gradually include additional dependencies such as Spring Session and Redis support.",[26,78698,78700],{"id":78699},"creating-a-spring-boot-application","Creating a Spring Boot Application",[22,78702,78703,78704,78706],{},"Start by creating a new project at ",[59,78705,2903],{},". Choose Java as the language, Maven as the build tool, and Spring Boot 3.1 (or any later version). Next, add the following dependencies:",[915,78708,78709,78712,78715,78717,78719],{},[37,78710,78711],{},"Spring Boot 3.1",[37,78713,78714],{},"Web",[37,78716,10914],{},[37,78718,54895],{},[37,78720,78721],{},"Spring Data Redis",[22,78723,78724],{},[653,78725],{"alt":78726,"src":78727},"Spring Initalizr","/images/blog/2023/05/03/start-spring-io.png",[22,78729,78730],{},"Before diving into how Spring Session can help with managing user session information, let's comment out the Spring Session and Spring Data references and build a basic application without them.",[26,78732,78734],{"id":78733},"default-behavior-of-spring-security","Default Behavior of Spring Security",[22,78736,15404,78737,19906,78740,78743],{},[59,78738,78739],{},"HomeController",[59,78741,78742],{},"home()"," method that simply returns a message containing the name of the currently logged-in user.",[52,78745,78747],{"className":54,"code":78746,"language":56,"meta":57,"style":57},"@RestController\npublic class HomeController {\n\n @GetMapping(\"/\")\n public String home(Principal principal, HttpSession session) {\n return \"Hello, \" + principal.getName();\n }\n\n}\n",[59,78748,78749,78755,78765,78769,78781,78803,78819,78823,78827],{"__ignoreMap":57},[62,78750,78751,78753],{"class":64,"line":65},[62,78752,942],{"class":72},[62,78754,2342],{"class":68},[62,78756,78757,78759,78761,78763],{"class":64,"line":76},[62,78758,116],{"class":68},[62,78760,119],{"class":68},[62,78762,25636],{"class":122},[62,78764,126],{"class":72},[62,78766,78767],{"class":64,"line":82},[62,78768,79],{"emptyLinePlaceholder":13},[62,78770,78771,78773,78775,78777,78779],{"class":64,"line":89},[62,78772,2143],{"class":72},[62,78774,2548],{"class":68},[62,78776,2109],{"class":72},[62,78778,15635],{"class":1675},[62,78780,2212],{"class":72},[62,78782,78783,78785,78787,78789,78792,78795,78798,78801],{"class":64,"line":95},[62,78784,194],{"class":68},[62,78786,2469],{"class":72},[62,78788,18647],{"class":122},[62,78790,78791],{"class":72},"(Principal ",[62,78793,78794],{"class":889},"principal",[62,78796,78797],{"class":72},", HttpSession ",[62,78799,78800],{"class":889},"session",[62,78802,768],{"class":72},[62,78804,78805,78807,78810,78812,78815,78817],{"class":64,"line":101},[62,78806,360],{"class":68},[62,78808,78809],{"class":1675}," \"Hello, \"",[62,78811,4507],{"class":68},[62,78813,78814],{"class":72}," principal.",[62,78816,12678],{"class":122},[62,78818,822],{"class":72},[62,78820,78821],{"class":64,"line":107},[62,78822,223],{"class":72},[62,78824,78825],{"class":64,"line":113},[62,78826,79],{"emptyLinePlaceholder":13},[62,78828,78829],{"class":64,"line":129},[62,78830,379],{"class":72},[22,78832,78833,78834,78837],{},"Once the application is running, navigate to ",[677,78835,17969],{"href":17969,"rel":78836},[681],". You'll be presented with a form login, which is the default security behavior provided by Spring Security.",[22,78839,78840],{},"However, there is a problem with the default session handling. When the application is restarted, you will have to log in again, as the previous session data is lost. This is because the session is tied to the application server (in our case, Tomcat) and is not persisted across server restarts.",[22,78842,78843],{},"This can be even more problematic in a horizontally scaled environment where multiple instances of an application are running. In such cases, a user's session might not be available if they're connected to a different instance of the application.",[22,78845,78846],{},"This is where Spring Session comes into play.",[26,78848,78850],{"id":78849},"introducing-spring-session-and-redis","Introducing Spring Session and Redis",[22,78852,78853],{},"Spring Session provides a solution for managing user session information in a way that is not tied to the application server. In our example, we'll use Redis as a data store for session management.",[22,78855,78856],{},"First, uncomment the lines that include the Spring Session and Spring Data Redis dependencies:",[52,78858,78860],{"className":1769,"code":78859,"language":1771,"meta":57,"style":57},"\u003C!-- Spring Session -->\n\u003Cdependency>\n \u003CgroupId>org.springframework.session\u003C/groupId>\n \u003CartifactId>spring-session-data-redis\u003C/artifactId>\n\u003C/dependency>\n\u003Cdependency>\n \u003CgroupId>org.springframework.boot\u003C/groupId>\n \u003CartifactId>spring-boot-starter-data-redis\u003C/artifactId>\n\u003C/dependency>\n\n",[59,78861,78862,78867,78871,78876,78881,78885,78889,78893,78898],{"__ignoreMap":57},[62,78863,78864],{"class":64,"line":65},[62,78865,78866],{},"\u003C!-- Spring Session -->\n",[62,78868,78869],{"class":64,"line":76},[62,78870,46425],{},[62,78872,78873],{"class":64,"line":82},[62,78874,78875],{}," \u003CgroupId>org.springframework.session\u003C/groupId>\n",[62,78877,78878],{"class":64,"line":89},[62,78879,78880],{}," \u003CartifactId>spring-session-data-redis\u003C/artifactId>\n",[62,78882,78883],{"class":64,"line":95},[62,78884,46445],{},[62,78886,78887],{"class":64,"line":101},[62,78888,46425],{},[62,78890,78891],{"class":64,"line":107},[62,78892,54736],{},[62,78894,78895],{"class":64,"line":113},[62,78896,78897],{}," \u003CartifactId>spring-boot-starter-data-redis\u003C/artifactId>\n",[62,78899,78900],{"class":64,"line":129},[62,78901,46445],{},[22,78903,78904],{},"Next, we'll set up a Docker Compose file to run a Redis container:",[52,78906,78908],{"className":10993,"code":78907,"language":10995,"meta":57,"style":57},"version: \"3.1\"\nservices:\n redis:\n image: redis:alpine\n ports:\n - \"6379:6379\"\n",[59,78909,78910,78919,78925,78932,78941,78947],{"__ignoreMap":57},[62,78911,78912,78914,78916],{"class":64,"line":65},[62,78913,1933],{"class":1780},[62,78915,3696],{"class":72},[62,78917,78918],{"class":1675},"\"3.1\"\n",[62,78920,78921,78923],{"class":64,"line":76},[62,78922,17489],{"class":1780},[62,78924,11005],{"class":72},[62,78926,78927,78930],{"class":64,"line":82},[62,78928,78929],{"class":1780}," redis",[62,78931,11005],{"class":72},[62,78933,78934,78936,78938],{"class":64,"line":89},[62,78935,17503],{"class":1780},[62,78937,3696],{"class":72},[62,78939,78940],{"class":1675},"redis:alpine\n",[62,78942,78943,78945],{"class":64,"line":95},[62,78944,17550],{"class":1780},[62,78946,11005],{"class":72},[62,78948,78949,78951],{"class":64,"line":101},[62,78950,17557],{"class":72},[62,78952,78953],{"class":1675},"\"6379:6379\"\n",[22,78955,78956],{},"With this configuration in place, our Spring Boot application will automatically start the Redis container when it starts up. This allows us to persist session data across application restarts and share it across multiple instances.Depending on the version of Spring Boot you're on you might need to add the following dependency.",[52,78958,78960],{"className":1769,"code":78959,"language":1771,"meta":57,"style":57},"\u003Cdependency>\n \u003CgroupId>org.springframework.boot\u003C/groupId>\n \u003CartifactId>spring-boot-docker-compose\u003C/artifactId>\n\u003C/dependency>\n",[59,78961,78962,78966,78971,78976],{"__ignoreMap":57},[62,78963,78964],{"class":64,"line":65},[62,78965,46425],{},[62,78967,78968],{"class":64,"line":76},[62,78969,78970],{}," \u003CgroupId>org.springframework.boot\u003C/groupId>\n",[62,78972,78973],{"class":64,"line":82},[62,78974,78975],{}," \u003CartifactId>spring-boot-docker-compose\u003C/artifactId>\n",[62,78977,78978],{"class":64,"line":89},[62,78979,46445],{},[22,78981,78982,78983,78985],{},"Modify the ",[59,78984,78739],{}," to track a user's session and display a view counter that increments each time the user accesses the home page. The view counter's value should be stored as a session attribute.",[52,78987,78989],{"className":54,"code":78988,"language":56,"meta":57,"style":57},"@RestController\npublic class HomeController {\n\n private final String HOME_VIEW_COUNT = \"home_view_count\";\n\n @GetMapping(\"/\")\n public String home(Principal principal, HttpSession session) {\n incrementCount(session,HOME_VIEW_COUNT);\n return \"Hello, \" + principal.getName();\n }\n\n @GetMapping(\"/count\")\n public String count(HttpSession session) {\n return \"HOME_VIEW_COUNT: \" + session.getAttribute(\"HOME_VIEW_COUNT\");\n }\n\n private void incrementCount(HttpSession session, String attr) {\n var homeViewCount = session.getAttribute(HOME_VIEW_COUNT) == null ? 0 : (Integer) session.getAttribute(HOME_VIEW_COUNT);\n session.setAttribute(attr,homeViewCount + 1);\n }\n}\n",[59,78990,78991,78997,79007,79011,79027,79031,79043,79061,79069,79083,79087,79091,79104,79119,79140,79144,79148,79168,79202,79219,79223],{"__ignoreMap":57},[62,78992,78993,78995],{"class":64,"line":65},[62,78994,942],{"class":72},[62,78996,2342],{"class":68},[62,78998,78999,79001,79003,79005],{"class":64,"line":76},[62,79000,116],{"class":68},[62,79002,119],{"class":68},[62,79004,25636],{"class":122},[62,79006,126],{"class":72},[62,79008,79009],{"class":64,"line":82},[62,79010,79],{"emptyLinePlaceholder":13},[62,79012,79013,79015,79017,79020,79022,79025],{"class":64,"line":89},[62,79014,137],{"class":68},[62,79016,458],{"class":68},[62,79018,79019],{"class":72}," String HOME_VIEW_COUNT ",[62,79021,146],{"class":68},[62,79023,79024],{"class":1675}," \"home_view_count\"",[62,79026,153],{"class":72},[62,79028,79029],{"class":64,"line":95},[62,79030,79],{"emptyLinePlaceholder":13},[62,79032,79033,79035,79037,79039,79041],{"class":64,"line":101},[62,79034,2143],{"class":72},[62,79036,2548],{"class":68},[62,79038,2109],{"class":72},[62,79040,15635],{"class":1675},[62,79042,2212],{"class":72},[62,79044,79045,79047,79049,79051,79053,79055,79057,79059],{"class":64,"line":107},[62,79046,194],{"class":68},[62,79048,2469],{"class":72},[62,79050,18647],{"class":122},[62,79052,78791],{"class":72},[62,79054,78794],{"class":889},[62,79056,78797],{"class":72},[62,79058,78800],{"class":889},[62,79060,768],{"class":72},[62,79062,79063,79066],{"class":64,"line":113},[62,79064,79065],{"class":122}," incrementCount",[62,79067,79068],{"class":72},"(session,HOME_VIEW_COUNT);\n",[62,79070,79071,79073,79075,79077,79079,79081],{"class":64,"line":129},[62,79072,360],{"class":68},[62,79074,78809],{"class":1675},[62,79076,4507],{"class":68},[62,79078,78814],{"class":72},[62,79080,12678],{"class":122},[62,79082,822],{"class":72},[62,79084,79085],{"class":64,"line":134},[62,79086,223],{"class":72},[62,79088,79089],{"class":64,"line":156},[62,79090,79],{"emptyLinePlaceholder":13},[62,79092,79093,79095,79097,79099,79102],{"class":64,"line":161},[62,79094,2143],{"class":72},[62,79096,2548],{"class":68},[62,79098,2109],{"class":72},[62,79100,79101],{"class":1675},"\"/count\"",[62,79103,2212],{"class":72},[62,79105,79106,79108,79110,79112,79115,79117],{"class":64,"line":167},[62,79107,194],{"class":68},[62,79109,2469],{"class":72},[62,79111,45647],{"class":122},[62,79113,79114],{"class":72},"(HttpSession ",[62,79116,78800],{"class":889},[62,79118,768],{"class":72},[62,79120,79121,79123,79126,79128,79131,79133,79135,79138],{"class":64,"line":173},[62,79122,360],{"class":68},[62,79124,79125],{"class":1675}," \"HOME_VIEW_COUNT: \"",[62,79127,4507],{"class":68},[62,79129,79130],{"class":72}," session.",[62,79132,16806],{"class":122},[62,79134,2109],{"class":72},[62,79136,79137],{"class":1675},"\"HOME_VIEW_COUNT\"",[62,79139,1133],{"class":72},[62,79141,79142],{"class":64,"line":179},[62,79143,223],{"class":72},[62,79145,79146],{"class":64,"line":185},[62,79147,79],{"emptyLinePlaceholder":13},[62,79149,79150,79152,79154,79157,79159,79161,79163,79166],{"class":64,"line":191},[62,79151,137],{"class":68},[62,79153,200],{"class":68},[62,79155,79156],{"class":122}," incrementCount",[62,79158,79114],{"class":72},[62,79160,78800],{"class":889},[62,79162,8624],{"class":72},[62,79164,79165],{"class":889},"attr",[62,79167,768],{"class":72},[62,79169,79170,79172,79175,79177,79179,79181,79184,79186,79188,79190,79192,79194,79197,79199],{"class":64,"line":209},[62,79171,13605],{"class":68},[62,79173,79174],{"class":72}," homeViewCount ",[62,79176,146],{"class":68},[62,79178,79130],{"class":72},[62,79180,16806],{"class":122},[62,79182,79183],{"class":72},"(HOME_VIEW_COUNT) ",[62,79185,28328],{"class":68},[62,79187,13324],{"class":149},[62,79189,64807],{"class":68},[62,79191,150],{"class":149},[62,79193,57090],{"class":68},[62,79195,79196],{"class":72}," (Integer) session.",[62,79198,16806],{"class":122},[62,79200,79201],{"class":72},"(HOME_VIEW_COUNT);\n",[62,79203,79204,79207,79210,79213,79215,79217],{"class":64,"line":220},[62,79205,79206],{"class":72}," session.",[62,79208,79209],{"class":122},"setAttribute",[62,79211,79212],{"class":72},"(attr,homeViewCount ",[62,79214,1148],{"class":68},[62,79216,22038],{"class":149},[62,79218,1133],{"class":72},[62,79220,79221],{"class":64,"line":226},[62,79222,223],{"class":72},[62,79224,79225],{"class":64,"line":231},[62,79226,379],{"class":72},[22,79228,79229],{},"Now, run the application, log in, and observe that the view counter correctly increments and that the session persists across application restarts.",[26,79231,32553],{"id":32552},[22,79233,79234],{},"Spring Session provides a powerful way to manage user session information in Spring Boot applications, allowing for more scalable and resilient applications. In our example, we used Redis as the data store, but other options such as JDBC and Hazelcast are also supported.",[22,79236,79237],{},"If you're using Spring Security in your application, it's worth considering integrating Spring Session to improve the overall user experience. However, if you're building a stateless application (such as a REST API), Spring Session might not be necessary.",[22,79239,79240,79241,2755],{},"To learn more about Spring Session, take a look at the ",[677,79242,79245],{"href":79243,"rel":79244},"https://docs.spring.io/spring-session/docs/current/reference/html5/",[681],"official documentation",[22,79247,1525],{},[1527,79249,79250],{},"html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}html pre.shiki code .spoOn, html code.shiki .spoOn{--shiki-default:#E36209;--shiki-dark:#FFAB70;--shiki-sepia:#E36209}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .suaqH, html code.shiki .suaqH{--shiki-default:#22863A;--shiki-dark:#85E89D;--shiki-sepia:#22863A}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}",{"title":57,"searchDepth":76,"depth":76,"links":79252},[79253,79254,79255,79256,79257],{"id":78671,"depth":76,"text":78672},{"id":78699,"depth":76,"text":78700},{"id":78733,"depth":76,"text":78734},{"id":78849,"depth":76,"text":78850},{"id":32552,"depth":76,"text":32553},{"_id":79259,"path":79260,"title":79261,"description":79262,"meta":79263,"body":79268},"content/blog/2023/04/28/spring-security-oauth2-login.md","/blog/2023/04/28/spring-security-oauth2-login","OAuth2 Login Made Easy in Java: A Spring Boot & Spring Security Walkthrough","In this tutorial, we will discuss how to set up OAuth 2 logins in Spring Security with Spring Boot.",{"slug":79264,"date":79265,"published":13,"tags":79266,"author":17,"cover":79267,"excerpt":-1},"spring-security-oauth2-login","2023-04-28T08:00:00.000Z",[10914],"./spring-security-oauth2-login.png",{"type":19,"value":79269,"toc":79812},[79270,79273,79275,79278,79280,79317,79322,79325,79329,79335,79440,79443,79445,79454,79621,79627,79630,79636,79702,79718,79722,79745,79749,79779,79781,79788,79796,79798,79801,79809],[22,79271,79272],{},"In this tutorial, we will discuss how to set up OAuth 2 logins in Spring Security with Spring Boot. OAuth 2 logins are a convenient way to let your users log in via social media (GitHub, Google, Twitter) without needing to register on your system.",[26,79274,2874],{"id":2873},[22,79276,79277],{},"You should be familiar with Java, Spring Boot, and Spring Security. Optionally, you should know how to use IntelliJ IDEA, but you can use any IDE of your choice.",[26,79279,48224],{"id":15195},[34,79281,79282,79288,79291,79296,79302,79305,79314],{},[37,79283,1741,79284,79287],{},[677,79285,2903],{"href":2901,"rel":79286},[681]," to create a new Spring Boot project.",[37,79289,79290],{},"Select Maven as your build tool and Java as your language.",[37,79292,79293,79294,2755],{},"Change the group to something meaningful. For example, ",[59,79295,23065],{},[37,79297,79298,79299,2755],{},"Name your project, like ",[59,79300,79301],{},"SocialLogin",[37,79303,79304],{},"Choose JDK 17 (or the latest available).",[37,79306,79307,79308,19931,79310,79313],{},"Add dependencies: ",[59,79309,27737],{},[59,79311,79312],{},"oauth2-client",". Spring Security will be added as a transitive dependency.",[37,79315,79316],{},"Generate and download the project. This will provide a zip file.",[22,79318,79319],{},[653,79320],{"alt":78726,"src":79321},"/images/blog/2023/04/28/start-spring-io.png",[22,79323,79324],{},"Unzip the project and open it in your favorite IDE or text editor. I'll use IntelliJ IDEA Ultimate.",[26,79326,79328],{"id":79327},"creating-a-controller","Creating a Controller",[22,79330,79331,79332,79334],{},"Start by creating a ",[59,79333,78739],{}," class, where we'll set up both public and private routes.",[52,79336,79338],{"className":54,"code":79337,"language":56,"meta":57,"style":57},"@RestController\npublic class HomeController {\n\n @GetMapping(\"/\")\n public String home() {\n return \"Hello, home!\";\n }\n\n @GetMapping(\"/secured\")\n public String secured() {\n return \"Hello, secured!\";\n }\n}\n",[59,79339,79340,79346,79356,79360,79372,79382,79391,79395,79399,79412,79423,79432,79436],{"__ignoreMap":57},[62,79341,79342,79344],{"class":64,"line":65},[62,79343,942],{"class":72},[62,79345,2342],{"class":68},[62,79347,79348,79350,79352,79354],{"class":64,"line":76},[62,79349,116],{"class":68},[62,79351,119],{"class":68},[62,79353,25636],{"class":122},[62,79355,126],{"class":72},[62,79357,79358],{"class":64,"line":82},[62,79359,79],{"emptyLinePlaceholder":13},[62,79361,79362,79364,79366,79368,79370],{"class":64,"line":89},[62,79363,2143],{"class":72},[62,79365,2548],{"class":68},[62,79367,2109],{"class":72},[62,79369,15635],{"class":1675},[62,79371,2212],{"class":72},[62,79373,79374,79376,79378,79380],{"class":64,"line":95},[62,79375,194],{"class":68},[62,79377,2469],{"class":72},[62,79379,18647],{"class":122},[62,79381,206],{"class":72},[62,79383,79384,79386,79389],{"class":64,"line":101},[62,79385,360],{"class":68},[62,79387,79388],{"class":1675}," \"Hello, home!\"",[62,79390,153],{"class":72},[62,79392,79393],{"class":64,"line":107},[62,79394,223],{"class":72},[62,79396,79397],{"class":64,"line":113},[62,79398,79],{"emptyLinePlaceholder":13},[62,79400,79401,79403,79405,79407,79410],{"class":64,"line":129},[62,79402,2143],{"class":72},[62,79404,2548],{"class":68},[62,79406,2109],{"class":72},[62,79408,79409],{"class":1675},"\"/secured\"",[62,79411,2212],{"class":72},[62,79413,79414,79416,79418,79421],{"class":64,"line":134},[62,79415,194],{"class":68},[62,79417,2469],{"class":72},[62,79419,79420],{"class":122},"secured",[62,79422,206],{"class":72},[62,79424,79425,79427,79430],{"class":64,"line":156},[62,79426,360],{"class":68},[62,79428,79429],{"class":1675}," \"Hello, secured!\"",[62,79431,153],{"class":72},[62,79433,79434],{"class":64,"line":161},[62,79435,223],{"class":72},[62,79437,79438],{"class":64,"line":167},[62,79439,379],{"class":72},[22,79441,79442],{},"With these routes in place, we can now set up our security configuration.",[26,79444,15401],{"id":15400},[22,79446,79447,79448,79450,79451,79453],{},"First, create a new ",[59,79449,15407],{}," class in the ",[59,79452,48865],{}," package:",[52,79455,79457],{"className":54,"code":79456,"language":56,"meta":57,"style":57},"@Configuration\n@EnableWebSecurity\npublic class SecurityConfig {\n\n @Bean\n SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n return http\n .authorizeHttpRequests(auth -> {\n auth.requestMatchers(\"/\").permitAll();\n auth.requestMatchers(\"/favicon.ico\").permitAll();\n auth.anyRequest().authenticated();\n })\n .oauth2Login(withDefaults())\n .formLogin(withDefaults())\n .build();\n }\n\n}\n",[59,79458,79459,79465,79471,79481,79485,79491,79507,79513,79525,79542,79559,79571,79576,79589,79601,79609,79613,79617],{"__ignoreMap":57},[62,79460,79461,79463],{"class":64,"line":65},[62,79462,942],{"class":72},[62,79464,11133],{"class":68},[62,79466,79467,79469],{"class":64,"line":76},[62,79468,942],{"class":72},[62,79470,11461],{"class":68},[62,79472,79473,79475,79477,79479],{"class":64,"line":82},[62,79474,116],{"class":68},[62,79476,119],{"class":68},[62,79478,11470],{"class":122},[62,79480,126],{"class":72},[62,79482,79483],{"class":64,"line":89},[62,79484,79],{"emptyLinePlaceholder":13},[62,79486,79487,79489],{"class":64,"line":95},[62,79488,2143],{"class":72},[62,79490,2146],{"class":68},[62,79492,79493,79495,79497,79499,79501,79503,79505],{"class":64,"line":101},[62,79494,11487],{"class":72},[62,79496,11490],{"class":122},[62,79498,11493],{"class":72},[62,79500,11496],{"class":889},[62,79502,5024],{"class":72},[62,79504,11501],{"class":68},[62,79506,11504],{"class":72},[62,79508,79509,79511],{"class":64,"line":107},[62,79510,360],{"class":68},[62,79512,11511],{"class":72},[62,79514,79515,79517,79519,79521,79523],{"class":64,"line":113},[62,79516,2418],{"class":72},[62,79518,11533],{"class":122},[62,79520,11536],{"class":72},[62,79522,800],{"class":68},[62,79524,126],{"class":72},[62,79526,79527,79530,79532,79534,79536,79538,79540],{"class":64,"line":129},[62,79528,79529],{"class":72}," auth.",[62,79531,15490],{"class":122},[62,79533,2109],{"class":72},[62,79535,15635],{"class":1675},[62,79537,15503],{"class":72},[62,79539,11549],{"class":122},[62,79541,822],{"class":72},[62,79543,79544,79546,79548,79550,79553,79555,79557],{"class":64,"line":134},[62,79545,79529],{"class":72},[62,79547,15490],{"class":122},[62,79549,2109],{"class":72},[62,79551,79552],{"class":1675},"\"/favicon.ico\"",[62,79554,15503],{"class":72},[62,79556,11549],{"class":122},[62,79558,822],{"class":72},[62,79560,79561,79563,79565,79567,79569],{"class":64,"line":156},[62,79562,79529],{"class":72},[62,79564,11544],{"class":122},[62,79566,3229],{"class":72},[62,79568,15518],{"class":122},[62,79570,822],{"class":72},[62,79572,79573],{"class":64,"line":161},[62,79574,79575],{"class":72}," })\n",[62,79577,79578,79580,79582,79584,79587],{"class":64,"line":167},[62,79579,2418],{"class":72},[62,79581,15576],{"class":122},[62,79583,2109],{"class":72},[62,79585,79586],{"class":122},"withDefaults",[62,79588,4460],{"class":72},[62,79590,79591,79593,79595,79597,79599],{"class":64,"line":173},[62,79592,2418],{"class":72},[62,79594,11518],{"class":122},[62,79596,2109],{"class":72},[62,79598,79586],{"class":122},[62,79600,4460],{"class":72},[62,79602,79603,79605,79607],{"class":64,"line":179},[62,79604,2418],{"class":72},[62,79606,2189],{"class":122},[62,79608,822],{"class":72},[62,79610,79611],{"class":64,"line":185},[62,79612,223],{"class":72},[62,79614,79615],{"class":64,"line":191},[62,79616,79],{"emptyLinePlaceholder":13},[62,79618,79619],{"class":64,"line":209},[62,79620,379],{"class":72},[22,79622,79623,79624,79626],{},"Here, we configured Spring Security to allow anyone to access the ",[59,79625,6936],{}," route, but require authentication for any other route. We've enabled both form login and OAuth2 login as authentication methods.",[26,79628,79629],{"id":16868},"Setting up OAuth2 Providers",[22,79631,79632,79633,79635],{},"To set up OAuth2 providers, add the following properties to your ",[59,79634,1265],{}," file.",[52,79637,79639],{"className":1269,"code":79638,"language":1271,"meta":57,"style":57},"# Logging level\nlogging.level.org.springframework.security=TRACE\n\n# GitHub\nspring.security.oauth2.client.registration.github.client-id=\u003Cyour-github-client-id>\nspring.security.oauth2.client.registration.github.client-secret=\u003Cyour-github-client-secret>\n\n# Google\nspring.security.oauth2.client.registration.google.client-id=\u003Cyour-google-client-id>\nspring.security.oauth2.client.registration.google.client-secret=\u003Cyour-google-client-secret>\n\n",[59,79640,79641,79646,79652,79656,79661,79669,79677,79681,79686,79694],{"__ignoreMap":57},[62,79642,79643],{"class":64,"line":65},[62,79644,79645],{"class":85},"# Logging level\n",[62,79647,79648,79650],{"class":64,"line":76},[62,79649,36193],{"class":68},[62,79651,36196],{"class":72},[62,79653,79654],{"class":64,"line":82},[62,79655,79],{"emptyLinePlaceholder":13},[62,79657,79658],{"class":64,"line":89},[62,79659,79660],{"class":85},"# GitHub\n",[62,79662,79663,79666],{"class":64,"line":95},[62,79664,79665],{"class":68},"spring.security.oauth2.client.registration.github.client-id",[62,79667,79668],{"class":72},"=\u003Cyour-github-client-id>\n",[62,79670,79671,79674],{"class":64,"line":101},[62,79672,79673],{"class":68},"spring.security.oauth2.client.registration.github.client-secret",[62,79675,79676],{"class":72},"=\u003Cyour-github-client-secret>\n",[62,79678,79679],{"class":64,"line":107},[62,79680,79],{"emptyLinePlaceholder":13},[62,79682,79683],{"class":64,"line":113},[62,79684,79685],{"class":85},"# Google\n",[62,79687,79688,79691],{"class":64,"line":129},[62,79689,79690],{"class":68},"spring.security.oauth2.client.registration.google.client-id",[62,79692,79693],{"class":72},"=\u003Cyour-google-client-id>\n",[62,79695,79696,79699],{"class":64,"line":134},[62,79697,79698],{"class":68},"spring.security.oauth2.client.registration.google.client-secret",[62,79700,79701],{"class":72},"=\u003Cyour-google-client-secret>\n",[22,79703,79704,79705,976,79708,976,79711,4201,79714,79717],{},"Note: You'll need to replace ",[59,79706,79707],{},"\u003Cyour-github-client-id>",[59,79709,79710],{},"\u003Cyour-github-client-secret>",[59,79712,79713],{},"\u003Cyour-google-client-id>",[59,79715,79716],{},"\u003Cyour-google-client-secret>"," with the corresponding values from your OAuth providers.",[636,79719,79721],{"id":79720},"setting-up-github-as-an-oauth-provider","Setting up GitHub as an OAuth Provider",[34,79723,79724,79727,79730,79735],{},[37,79725,79726],{},"Go to your GitHub account settings, and navigate to Developer settings > OAuth Apps.",[37,79728,79729],{},"Click \"New OAuth App\" and fill in the required fields.",[37,79731,79732,79733,2755],{},"For the \"Authorization callback URL,\" use: ",[59,79734,16989],{},[37,79736,79737,79738,19931,79741,79744],{},"Register the application, and you'll receive a ",[59,79739,79740],{},"client_id",[59,79742,79743],{},"client_secret"," for your properties file.",[636,79746,79748],{"id":79747},"setting-up-google-as-an-oauth-provider","Setting up Google as an OAuth Provider",[34,79750,79751,79758,79761,79764,79767,79772],{},[37,79752,79753,79754,2755],{},"Visit the Google Cloud Console at ",[677,79755,79757],{"href":16881,"rel":79756},[681],"console.cloud.google.com",[37,79759,79760],{},"Set up the OAuth 2 consent screen if you haven't done so already.",[37,79762,79763],{},"Go to the Credentials section and create a new OAuth 2 client ID.",[37,79765,79766],{},"Select \"Web application\" as the type, and enter a name for the application.",[37,79768,79769,79770,2755],{},"Add an authorized redirect URI: ",[59,79771,16949],{},[37,79773,79774,79775,19931,79777,79744],{},"Click \"Create\" to obtain your ",[59,79776,79740],{},[59,79778,79743],{},[26,79780,19432],{"id":19431},[22,79782,79783,79784,79787],{},"Run your Spring Boot application and visit ",[677,79785,17969],{"href":17969,"rel":79786},[681]," in your browser. You should see the public \"Hello, home!\" message.",[22,79789,79790,79791,79795],{},"Now, navigate to ",[677,79792,79793],{"href":79793,"rel":79794},"http://localhost:8080/secured",[681],", and you will be prompted to log in via form login or the OAuth providers you configured (GitHub and Google). Log in using one of those providers, and you will be redirected to the secured \"Hello, secured!\" page.",[26,79797,1499],{"id":1498},[22,79799,79800],{},"In this blog post, we demonstrated how easy it is to set up OAuth 2 logins in Spring Security with Spring Boot, allowing your users to authenticate via social media platforms like GitHub and Google without having to register on your system.",[22,79802,79803,79804,2755],{},"If you're interested in customizing the login page or want further information about configuring OAuth2 properties, you can refer to the ",[677,79805,79808],{"href":79806,"rel":79807},"https://docs.spring.io/spring-security/site/docs/current/reference/html5/#oauth2",[681],"Spring Security documentation",[1527,79810,79811],{},"html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .spoOn, html code.shiki .spoOn{--shiki-default:#E36209;--shiki-dark:#FFAB70;--shiki-sepia:#E36209}html pre.shiki code .s4-Ip, html code.shiki .s4-Ip{--shiki-default:#6A737D;--shiki-dark:#6A737D;--shiki-sepia:#6A737D}",{"title":57,"searchDepth":76,"depth":76,"links":79813},[79814,79815,79816,79817,79818,79822,79823],{"id":2873,"depth":76,"text":2874},{"id":15195,"depth":76,"text":48224},{"id":79327,"depth":76,"text":79328},{"id":15400,"depth":76,"text":15401},{"id":16868,"depth":76,"text":79629,"children":79819},[79820,79821],{"id":79720,"depth":82,"text":79721},{"id":79747,"depth":82,"text":79748},{"id":19431,"depth":76,"text":19432},{"id":1498,"depth":76,"text":1499},{"_id":79825,"path":79826,"title":79827,"description":79828,"meta":79829,"body":79834},"content/blog/2023/04/26/spring-boot-docker-compose.md","/blog/2023/04/26/spring-boot-docker-compose","🔥 New in Spring Boot 3.1 - Spring Boot Docker Compose Module","In this tutorial you will learn about the new Spring Boot Docker Compose Module. This module allows you to use Docker Compose files directly with Spring Boot applications.",{"slug":79830,"date":79831,"published":13,"tags":79832,"author":17,"cover":79833,"excerpt":-1},"spring-boot-docker-compose","2023-04-26T08:00:00.000Z",[2925],"./spring-boot-docker-compose.png",{"type":19,"value":79835,"toc":80336},[79836,79839,79844,79847,79851,79858,79872,79877,79880,79884,79890,79897,79974,79985,80020,80027,80155,80159,80162,80167,80258,80261,80270,80297,80303,80316,80322,80324,80327,80330,80333],[22,79837,79838],{},"In this article we're talking all about Spring Boot 3.1 RC1, which has just been released with many great features. In particular, we're going to focus on the ability to use Docker Compose files directly with Spring Boot applications.",[22,79840,79841,79842,2755],{},"This isn't a new feature per se, but it comes with some significant improvements over previous versions. One of the major enhancements is that the Spring Boot Docker Compose Module will start and stop your containers automatically. Additionally, you no longer need to duplicate properties between your Docker Compose file and ",[59,79843,1265],{},[22,79845,79846],{},"In this blog post, I'll show you how to get started with this exciting new feature in Spring Boot 3.1 RC1. Let's begin with a demo!",[26,79848,79850],{"id":79849},"setting-up-a-spring-boot-31-project","Setting up a Spring Boot 3.1 Project",[22,79852,79853,79854,79857],{},"First, let's set up a basic Spring Boot 3.1 project using ",[677,79855,24606],{"href":2901,"rel":79856},[681],". Here's a quick overview of the options we're going to select:",[915,79859,79860,79863,79866,79869],{},[37,79861,79862],{},"Maven Project",[37,79864,79865],{},"3.1.0 RC1 version",[37,79867,79868],{},"Java 17",[37,79870,79871],{},"Dependencies: Web, Spring Data JPA, PostgreSQL Driver",[22,79873,79874],{},[653,79875],{"alt":24606,"src":79876},"/images/blog/2023/04/26/start-spring-io.png",[22,79878,79879],{},"With these options selected, click \"Generate\" and extract the downloaded zip file. Open the extracted project in your favorite IDE or editor (I'm using IntelliJ IDEA Ultimate Edition).",[26,79881,79883],{"id":79882},"creating-a-simple-blog-application","Creating a Simple Blog Application",[22,79885,79886,79887,79889],{},"For this demo, let's create a simple blog application with a single ",[59,79888,38700],{}," entity and a RESTful API to fetch all posts.",[22,79891,79892,79893,48324,79895,79453],{},"Start by creating a Java class called ",[59,79894,38700],{},[59,79896,16671],{},[52,79898,79900],{"className":54,"code":79899,"language":56,"meta":57,"style":57},"@Entity\npublic class Post {\n\n @Id\n @GeneratedValue\n private Integer id;\n\n private String title;\n\n private String body;\n\n // Constructors, getters, setters, and toString() methods go here\n}\n",[59,79901,79902,79908,79918,79922,79928,79934,79940,79944,79950,79954,79961,79965,79970],{"__ignoreMap":57},[62,79903,79904,79906],{"class":64,"line":65},[62,79905,942],{"class":72},[62,79907,8999],{"class":68},[62,79909,79910,79912,79914,79916],{"class":64,"line":76},[62,79911,116],{"class":68},[62,79913,119],{"class":68},[62,79915,41321],{"class":122},[62,79917,126],{"class":72},[62,79919,79920],{"class":64,"line":82},[62,79921,79],{"emptyLinePlaceholder":13},[62,79923,79924,79926],{"class":64,"line":89},[62,79925,2143],{"class":72},[62,79927,22298],{"class":68},[62,79929,79930,79932],{"class":64,"line":95},[62,79931,2143],{"class":72},[62,79933,9022],{"class":68},[62,79935,79936,79938],{"class":64,"line":101},[62,79937,137],{"class":68},[62,79939,45372],{"class":72},[62,79941,79942],{"class":64,"line":107},[62,79943,79],{"emptyLinePlaceholder":13},[62,79945,79946,79948],{"class":64,"line":113},[62,79947,137],{"class":68},[62,79949,9036],{"class":72},[62,79951,79952],{"class":64,"line":129},[62,79953,79],{"emptyLinePlaceholder":13},[62,79955,79956,79958],{"class":64,"line":134},[62,79957,137],{"class":68},[62,79959,79960],{"class":72}," String body;\n",[62,79962,79963],{"class":64,"line":156},[62,79964,79],{"emptyLinePlaceholder":13},[62,79966,79967],{"class":64,"line":161},[62,79968,79969],{"class":85}," // Constructors, getters, setters, and toString() methods go here\n",[62,79971,79972],{"class":64,"line":167},[62,79973,379],{"class":72},[22,79975,79976,79977,48324,79979,79981,79982,1266],{},"Next, create a new interface called ",[59,79978,55750],{},[59,79980,23540],{}," package that extends ",[59,79983,79984],{},"CrudRepository\u003CPost, Integer>",[52,79986,79988],{"className":54,"code":79987,"language":56,"meta":57,"style":57},"public interface PostRepository extends CrudRepository\u003CPost, Integer> {\n\n}\n",[59,79989,79990,80012,80016],{"__ignoreMap":57},[62,79991,79992,79994,79996,79998,80000,80002,80004,80006,80008,80010],{"class":64,"line":65},[62,79993,116],{"class":68},[62,79995,8531],{"class":68},[62,79997,38638],{"class":122},[62,79999,8537],{"class":68},[62,80001,22395],{"class":122},[62,80003,760],{"class":72},[62,80005,38700],{"class":68},[62,80007,976],{"class":72},[62,80009,979],{"class":68},[62,80011,8552],{"class":72},[62,80013,80014],{"class":64,"line":76},[62,80015,79],{"emptyLinePlaceholder":13},[62,80017,80018],{"class":64,"line":82},[62,80019,379],{"class":72},[22,80021,19716,80022,79450,80024,80026],{},[59,80023,41342],{},[59,80025,48327],{}," package with a single GET endpoint to fetch all posts:",[52,80028,80030],{"className":54,"code":80029,"language":56,"meta":57,"style":57},"@RestController\n@RequestMapping(\"/api/posts\")\npublic class PostController {\n\n private final PostRepository repository;\n\n public PostController(PostRepository repository) {\n this.repository = repository;\n }\n\n @GetMapping\n public List\u003CPost> findAll() {\n return (List\u003CPost>) repository.findAll();\n }\n}\n",[59,80031,80032,80038,80050,80060,80064,80073,80077,80089,80099,80103,80107,80113,80127,80147,80151],{"__ignoreMap":57},[62,80033,80034,80036],{"class":64,"line":65},[62,80035,942],{"class":72},[62,80037,2342],{"class":68},[62,80039,80040,80042,80044,80046,80048],{"class":64,"line":76},[62,80041,942],{"class":72},[62,80043,10592],{"class":68},[62,80045,2109],{"class":72},[62,80047,41368],{"class":1675},[62,80049,2212],{"class":72},[62,80051,80052,80054,80056,80058],{"class":64,"line":82},[62,80053,116],{"class":68},[62,80055,119],{"class":68},[62,80057,41379],{"class":122},[62,80059,126],{"class":72},[62,80061,80062],{"class":64,"line":89},[62,80063,79],{"emptyLinePlaceholder":13},[62,80065,80066,80068,80070],{"class":64,"line":95},[62,80067,137],{"class":68},[62,80069,458],{"class":68},[62,80071,80072],{"class":72}," PostRepository repository;\n",[62,80074,80075],{"class":64,"line":101},[62,80076,79],{"emptyLinePlaceholder":13},[62,80078,80079,80081,80083,80085,80087],{"class":64,"line":107},[62,80080,194],{"class":68},[62,80082,41379],{"class":122},[62,80084,55804],{"class":72},[62,80086,23540],{"class":889},[62,80088,768],{"class":72},[62,80090,80091,80093,80095,80097],{"class":64,"line":113},[62,80092,2405],{"class":149},[62,80094,23549],{"class":72},[62,80096,146],{"class":68},[62,80098,23554],{"class":72},[62,80100,80101],{"class":64,"line":129},[62,80102,223],{"class":72},[62,80104,80105],{"class":64,"line":134},[62,80106,79],{"emptyLinePlaceholder":13},[62,80108,80109,80111],{"class":64,"line":156},[62,80110,2143],{"class":72},[62,80112,47319],{"class":68},[62,80114,80115,80117,80119,80121,80123,80125],{"class":64,"line":161},[62,80116,194],{"class":68},[62,80118,3079],{"class":72},[62,80120,38700],{"class":68},[62,80122,3135],{"class":72},[62,80124,10287],{"class":122},[62,80126,206],{"class":72},[62,80128,80129,80131,80134,80136,80138,80140,80143,80145],{"class":64,"line":167},[62,80130,360],{"class":68},[62,80132,80133],{"class":72}," (List",[62,80135,760],{"class":68},[62,80137,38700],{"class":72},[62,80139,2583],{"class":68},[62,80141,80142],{"class":72},") repository.",[62,80144,10287],{"class":122},[62,80146,822],{"class":72},[62,80148,80149],{"class":64,"line":173},[62,80150,223],{"class":72},[62,80152,80153],{"class":64,"line":179},[62,80154,379],{"class":72},[26,80156,80158],{"id":80157},"adding-docker-compose","Adding Docker Compose",[22,80160,80161],{},"At this point, we've got a basic application with a single REST endpoint to fetch all posts. Now, let's add Docker Compose for spinning up a PostgreSQL container as our database.",[22,80163,46273,80164,80166],{},[59,80165,45926],{}," in the project's root directory:",[52,80168,80170],{"className":10993,"code":80169,"language":10995,"meta":57,"style":57},"version: \"3.1\"\n\nservices:\n db:\n image: postgres\n restart: always\n ports:\n - \"5432:5432\"\n environment:\n POSTGRES_USER: postgres\n POSTGRES_PASSWORD: password\n POSTGRES_DB: blog\n",[59,80171,80172,80180,80184,80190,80196,80205,80215,80221,80227,80233,80241,80249],{"__ignoreMap":57},[62,80173,80174,80176,80178],{"class":64,"line":65},[62,80175,1933],{"class":1780},[62,80177,3696],{"class":72},[62,80179,78918],{"class":1675},[62,80181,80182],{"class":64,"line":76},[62,80183,79],{"emptyLinePlaceholder":13},[62,80185,80186,80188],{"class":64,"line":82},[62,80187,17489],{"class":1780},[62,80189,11005],{"class":72},[62,80191,80192,80194],{"class":64,"line":89},[62,80193,17496],{"class":1780},[62,80195,11005],{"class":72},[62,80197,80198,80200,80202],{"class":64,"line":95},[62,80199,17503],{"class":1780},[62,80201,3696],{"class":72},[62,80203,80204],{"class":1675},"postgres\n",[62,80206,80207,80210,80212],{"class":64,"line":101},[62,80208,80209],{"class":1780}," restart",[62,80211,3696],{"class":72},[62,80213,80214],{"class":1675},"always\n",[62,80216,80217,80219],{"class":64,"line":107},[62,80218,17550],{"class":1780},[62,80220,11005],{"class":72},[62,80222,80223,80225],{"class":64,"line":113},[62,80224,17557],{"class":72},[62,80226,17560],{"class":1675},[62,80228,80229,80231],{"class":64,"line":129},[62,80230,17513],{"class":1780},[62,80232,11005],{"class":72},[62,80234,80235,80237,80239],{"class":64,"line":134},[62,80236,17530],{"class":1780},[62,80238,3696],{"class":72},[62,80240,80204],{"class":1675},[62,80242,80243,80245,80247],{"class":64,"line":156},[62,80244,17540],{"class":1780},[62,80246,3696],{"class":72},[62,80248,17545],{"class":1675},[62,80250,80251,80253,80255],{"class":64,"line":161},[62,80252,17520],{"class":1780},[62,80254,3696],{"class":72},[62,80256,80257],{"class":1675},"blog\n",[22,80259,80260],{},"Now that we have a Docker Compose file in place, we need to make a couple of changes to enable auto-start/stop of containers and avoid duplicating properties.",[22,80262,80263,80264,80267,80268,1266],{},"First, add the ",[59,80265,80266],{},"spring-boot-starter-docker-compose"," dependency in your ",[59,80269,1765],{},[52,80271,80273],{"className":1769,"code":80272,"language":1771,"meta":57,"style":57},"\u003Cdependency>\n \u003CgroupId>org.springframework.boot\u003C/groupId>\n \u003CartifactId>spring-boot-starter-docker-compose\u003C/artifactId>\n \u003Cversion>3.1.0-RC1\u003C/version>\n\u003C/dependency>\n\n",[59,80274,80275,80279,80283,80288,80293],{"__ignoreMap":57},[62,80276,80277],{"class":64,"line":65},[62,80278,46425],{},[62,80280,80281],{"class":64,"line":76},[62,80282,54736],{},[62,80284,80285],{"class":64,"line":82},[62,80286,80287],{}," \u003CartifactId>spring-boot-starter-docker-compose\u003C/artifactId>\n",[62,80289,80290],{"class":64,"line":89},[62,80291,80292],{}," \u003Cversion>3.1.0-RC1\u003C/version>\n",[62,80294,80295],{"class":64,"line":95},[62,80296,46445],{},[22,80298,80299,80300,80302],{},"Then, in your ",[59,80301,1265],{}," file, add the following line to enable auto-generation of DDL statements for JPA:",[52,80304,80306],{"className":1269,"code":80305,"language":1271,"meta":57,"style":57},"spring.jpa.hibernate.ddl-auto=create\n",[59,80307,80308],{"__ignoreMap":57},[62,80309,80310,80313],{"class":64,"line":65},[62,80311,80312],{"class":68},"spring.jpa.hibernate.ddl-auto",[62,80314,80315],{"class":72},"=create\n",[22,80317,80318,80319,80321],{},"Now, you're ready to run your application! When you start the Spring Boot application, it will automatically create the PostgreSQL container and set up the connection using the properties defined in the ",[59,80320,45926],{}," file. Additionally, no duplicated properties are required, making this a much cleaner solution.",[26,80323,1499],{"id":1498},[22,80325,80326],{},"In this blog post, we've taken a look at one of the exciting new features in Spring Boot 3.1 RC1 – simplified Docker Compose usage. This new feature allows developers to auto-start/stop containers and avoid duplicating properties, which makes for a better overall development experience.",[22,80328,80329],{},"If you found value in this tutorial, please consider subscribing to my newsletter for more content like this. As always…",[22,80331,80332],{},"Happy Coding!",[1527,80334,80335],{},"html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .s4-Ip, html code.shiki .s4-Ip{--shiki-default:#6A737D;--shiki-dark:#6A737D;--shiki-sepia:#6A737D}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}html pre.shiki code .spoOn, html code.shiki .spoOn{--shiki-default:#E36209;--shiki-dark:#FFAB70;--shiki-sepia:#E36209}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}html pre.shiki code .suaqH, html code.shiki .suaqH{--shiki-default:#22863A;--shiki-dark:#85E89D;--shiki-sepia:#22863A}",{"title":57,"searchDepth":76,"depth":76,"links":80337},[80338,80339,80340,80341],{"id":79849,"depth":76,"text":79850},{"id":79882,"depth":76,"text":79883},{"id":80157,"depth":76,"text":80158},{"id":1498,"depth":76,"text":1499},{"_id":80343,"path":80344,"title":80345,"description":80346,"meta":80347,"body":80352},"content/blog/2023/04/20/multiple-spring-security-configs.md","/blog/2023/04/20/multiple-spring-security-configs","Advanced Spring Security - How to create multiple Spring Security Configurations","In this tutorial, we will discuss how to create multiple Spring Security configurations and why you might want to do so.",{"slug":80348,"date":80349,"published":13,"tags":80350,"author":17,"cover":80351,"excerpt":-1},"multiple-spring-security-configs","2023-04-20T08:00:00.000Z",[10914],"./spring-security-multiple-configurations.png",{"type":19,"value":80353,"toc":81538},[80354,80357,80359,80365,80377,80380,80393,80399,80403,80422,80450,80458,80493,80496,80503,80608,80624,80745,80760,80847,80869,80875,80879,80882,80904,80907,80909,80912,80923,80942,80977,80981,80996,81006,81133,81137,81150,81162,81321,81325,81334,81341,81466,81470,81477,81497,81500,81520,81523,81525,81533,81535],[22,80355,80356],{},"Security is an important aspect of any application, and Spring Security makes it secure by default. In this tutorial, we will discuss how to create multiple Spring Security configurations and why you might want to do so. We'll cover everything from setting up a new project, to customizing security configurations, and exploring multiple security filter chains. So let's dive in!",[26,80358,34881],{"id":34880},[22,80360,31558,80361,80364],{},[677,80362,2903],{"href":1744,"rel":80363},[681]," and create a new project with the following settings:",[915,80366,80367,80370,80372,80374],{},[37,80368,80369],{},"Build Tool: Maven",[37,80371,18824],{},[37,80373,23077],{},[37,80375,80376],{},"Java Version: 17",[22,80378,80379],{},"Next, add the following dependencies:",[915,80381,80382,80384,80387,80390],{},[37,80383,78714],{},[37,80385,80386],{},"Spring Security (leave it off for now, we'll add it later)",[37,80388,80389],{},"Spring Data JDBC",[37,80391,80392],{},"H2 Database",[22,80394,80395,80398],{},[653,80396],{"alt":24606,"src":80397},"/images/blog/2023/04/20/start-spring-io.png","Generate the project and open it up in your favorite IDE.",[26,80400,80402],{"id":80401},"setting-up-the-application","Setting up the Application",[22,80404,80405,80406,80408,80409,976,80411,4201,80413,80415,80416,80418,80419,1266],{},"First, create a simple domain model called ",[59,80407,38700],{}," with some fields like ",[59,80410,6283],{},[59,80412,3196],{},[59,80414,2230],{},". Annotate the ",[59,80417,6283],{}," field with ",[59,80420,80421],{},"@Id",[52,80423,80425],{"className":54,"code":80424,"language":56,"meta":57,"style":57},"public record Post(@Id Integer id, String title, String content) {\n\n}\n",[59,80426,80427,80442,80446],{"__ignoreMap":57},[62,80428,80429,80431,80433,80435,80437,80439],{"class":64,"line":65},[62,80430,116],{"class":68},[62,80432,2996],{"class":68},[62,80434,41321],{"class":122},[62,80436,2475],{"class":72},[62,80438,9016],{"class":68},[62,80440,80441],{"class":72}," Integer id, String title, String content) {\n",[62,80443,80444],{"class":64,"line":76},[62,80445,79],{"emptyLinePlaceholder":13},[62,80447,80448],{"class":64,"line":82},[62,80449,379],{"class":72},[22,80451,80452,80453,80455,80456,2755],{},"Next, create a ",[59,80454,55750],{}," interface that extends ",[59,80457,61831],{},[52,80459,80461],{"className":54,"code":80460,"language":56,"meta":57,"style":57},"public interface PostRepository extends ListCrudRepository\u003CPost,Integer> {\n\n}\n",[59,80462,80463,80485,80489],{"__ignoreMap":57},[62,80464,80465,80467,80469,80471,80473,80475,80477,80479,80481,80483],{"class":64,"line":65},[62,80466,116],{"class":68},[62,80468,8531],{"class":68},[62,80470,38638],{"class":122},[62,80472,8537],{"class":68},[62,80474,45494],{"class":122},[62,80476,760],{"class":72},[62,80478,38700],{"class":68},[62,80480,32225],{"class":72},[62,80482,979],{"class":68},[62,80484,8552],{"class":72},[62,80486,80487],{"class":64,"line":76},[62,80488,79],{"emptyLinePlaceholder":13},[62,80490,80491],{"class":64,"line":82},[62,80492,379],{"class":72},[22,80494,80495],{},"Then, create two controllers:",[34,80497,80498],{},[37,80499,80500,80502],{},[59,80501,78739],{}," with two mappings for the root and private URLs, returning strings \"hello home\" and \"secured\" respectively.",[52,80504,80506],{"className":54,"code":80505,"language":56,"meta":57,"style":57},"@RestController\npublic class HomeController {\n\n @GetMapping(\"/\")\n public String home() {\n return \"Hello, World!\";\n }\n\n @GetMapping(\"/private\")\n public String secure() {\n return \"secured\";\n }\n}\n",[59,80507,80508,80514,80524,80528,80540,80550,80559,80563,80567,80580,80591,80600,80604],{"__ignoreMap":57},[62,80509,80510,80512],{"class":64,"line":65},[62,80511,942],{"class":72},[62,80513,2342],{"class":68},[62,80515,80516,80518,80520,80522],{"class":64,"line":76},[62,80517,116],{"class":68},[62,80519,119],{"class":68},[62,80521,25636],{"class":122},[62,80523,126],{"class":72},[62,80525,80526],{"class":64,"line":82},[62,80527,79],{"emptyLinePlaceholder":13},[62,80529,80530,80532,80534,80536,80538],{"class":64,"line":89},[62,80531,2143],{"class":72},[62,80533,2548],{"class":68},[62,80535,2109],{"class":72},[62,80537,15635],{"class":1675},[62,80539,2212],{"class":72},[62,80541,80542,80544,80546,80548],{"class":64,"line":95},[62,80543,194],{"class":68},[62,80545,2469],{"class":72},[62,80547,18647],{"class":122},[62,80549,206],{"class":72},[62,80551,80552,80554,80557],{"class":64,"line":101},[62,80553,360],{"class":68},[62,80555,80556],{"class":1675}," \"Hello, World!\"",[62,80558,153],{"class":72},[62,80560,80561],{"class":64,"line":107},[62,80562,223],{"class":72},[62,80564,80565],{"class":64,"line":113},[62,80566,79],{"emptyLinePlaceholder":13},[62,80568,80569,80571,80573,80575,80578],{"class":64,"line":129},[62,80570,2143],{"class":72},[62,80572,2548],{"class":68},[62,80574,2109],{"class":72},[62,80576,80577],{"class":1675},"\"/private\"",[62,80579,2212],{"class":72},[62,80581,80582,80584,80586,80589],{"class":64,"line":134},[62,80583,194],{"class":68},[62,80585,2469],{"class":72},[62,80587,80588],{"class":122},"secure",[62,80590,206],{"class":72},[62,80592,80593,80595,80598],{"class":64,"line":156},[62,80594,360],{"class":68},[62,80596,80597],{"class":1675}," \"secured\"",[62,80599,153],{"class":72},[62,80601,80602],{"class":64,"line":161},[62,80603,223],{"class":72},[62,80605,80606],{"class":64,"line":167},[62,80607,379],{"class":72},[34,80609,80610],{"start":76},[37,80611,80612,80614,80615,80618,80619,80621,80622,2755],{},[59,80613,41342],{}," with a request mapping for ",[59,80616,80617],{},"/api/posts"," and a method ",[59,80620,10287],{}," that returns all the posts using the ",[59,80623,55750],{},[52,80625,80627],{"className":54,"code":80626,"language":56,"meta":57,"style":57},"@RestController\n@RequestMapping(\"/api/posts\")\npublic class PostController {\n\n private final PostRepository repository;\n\n public PostController(PostRepository repository) {\n this.repository = repository;\n }\n\n @GetMapping\n public List\u003CPost> findAll() {\n return repository.findAll();\n }\n\n}\n",[59,80628,80629,80635,80647,80657,80661,80669,80673,80685,80695,80699,80703,80709,80723,80733,80737,80741],{"__ignoreMap":57},[62,80630,80631,80633],{"class":64,"line":65},[62,80632,942],{"class":72},[62,80634,2342],{"class":68},[62,80636,80637,80639,80641,80643,80645],{"class":64,"line":76},[62,80638,942],{"class":72},[62,80640,10592],{"class":68},[62,80642,2109],{"class":72},[62,80644,41368],{"class":1675},[62,80646,2212],{"class":72},[62,80648,80649,80651,80653,80655],{"class":64,"line":82},[62,80650,116],{"class":68},[62,80652,119],{"class":68},[62,80654,41379],{"class":122},[62,80656,126],{"class":72},[62,80658,80659],{"class":64,"line":89},[62,80660,79],{"emptyLinePlaceholder":13},[62,80662,80663,80665,80667],{"class":64,"line":95},[62,80664,137],{"class":68},[62,80666,458],{"class":68},[62,80668,80072],{"class":72},[62,80670,80671],{"class":64,"line":101},[62,80672,79],{"emptyLinePlaceholder":13},[62,80674,80675,80677,80679,80681,80683],{"class":64,"line":107},[62,80676,194],{"class":68},[62,80678,41379],{"class":122},[62,80680,55804],{"class":72},[62,80682,23540],{"class":889},[62,80684,768],{"class":72},[62,80686,80687,80689,80691,80693],{"class":64,"line":113},[62,80688,2405],{"class":149},[62,80690,23549],{"class":72},[62,80692,146],{"class":68},[62,80694,23554],{"class":72},[62,80696,80697],{"class":64,"line":129},[62,80698,223],{"class":72},[62,80700,80701],{"class":64,"line":134},[62,80702,79],{"emptyLinePlaceholder":13},[62,80704,80705,80707],{"class":64,"line":156},[62,80706,2143],{"class":72},[62,80708,47319],{"class":68},[62,80710,80711,80713,80715,80717,80719,80721],{"class":64,"line":161},[62,80712,194],{"class":68},[62,80714,3079],{"class":72},[62,80716,38700],{"class":68},[62,80718,3135],{"class":72},[62,80720,10287],{"class":122},[62,80722,206],{"class":72},[62,80724,80725,80727,80729,80731],{"class":64,"line":167},[62,80726,360],{"class":68},[62,80728,4069],{"class":72},[62,80730,10287],{"class":122},[62,80732,822],{"class":72},[62,80734,80735],{"class":64,"line":173},[62,80736,223],{"class":72},[62,80738,80739],{"class":64,"line":179},[62,80740,79],{"emptyLinePlaceholder":13},[62,80742,80743],{"class":64,"line":185},[62,80744,379],{"class":72},[22,80746,80747,80748,32734,80750,80752,80753,80756,80757,80759],{},"After this, create a ",[59,80749,41817],{},[59,80751,4097],{}," folder with SQL to create a ",[59,80754,80755],{},"posts"," table and insert a single row. Also, update your ",[59,80758,1265],{}," file to set the data source name to \"blog\" and enable the H2 console.",[52,80761,80763],{"className":41824,"code":80762,"language":38716,"meta":57,"style":57},"create table Post (\n id int auto_increment primary key ,\n title varchar(255) not null,\n content text not null\n);\nINSERT INTO POST(title,content) VALUES ('Hello, World!','My First Blog Post');\n",[59,80764,80765,80776,80792,80810,80820,80824],{"__ignoreMap":57},[62,80766,80767,80769,80772,80774],{"class":64,"line":65},[62,80768,14348],{"class":68},[62,80770,80771],{"class":68}," table",[62,80773,41321],{"class":122},[62,80775,36931],{"class":72},[62,80777,80778,80781,80783,80786,80789],{"class":64,"line":76},[62,80779,80780],{"class":72}," id ",[62,80782,747],{"class":68},[62,80784,80785],{"class":72}," auto_increment ",[62,80787,80788],{"class":68},"primary key",[62,80790,80791],{"class":72}," ,\n",[62,80793,80794,80797,80799,80801,80803,80805,80808],{"class":64,"line":82},[62,80795,80796],{"class":72}," title ",[62,80798,41867],{"class":68},[62,80800,2109],{"class":72},[62,80802,41872],{"class":149},[62,80804,5024],{"class":72},[62,80806,80807],{"class":68},"not null",[62,80809,3338],{"class":72},[62,80811,80812,80815,80817],{"class":64,"line":89},[62,80813,80814],{"class":72}," content ",[62,80816,1727],{"class":68},[62,80818,80819],{"class":68}," not null\n",[62,80821,80822],{"class":64,"line":95},[62,80823,1133],{"class":72},[62,80825,80826,80829,80832,80835,80837,80840,80842,80845],{"class":64,"line":101},[62,80827,80828],{"class":68},"INSERT INTO",[62,80830,80831],{"class":72}," POST(title,content) ",[62,80833,80834],{"class":68},"VALUES",[62,80836,744],{"class":72},[62,80838,80839],{"class":1675},"'Hello, World!'",[62,80841,32225],{"class":72},[62,80843,80844],{"class":1675},"'My First Blog Post'",[62,80846,1133],{"class":72},[52,80848,80849],{"className":1269,"code":41971,"language":1271,"meta":57,"style":57},[59,80850,80851,80857,80863],{"__ignoreMap":57},[62,80852,80853,80855],{"class":64,"line":65},[62,80854,41978],{"class":68},[62,80856,14152],{"class":72},[62,80858,80859,80861],{"class":64,"line":76},[62,80860,41985],{"class":68},[62,80862,41988],{"class":72},[62,80864,80865,80867],{"class":64,"line":82},[62,80866,41993],{"class":68},[62,80868,1281],{"class":72},[22,80870,80871,80872,2755],{},"Now, run the application and you should be able to access the H2 console at ",[59,80873,80874],{},"localhost:8080/h2-console",[26,80876,80878],{"id":80877},"adding-spring-security","Adding Spring Security",[22,80880,80881],{},"Add the Spring Security dependency manually to your project configuration:",[52,80883,80885],{"className":1769,"code":80884,"language":1771,"meta":57,"style":57},"\u003Cdependency>\n \u003CgroupId>org.springframework.boot\u003C/groupId>\n \u003CartifactId>spring-boot-starter-security\u003C/artifactId>\n\u003C/dependency>\n\n",[59,80886,80887,80891,80895,80900],{"__ignoreMap":57},[62,80888,80889],{"class":64,"line":65},[62,80890,46425],{},[62,80892,80893],{"class":64,"line":76},[62,80894,54736],{},[62,80896,80897],{"class":64,"line":82},[62,80898,80899],{}," \u003CartifactId>spring-boot-starter-security\u003C/artifactId>\n",[62,80901,80902],{"class":64,"line":89},[62,80903,46445],{},[22,80905,80906],{},"Refresh your Maven dependencies, then restart the application. You'll notice you now need to authenticate to access the H2 console (with a randomly generated password and the username \"user\").",[26,80908,15401],{"id":15400},[22,80910,80911],{},"In this tutorial, we'll create multiple Spring Security configurations for different authorization scenarios:",[34,80913,80914,80917,80920],{},[37,80915,80916],{},"Configuration for the H2 console",[37,80918,80919],{},"Configuration for securing the API with HTTP Basic authentication",[37,80921,80922],{},"Configuration for securing the private URL with form login",[22,80924,80925,80926,80928,80929,80931,80932,19931,80935,80937,80938,80941],{},"Let's start by creating a ",[59,80927,15407],{}," class within a ",[59,80930,48865],{}," package, and annotating it with ",[59,80933,80934],{},"@EnableWebSecurity",[59,80936,48869],{},". We will define beans of type ",[59,80939,80940],{},"SecurityFilterChain"," for each scenario.",[52,80943,80945],{"className":54,"code":80944,"language":56,"meta":57,"style":57},"@Configuration\n@EnableWebSecurity\npublic class SecurityConfig {\n\n}\n",[59,80946,80947,80953,80959,80969,80973],{"__ignoreMap":57},[62,80948,80949,80951],{"class":64,"line":65},[62,80950,942],{"class":72},[62,80952,11133],{"class":68},[62,80954,80955,80957],{"class":64,"line":76},[62,80956,942],{"class":72},[62,80958,11461],{"class":68},[62,80960,80961,80963,80965,80967],{"class":64,"line":82},[62,80962,116],{"class":68},[62,80964,119],{"class":68},[62,80966,11470],{"class":122},[62,80968,126],{"class":72},[62,80970,80971],{"class":64,"line":89},[62,80972,79],{"emptyLinePlaceholder":13},[62,80974,80975],{"class":64,"line":95},[62,80976,379],{"class":72},[636,80978,80980],{"id":80979},"api-security-filter-chain","API Security Filter Chain",[22,80982,80983,80984,80987,80988,80991,80992,80995],{},"Create an ",[59,80985,80986],{},"apiSecurityFilterChain"," bean that also takes in an ",[59,80989,80990],{},"HttpSecurity"," object. This time, the configuration should ensure that any request for ",[59,80993,80994],{},"/api/**"," requires authentication, with HTTP basic authentication and session management disabled.",[22,80997,80998,80999,81002,81003,2755],{},"Remember to add another ",[59,81000,81001],{},"@Order"," annotation here, for example ",[59,81004,81005],{},"@Order(1)",[52,81007,81009],{"className":54,"code":81008,"language":56,"meta":57,"style":57},"@Bean\n@Order(1)\nSecurityFilterChain apiSecurityFilterChain(HttpSecurity http) throws Exception {\n return http\n .securityMatcher(\"/api/**\")\n .authorizeHttpRequests(auth -> {\n auth.anyRequest().authenticated();\n })\n .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))\n .httpBasic(withDefaults())\n .build();\n}\n",[59,81010,81011,81017,81029,81039,81045,81059,81071,81084,81088,81108,81121,81129],{"__ignoreMap":57},[62,81012,81013,81015],{"class":64,"line":65},[62,81014,942],{"class":72},[62,81016,2146],{"class":68},[62,81018,81019,81021,81023,81025,81027],{"class":64,"line":76},[62,81020,942],{"class":72},[62,81022,9415],{"class":68},[62,81024,2109],{"class":72},[62,81026,6689],{"class":149},[62,81028,2212],{"class":72},[62,81030,81031,81034,81036],{"class":64,"line":82},[62,81032,81033],{"class":72},"SecurityFilterChain ",[62,81035,80986],{"class":122},[62,81037,81038],{"class":72},"(HttpSecurity http) throws Exception {\n",[62,81040,81041,81043],{"class":64,"line":89},[62,81042,2599],{"class":68},[62,81044,11511],{"class":72},[62,81046,81047,81049,81052,81054,81057],{"class":64,"line":95},[62,81048,2610],{"class":72},[62,81050,81051],{"class":122},"securityMatcher",[62,81053,2109],{"class":72},[62,81055,81056],{"class":1675},"\"/api/**\"",[62,81058,2212],{"class":72},[62,81060,81061,81063,81065,81067,81069],{"class":64,"line":101},[62,81062,2610],{"class":72},[62,81064,11533],{"class":122},[62,81066,11536],{"class":72},[62,81068,800],{"class":68},[62,81070,126],{"class":72},[62,81072,81073,81076,81078,81080,81082],{"class":64,"line":107},[62,81074,81075],{"class":72}," auth.",[62,81077,11544],{"class":122},[62,81079,3229],{"class":72},[62,81081,15518],{"class":122},[62,81083,822],{"class":72},[62,81085,81086],{"class":64,"line":113},[62,81087,13231],{"class":72},[62,81089,81090,81092,81095,81098,81100,81102,81105],{"class":64,"line":129},[62,81091,2610],{"class":72},[62,81093,81094],{"class":122},"sessionManagement",[62,81096,81097],{"class":72},"(session ",[62,81099,800],{"class":68},[62,81101,79130],{"class":72},[62,81103,81104],{"class":122},"sessionCreationPolicy",[62,81106,81107],{"class":72},"(SessionCreationPolicy.STATELESS))\n",[62,81109,81110,81112,81115,81117,81119],{"class":64,"line":134},[62,81111,2610],{"class":72},[62,81113,81114],{"class":122},"httpBasic",[62,81116,2109],{"class":72},[62,81118,79586],{"class":122},[62,81120,4460],{"class":72},[62,81122,81123,81125,81127],{"class":64,"line":156},[62,81124,2610],{"class":72},[62,81126,2189],{"class":122},[62,81128,822],{"class":72},[62,81130,81131],{"class":64,"line":161},[62,81132,379],{"class":72},[636,81134,81136],{"id":81135},"h2-console-security-filter-chain","H2 Console Security Filter Chain",[22,81138,15404,81139,81142,81143,81145,81146,81149],{},[59,81140,81141],{},"h2ConsoleSecurityFilterChain"," bean that takes in an ",[59,81144,80990],{}," object. Ensure that any request for ",[59,81147,81148],{},"/h2-console/**"," is permitted, CSRF protection is ignored for H2 console, and frame options are disabled.",[22,81151,81152,81153,81155,81156,81159,81160,22831],{},"You'll also need to add an ",[59,81154,81001],{}," annotation here to ensure that this security configuration is processed in the correct order. For example, add ",[59,81157,81158],{},"@Order(2)"," as an annotation on your ",[59,81161,81141],{},[52,81163,81165],{"className":54,"code":81164,"language":56,"meta":57,"style":57},"@Bean\n@Order(2)\nSecurityFilterChain h2ConsoleSecurityFilterChain(HttpSecurity http) throws Exception {\n return http\n .securityMatcher(AntPathRequestMatcher.antMatcher(\"/h2-console/**\"))\n .authorizeHttpRequests( auth -> {\n auth.requestMatchers(AntPathRequestMatcher.antMatcher(\"/h2-console/**\")).permitAll();\n })\n .csrf(csrf -> csrf.ignoringRequestMatchers(AntPathRequestMatcher.antMatcher(\"/h2-console/**\")))\n .headers(headers -> headers.frameOptions().disable())\n .build();\n}\n",[59,81166,81167,81173,81185,81193,81199,81218,81231,81252,81256,81284,81309,81317],{"__ignoreMap":57},[62,81168,81169,81171],{"class":64,"line":65},[62,81170,942],{"class":72},[62,81172,2146],{"class":68},[62,81174,81175,81177,81179,81181,81183],{"class":64,"line":76},[62,81176,942],{"class":72},[62,81178,9415],{"class":68},[62,81180,2109],{"class":72},[62,81182,5219],{"class":149},[62,81184,2212],{"class":72},[62,81186,81187,81189,81191],{"class":64,"line":82},[62,81188,81033],{"class":72},[62,81190,81141],{"class":122},[62,81192,81038],{"class":72},[62,81194,81195,81197],{"class":64,"line":89},[62,81196,2599],{"class":68},[62,81198,11511],{"class":72},[62,81200,81201,81203,81205,81208,81211,81213,81216],{"class":64,"line":95},[62,81202,2610],{"class":72},[62,81204,81051],{"class":122},[62,81206,81207],{"class":72},"(AntPathRequestMatcher.",[62,81209,81210],{"class":122},"antMatcher",[62,81212,2109],{"class":72},[62,81214,81215],{"class":1675},"\"/h2-console/**\"",[62,81217,5047],{"class":72},[62,81219,81220,81222,81224,81227,81229],{"class":64,"line":101},[62,81221,2610],{"class":72},[62,81223,11533],{"class":122},[62,81225,81226],{"class":72},"( auth ",[62,81228,800],{"class":68},[62,81230,126],{"class":72},[62,81232,81233,81235,81237,81239,81241,81243,81245,81248,81250],{"class":64,"line":107},[62,81234,81075],{"class":72},[62,81236,15490],{"class":122},[62,81238,81207],{"class":72},[62,81240,81210],{"class":122},[62,81242,2109],{"class":72},[62,81244,81215],{"class":1675},[62,81246,81247],{"class":72},")).",[62,81249,11549],{"class":122},[62,81251,822],{"class":72},[62,81253,81254],{"class":64,"line":113},[62,81255,13231],{"class":72},[62,81257,81258,81260,81262,81265,81267,81270,81273,81275,81277,81279,81281],{"class":64,"line":129},[62,81259,2610],{"class":72},[62,81261,11558],{"class":122},[62,81263,81264],{"class":72},"(csrf ",[62,81266,800],{"class":68},[62,81268,81269],{"class":72}," csrf.",[62,81271,81272],{"class":122},"ignoringRequestMatchers",[62,81274,81207],{"class":72},[62,81276,81210],{"class":122},[62,81278,2109],{"class":72},[62,81280,81215],{"class":1675},[62,81282,81283],{"class":72},")))\n",[62,81285,81286,81288,81291,81294,81296,81299,81302,81304,81307],{"class":64,"line":134},[62,81287,2610],{"class":72},[62,81289,81290],{"class":122},"headers",[62,81292,81293],{"class":72},"(headers ",[62,81295,800],{"class":68},[62,81297,81298],{"class":72}," headers.",[62,81300,81301],{"class":122},"frameOptions",[62,81303,3229],{"class":72},[62,81305,81306],{"class":122},"disable",[62,81308,4460],{"class":72},[62,81310,81311,81313,81315],{"class":64,"line":156},[62,81312,2610],{"class":72},[62,81314,2189],{"class":122},[62,81316,822],{"class":72},[62,81318,81319],{"class":64,"line":161},[62,81320,379],{"class":72},[636,81322,81324],{"id":81323},"form-login-security-filter-chain","Form Login Security Filter Chain",[22,81326,81327,81328,81142,81331,81333],{},"Finally, create a ",[59,81329,81330],{},"formLoginSecurityFilterChain",[59,81332,80990],{}," object. In this configuration, permit access to the root URL (\"/\") without authentication, and authenticate any other request with form login.",[22,81335,81336,81337,81340],{},"Add the ",[59,81338,81339],{},"@Order(3)"," annotation to ensure that this security configuration is processed last.",[52,81342,81344],{"className":54,"code":81343,"language":56,"meta":57,"style":57},"@Bean\n@Order(3)\nSecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n return http\n .authorizeHttpRequests(auth -> {\n auth.requestMatchers(\"/\").permitAll();\n auth.requestMatchers(\"/error\").permitAll();\n auth.anyRequest().authenticated();\n }\n )\n .formLogin(withDefaults())\n .build();\n}\n",[59,81345,81346,81352,81364,81372,81378,81390,81406,81422,81434,81438,81442,81454,81462],{"__ignoreMap":57},[62,81347,81348,81350],{"class":64,"line":65},[62,81349,942],{"class":72},[62,81351,2146],{"class":68},[62,81353,81354,81356,81358,81360,81362],{"class":64,"line":76},[62,81355,942],{"class":72},[62,81357,9415],{"class":68},[62,81359,2109],{"class":72},[62,81361,4472],{"class":149},[62,81363,2212],{"class":72},[62,81365,81366,81368,81370],{"class":64,"line":82},[62,81367,81033],{"class":72},[62,81369,11490],{"class":122},[62,81371,81038],{"class":72},[62,81373,81374,81376],{"class":64,"line":89},[62,81375,2599],{"class":68},[62,81377,11511],{"class":72},[62,81379,81380,81382,81384,81386,81388],{"class":64,"line":95},[62,81381,2610],{"class":72},[62,81383,11533],{"class":122},[62,81385,11536],{"class":72},[62,81387,800],{"class":68},[62,81389,126],{"class":72},[62,81391,81392,81394,81396,81398,81400,81402,81404],{"class":64,"line":101},[62,81393,79529],{"class":72},[62,81395,15490],{"class":122},[62,81397,2109],{"class":72},[62,81399,15635],{"class":1675},[62,81401,15503],{"class":72},[62,81403,11549],{"class":122},[62,81405,822],{"class":72},[62,81407,81408,81410,81412,81414,81416,81418,81420],{"class":64,"line":107},[62,81409,79529],{"class":72},[62,81411,15490],{"class":122},[62,81413,2109],{"class":72},[62,81415,15500],{"class":1675},[62,81417,15503],{"class":72},[62,81419,11549],{"class":122},[62,81421,822],{"class":72},[62,81423,81424,81426,81428,81430,81432],{"class":64,"line":113},[62,81425,79529],{"class":72},[62,81427,11544],{"class":122},[62,81429,3229],{"class":72},[62,81431,15518],{"class":122},[62,81433,822],{"class":72},[62,81435,81436],{"class":64,"line":129},[62,81437,6325],{"class":72},[62,81439,81440],{"class":64,"line":134},[62,81441,14430],{"class":72},[62,81443,81444,81446,81448,81450,81452],{"class":64,"line":156},[62,81445,2610],{"class":72},[62,81447,11518],{"class":122},[62,81449,2109],{"class":72},[62,81451,79586],{"class":122},[62,81453,4460],{"class":72},[62,81455,81456,81458,81460],{"class":64,"line":161},[62,81457,2610],{"class":72},[62,81459,2189],{"class":122},[62,81461,822],{"class":72},[62,81463,81464],{"class":64,"line":167},[62,81465,379],{"class":72},[636,81467,81469],{"id":81468},"adding-security-matchers","Adding Security Matchers",[22,81471,81472,81473,81476],{},"To ensure that each security filter chain is only invoked for the matching pattern, add an ",[59,81474,81475],{},"@SecurityMatcher"," annotation to each bean:",[915,81478,81479,81485,81491],{},[37,81480,81481,81482],{},"For the H2 Console filter chain, use an Ant matcher: ",[59,81483,81484],{},"@SecurityMatcher(AntPathRequestMatcher(\"/h2-console/**\"))",[37,81486,81487,81488],{},"For the API filter chain, use an MVC matcher: ",[59,81489,81490],{},"@SecurityMatcher(MvcRequestMatcher(\"/api/**\"))",[37,81492,81493,81494,81496],{},"For the Form Login filter chain, you can omit the ",[59,81495,81475],{}," as it will only be invoked if none of the other matchers apply.",[22,81498,81499],{},"Now, run the application again and test each scenario:",[34,81501,81502,81507,81513],{},[37,81503,81504,81505],{},"Access the H2 console without authentication: ",[59,81506,52672],{},[37,81508,81509,81510,81512],{},"Call the ",[59,81511,80617],{}," endpoint and authenticate using HTTP Basic",[37,81514,81515,81516,81519],{},"Access the ",[59,81517,81518],{},"/private"," URL and authenticate using Form login",[22,81521,81522],{},"You should find that each security configuration works as expected.",[26,81524,1499],{"id":1498},[22,81526,81527,81528,976,81530,81532],{},"In this tutorial, we covered how to create multiple Spring Security configurations for different authorization scenarios. This approach can help keep your security configurations clean and focused, making them easier to understand and maintain. By leveraging Spring Security's ",[59,81529,81001],{},[59,81531,81475],{},", and other features, you can easily build complex and secure applications.",[22,81534,1525],{},[1527,81536,81537],{},"html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}html pre.shiki code .spoOn, html code.shiki .spoOn{--shiki-default:#E36209;--shiki-dark:#FFAB70;--shiki-sepia:#E36209}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}",{"title":57,"searchDepth":76,"depth":76,"links":81539},[81540,81541,81542,81543,81549],{"id":34880,"depth":76,"text":34881},{"id":80401,"depth":76,"text":80402},{"id":80877,"depth":76,"text":80878},{"id":15400,"depth":76,"text":15401,"children":81544},[81545,81546,81547,81548],{"id":80979,"depth":82,"text":80980},{"id":81135,"depth":82,"text":81136},{"id":81323,"depth":82,"text":81324},{"id":81468,"depth":82,"text":81469},{"id":1498,"depth":76,"text":1499},{"_id":81551,"path":81552,"title":81553,"description":81554,"meta":81555,"body":81560},"content/blog/2023/04/17/graphql-client.md","/blog/2023/04/17/graphql-client","Create a GraphQL Client in Java with Spring Boot","If you're working on a Spring application and you need to call another REST API, you have tools such as RestTemplate or WebClient. But what if you need to call a GraphQL API? With Spring, a GraphQL Client is available. In this tutorial, you will learn how to include it in your project and use it by building a practical example.",{"slug":81556,"date":81557,"published":13,"tags":81558,"author":17,"cover":81559,"excerpt":-1},"graphql-client","2023-04-17T08:00:00.000Z",[2925,8507],"./graphql-client-thumbnail.png",{"type":19,"value":81561,"toc":82160},[81562,81564,81568,81579,81583,81599,81604,81616,81620,81630,81691,81695,81711,81747,81751,81760,81767,81774,81796,81802,81886,81890,81897,82046,82055,82058,82067,82140,82146,82151,82154,82157],[22,81563,81554],{},[26,81565,81567],{"id":81566},"accessing-a-free-graphql-api","Accessing a Free GraphQL API",[22,81569,81570,81571,81575,81576,81578],{},"To demonstrate, we will utilize a free GraphQL API from ",[677,81572,81574],{"href":81573},"www.examplelink.com","Countries Trevorblades.com",". The query returns information such as the country's name, emoji, currency, code, and capital. Note that some fields may return ",[59,81577,3256],{}," values, so we should guard against those in our application.",[26,81580,81582],{"id":81581},"creating-our-application","Creating Our Application",[22,81584,81585,81586,81588,81589,81592,81593,976,81595,4201,81597,2755],{},"We will generate our Spring Boot project from ",[677,81587,2903],{"href":81573},". For our application, we will use Maven as a build tool, Java as the language, and the latest version of Spring Boot. Let's name the application ",[59,81590,81591],{},"Countries",", and we will be using Java 17. For our dependencies, we require ",[59,81594,78714],{},[59,81596,80389],{},[59,81598,80392],{},[22,81600,81601],{},[653,81602],{"alt":24606,"src":81603},"/images/blog/2023/04/17/graphql-client-spring-init.png",[22,81605,3521,81606,81608,81609,81611,81612,81615],{},[59,81607,78714],{}," dependency enables us to build a web application. ",[59,81610,80389],{}," is crucial because it will allow us to load country data and store it in a database to avoid repeating GraphQL API calls. For our database, we will use an ",[59,81613,81614],{},"H2"," in-memory database for simplicity.",[26,81617,81619],{"id":81618},"setting-up-our-country-record","Setting Up Our Country Record",[22,81621,81622,81623,81625,81626,81629],{},"Having generated the application, let's move to the IDE and start coding. We will create a ",[59,81624,11933],{}," package and within it a record named ",[59,81627,81628],{},"Country"," to represent a country based on the fields from the GraphQL API.",[52,81631,81633],{"className":54,"code":81632,"language":56,"meta":57,"style":57},"public record Country(\n @Id\n Integer id,\n String name,\n String emoji,\n String currency,\n String code,\n String capital\n) {\n}\n",[59,81634,81635,81646,81653,81658,81663,81668,81673,81678,81683,81687],{"__ignoreMap":57},[62,81636,81637,81639,81641,81644],{"class":64,"line":65},[62,81638,116],{"class":68},[62,81640,2996],{"class":68},[62,81642,81643],{"class":122}," Country",[62,81645,3301],{"class":72},[62,81647,81648,81651],{"class":64,"line":76},[62,81649,81650],{"class":72}," @",[62,81652,22298],{"class":68},[62,81654,81655],{"class":64,"line":82},[62,81656,81657],{"class":72}," Integer id,\n",[62,81659,81660],{"class":64,"line":89},[62,81661,81662],{"class":72}," String name,\n",[62,81664,81665],{"class":64,"line":95},[62,81666,81667],{"class":72}," String emoji,\n",[62,81669,81670],{"class":64,"line":101},[62,81671,81672],{"class":72}," String currency,\n",[62,81674,81675],{"class":64,"line":107},[62,81676,81677],{"class":72}," String code,\n",[62,81679,81680],{"class":64,"line":113},[62,81681,81682],{"class":72}," String capital\n",[62,81684,81685],{"class":64,"line":129},[62,81686,768],{"class":72},[62,81688,81689],{"class":64,"line":134},[62,81690,379],{"class":72},[26,81692,81694],{"id":81693},"building-our-country-repository","Building Our Country Repository",[22,81696,81697,81698,81701,81702,79981,81704,81707,81708,81710],{},"We'll then build a ",[59,81699,81700],{},"CountryRepository"," interface in the ",[59,81703,23540],{},[59,81705,81706],{},"CrudRepository\u003CCountry, Integer>",". This will provide basic CRUD functionality for the ",[59,81709,81628],{}," entity.",[52,81712,81714],{"className":54,"code":81713,"language":56,"meta":57,"style":57},"public interface CountryRepository extends ListCrudRepository\u003CCountry,Integer> {\n\n}\n",[59,81715,81716,81739,81743],{"__ignoreMap":57},[62,81717,81718,81720,81722,81725,81727,81729,81731,81733,81735,81737],{"class":64,"line":65},[62,81719,116],{"class":68},[62,81721,8531],{"class":68},[62,81723,81724],{"class":122}," CountryRepository",[62,81726,8537],{"class":68},[62,81728,45494],{"class":122},[62,81730,760],{"class":72},[62,81732,81628],{"class":68},[62,81734,32225],{"class":72},[62,81736,979],{"class":68},[62,81738,8552],{"class":72},[62,81740,81741],{"class":64,"line":76},[62,81742,79],{"emptyLinePlaceholder":13},[62,81744,81745],{"class":64,"line":82},[62,81746,379],{"class":72},[26,81748,81750],{"id":81749},"constructing-the-country-service","Constructing The Country Service",[22,81752,18490,81753,48324,81756,81759],{},[59,81754,81755],{},"CountryService",[59,81757,81758],{},"Service"," package. This service will connect with the GraphQL API and return the list of countries.",[22,81761,81762,81763,81766],{},"In the service, we can use a standard WebClient, but to simplify the process, we will utilize the ",[59,81764,81765],{},"HttpGraphQLClient"," provided by Spring for GraphQL.",[22,81768,81769,81770,81773],{},"To use this client, we will add another dependency, ",[59,81771,81772],{},"spring-boot-starter-graphql",". If you already selected Spring for GraphQL from the Spring Initializr you can skip this step.",[52,81775,81777],{"className":1769,"code":81776,"language":1771,"meta":57,"style":57},"\u003Cdependency>\n \u003CgroupId>org.springframework.boot\u003C/groupId>\n \u003CartifactId>spring-boot-starter-graphql\u003C/artifactId>\n\u003C/dependency>\n",[59,81778,81779,81783,81787,81792],{"__ignoreMap":57},[62,81780,81781],{"class":64,"line":65},[62,81782,46425],{},[62,81784,81785],{"class":64,"line":76},[62,81786,78970],{},[62,81788,81789],{"class":64,"line":82},[62,81790,81791],{}," \u003CartifactId>spring-boot-starter-graphql\u003C/artifactId>\n",[62,81793,81794],{"class":64,"line":89},[62,81795,46445],{},[22,81797,81798,81799,2755],{},"Then, we instantiate a ",[59,81800,81801],{},"GraphQLClient",[52,81803,81805],{"className":54,"code":81804,"language":56,"meta":57,"style":57},"private final HttpGraphQlClient graphQlClient;\n\npublic CountryService() {\n WebClient client = WebClient.builder()\n .baseUrl(\"https://countries.trevorblades.com\")\n .build();\n graphQlClient = HttpGraphQlClient.builder(client).build();\n}\n",[59,81806,81807,81816,81820,81829,81842,81855,81863,81882],{"__ignoreMap":57},[62,81808,81809,81811,81813],{"class":64,"line":65},[62,81810,1359],{"class":68},[62,81812,458],{"class":68},[62,81814,81815],{"class":72}," HttpGraphQlClient graphQlClient;\n",[62,81817,81818],{"class":64,"line":76},[62,81819,79],{"emptyLinePlaceholder":13},[62,81821,81822,81824,81827],{"class":64,"line":82},[62,81823,116],{"class":68},[62,81825,81826],{"class":122}," CountryService",[62,81828,206],{"class":72},[62,81830,81831,81834,81836,81838,81840],{"class":64,"line":89},[62,81832,81833],{"class":72}," WebClient client ",[62,81835,146],{"class":68},[62,81837,47816],{"class":72},[62,81839,2160],{"class":122},[62,81841,2223],{"class":72},[62,81843,81844,81846,81848,81850,81853],{"class":64,"line":95},[62,81845,2610],{"class":72},[62,81847,11188],{"class":122},[62,81849,2109],{"class":72},[62,81851,81852],{"class":1675},"\"https://countries.trevorblades.com\"",[62,81854,2212],{"class":72},[62,81856,81857,81859,81861],{"class":64,"line":101},[62,81858,2610],{"class":72},[62,81860,2189],{"class":122},[62,81862,822],{"class":72},[62,81864,81865,81868,81870,81873,81875,81878,81880],{"class":64,"line":107},[62,81866,81867],{"class":72}," graphQlClient ",[62,81869,146],{"class":68},[62,81871,81872],{"class":72}," HttpGraphQlClient.",[62,81874,2160],{"class":122},[62,81876,81877],{"class":72},"(client).",[62,81879,2189],{"class":122},[62,81881,822],{"class":72},[62,81883,81884],{"class":64,"line":113},[62,81885,379],{"class":72},[26,81887,81889],{"id":81888},"calling-the-graphql-api","Calling The GraphQL API",[22,81891,81892,81893,81896],{},"Now that our GraphQL client is ready, we can create a method, ",[59,81894,81895],{},"getCountries()",", to make the API call to retrieve a list of countries.",[52,81898,81900],{"className":54,"code":81899,"language":56,"meta":57,"style":57}," public Mono\u003CList\u003CCountry>> getCountries() {\n String document = \"\"\"\n query {\n countries {\n name\n emoji\n currency\n code\n capital\n }\n }\n \"\"\";\n\n Mono\u003CList\u003CCountry>> countries = graphQlClient.document(document)\n .retrieve(\"countries\")\n .toEntityList(Country.class);\n\n return countries;\n }\n",[59,81901,81902,81924,81933,81938,81943,81948,81953,81958,81963,81968,81972,81976,81983,81987,82008,82021,82031,82035,82042],{"__ignoreMap":57},[62,81903,81904,81906,81909,81911,81913,81915,81917,81919,81922],{"class":64,"line":65},[62,81905,194],{"class":68},[62,81907,81908],{"class":72}," Mono",[62,81910,760],{"class":68},[62,81912,59063],{"class":72},[62,81914,760],{"class":68},[62,81916,81628],{"class":72},[62,81918,5632],{"class":68},[62,81920,81921],{"class":122}," getCountries",[62,81923,206],{"class":72},[62,81925,81926,81929,81931],{"class":64,"line":76},[62,81927,81928],{"class":72}," String document ",[62,81930,146],{"class":68},[62,81932,18255],{"class":1675},[62,81934,81935],{"class":64,"line":82},[62,81936,81937],{"class":1675}," query {\n",[62,81939,81940],{"class":64,"line":89},[62,81941,81942],{"class":1675}," countries {\n",[62,81944,81945],{"class":64,"line":95},[62,81946,81947],{"class":1675}," name\n",[62,81949,81950],{"class":64,"line":101},[62,81951,81952],{"class":1675}," emoji\n",[62,81954,81955],{"class":64,"line":107},[62,81956,81957],{"class":1675}," currency\n",[62,81959,81960],{"class":64,"line":113},[62,81961,81962],{"class":1675}," code\n",[62,81964,81965],{"class":64,"line":129},[62,81966,81967],{"class":1675}," capital\n",[62,81969,81970],{"class":64,"line":134},[62,81971,861],{"class":1675},[62,81973,81974],{"class":64,"line":156},[62,81975,533],{"class":1675},[62,81977,81978,81981],{"class":64,"line":161},[62,81979,81980],{"class":1675}," \"\"\"",[62,81982,153],{"class":72},[62,81984,81985],{"class":64,"line":167},[62,81986,79],{"emptyLinePlaceholder":13},[62,81988,81989,81992,81994,81997,81999,82002,82005],{"class":64,"line":173},[62,81990,81991],{"class":72}," Mono\u003CList\u003C",[62,81993,81628],{"class":68},[62,81995,81996],{"class":72},">> countries ",[62,81998,146],{"class":68},[62,82000,82001],{"class":72}," graphQlClient.",[62,82003,82004],{"class":122},"document",[62,82006,82007],{"class":72},"(document)\n",[62,82009,82010,82012,82014,82016,82019],{"class":64,"line":179},[62,82011,2418],{"class":72},[62,82013,11405],{"class":122},[62,82015,2109],{"class":72},[62,82017,82018],{"class":1675},"\"countries\"",[62,82020,2212],{"class":72},[62,82022,82023,82025,82028],{"class":64,"line":185},[62,82024,2418],{"class":72},[62,82026,82027],{"class":122},"toEntityList",[62,82029,82030],{"class":72},"(Country.class);\n",[62,82032,82033],{"class":64,"line":191},[62,82034,79],{"emptyLinePlaceholder":13},[62,82036,82037,82039],{"class":64,"line":209},[62,82038,360],{"class":68},[62,82040,82041],{"class":72}," countries;\n",[62,82043,82044],{"class":64,"line":220},[62,82045,223],{"class":72},[22,82047,3521,82048,82051,82052,82054],{},[59,82049,82050],{},"graphQLClient.query(document).retrieve().asEntityList(Country.class)"," sequence initiates the API call, retrieves the result, and maps it to a list of ",[59,82053,81628],{}," objects.",[26,82056,82057],{"id":2290},"Running The Application",[22,82059,82060,82061,82064,82065,2755],{},"To run the application, we need to add a CommandLineRunner in the main application class that will call ",[59,82062,82063],{},"getCountries"," and save the list of countries in the database using our ",[59,82066,81700],{},[52,82068,82070],{"className":54,"code":82069,"language":56,"meta":57,"style":57},"@Bean\nCommandLineRunner commandLineRunner(CountryService service, CountryRepository repository) {\n return args -> {\n Mono\u003CList\u003CCountry>> countries = service.getCountries();\n countries.subscribe(repository::saveAll);\n };\n}\n",[59,82071,82072,82078,82087,82098,82116,82131,82136],{"__ignoreMap":57},[62,82073,82074,82076],{"class":64,"line":65},[62,82075,942],{"class":72},[62,82077,2146],{"class":68},[62,82079,82080,82082,82084],{"class":64,"line":76},[62,82081,40055],{"class":72},[62,82083,2154],{"class":122},[62,82085,82086],{"class":72},"(CountryService service, CountryRepository repository) {\n",[62,82088,82089,82092,82094,82096],{"class":64,"line":82},[62,82090,82091],{"class":68}," return",[62,82093,2169],{"class":72},[62,82095,800],{"class":68},[62,82097,126],{"class":72},[62,82099,82100,82103,82105,82107,82109,82112,82114],{"class":64,"line":89},[62,82101,82102],{"class":72}," Mono\u003CList\u003C",[62,82104,81628],{"class":68},[62,82106,81996],{"class":72},[62,82108,146],{"class":68},[62,82110,82111],{"class":72}," service.",[62,82113,82063],{"class":122},[62,82115,822],{"class":72},[62,82117,82118,82121,82123,82126,82128],{"class":64,"line":95},[62,82119,82120],{"class":72}," countries.",[62,82122,50980],{"class":122},[62,82124,82125],{"class":72},"(repository",[62,82127,4451],{"class":68},[62,82129,82130],{"class":72},"saveAll);\n",[62,82132,82133],{"class":64,"line":101},[62,82134,82135],{"class":72}," };\n",[62,82137,82138],{"class":64,"line":107},[62,82139,379],{"class":72},[22,82141,82142,82143,2755],{},"After running the application and no exceptions are thrown, we can confirm that our country data has been saved in the database. You can check this by accessing the H2 Console and running a ",[59,82144,82145],{},"SELECT * FROM country;",[22,82147,82148],{},[653,82149],{"alt":62194,"src":82150},"/images/blog/2023/04/17/h2-select-countries.png",[22,82152,82153],{},"That's all there is to connecting to a GraphQL API in Spring Boot! You now have the tools needed to tackle any GraphQL requests in your projects, courtesy of Spring for GraphQL.",[22,82155,82156],{},"If you found this post helpful, please give it a thumbs up, subscribe to the channel, and, as always, happy coding!",[1527,82158,82159],{},"html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}",{"title":57,"searchDepth":76,"depth":76,"links":82161},[82162,82163,82164,82165,82166,82167,82168],{"id":81566,"depth":76,"text":81567},{"id":81581,"depth":76,"text":81582},{"id":81618,"depth":76,"text":81619},{"id":81693,"depth":76,"text":81694},{"id":81749,"depth":76,"text":81750},{"id":81888,"depth":76,"text":81889},{"id":2290,"depth":76,"text":82057},{"_id":82170,"path":82171,"title":82172,"description":73331,"meta":82173,"body":82178},"content/blog/2023/04/12/virtual-threads-spring.md","/blog/2023/04/12/virtual-threads-spring","Spring into the Future: Embracing Virtual Threads with Java's Project Loom",{"slug":82174,"date":82175,"published":13,"tags":82176,"author":17,"cover":82177,"excerpt":-1},"virtual-threads-spring","2023-04-12T16:00:00.000Z",[2925,8507],"./virtual-threads-spring-boot.png",{"type":19,"value":82179,"toc":82548},[82180,82183,82187,82190,82198,82202,82205,82208,82211,82217,82220,82223,82229,82235,82239,82242,82256,82262,82266,82269,82275,82279,82282,82521,82524,82526,82540,82546],[22,82181,82182],{},"Welcome back, friends! Today, we're diving into Project Loom and specifically virtual threads. We'll be covering what they are, why you should care as a Spring developer, and even provide a demo on using virtual threads in a Spring application. Let’s begin!",[26,82184,82186],{"id":82185},"what-is-project-loom","What is Project Loom?",[22,82188,82189],{},"Project Loom aims to reduce the effort of writing, maintaining, and observing high-throughput concurrent applications. It introduces virtual threads as a solution to improve performance and scalability, particularly when dealing with blocking APIs commonly found in Spring MVC applications.",[22,82191,82192,82193,82197],{},"It's been announced that ",[677,82194,82196],{"href":38502,"rel":82195},[681],"virtual threads are targeted for JDK 21",", which is exciting news as it means they might show up in Spring Framework 6.1 (also targeted for later this year). So it's about time we all learn about virtual threads and why we might care about them as Spring developers.",[26,82199,82201],{"id":82200},"history-why-we-need-virtual-threads","History: Why We Need Virtual Threads",[22,82203,82204],{},"Let's first understand the thread-per-request model and how threads work in Java. When a client makes a request to a server, the request often involves the server to process blocking operations, such as reading or persisting from a database using JDBC or JPA, writing to a file, or communicating with another service.",[22,82206,82207],{},"This application works great on your laptop and during the initial stages of implementation. However, once news spreads about your exceptional new application, you may start experiencing a high volume of traffic that could cause it to crash.",[22,82209,82210],{},"Why is this happening? What can you do to address this?",[22,82212,82213],{},[653,82214],{"alt":82215,"src":82216},"embracing-virtual-threads.png","/images/blog/2023/04/12/embracing-virtual-threads.png",[22,82218,82219],{},"To answer these questions you must understand the thread-per-request model. When a request is handled by the web server it is using a Java Thread and that thread is tied to an operating system thread. When you make a call to a blocking operation like reading or persisting to a database that thread is blocked from doing anything else until the request is fulfilled.",[22,82221,82222],{},"The thread-per-request model ties up threads in the system, and there is a maximum number of concurrent threads allowed. When the number of maximum threads has been reached, each subsequent request will need to wait for a thread to be released to fulfill that request. This can cause slowness or even errors in the application when it experiences high traffic.",[22,82224,82225],{},[653,82226],{"alt":82227,"src":82228},"thread-per-request.png","/images/blog/2023/04/12/thread-per-request.png",[22,82230,82231],{},[653,82232],{"alt":82233,"src":82234},"java-is-made-of-threads.png","/images/blog/2023/04/12/java-is-made-of-threads.png",[26,82236,82238],{"id":82237},"scalability-solutions","Scalability Solutions",[22,82240,82241],{},"To improve scalability, there are two main approaches that you can currently take advantage of:",[34,82243,82244,82250],{},[37,82245,82246,82249],{},[646,82247,82248],{},"Scaling Hardware",": Add more memory, CPU or servers (vertical and horizontal scaling)",[37,82251,82252,82255],{},[646,82253,82254],{},"Asynchronous Programming",": Writing non-blocking software to optimize thread usage",[22,82257,82258],{},[653,82259],{"alt":82260,"src":82261},"thread-per-request-solutions.png","/images/blog/2023/04/12/thread-per-request-solutions.png",[26,82263,82265],{"id":82264},"introduction-to-virtual-threads","Introduction to Virtual Threads",[22,82267,82268],{},"Virtual threads, available as a preview release in JDK 19 and 20, are lightweight, inexpensive, and easy to create. They are tied to a platform thread that is connected to the operating system thread. Consequently, we no longer tie up platform threads in our applications and can handle more concurrent requests. The most exciting aspect of virtual threads is that we can use them in our Spring applications with little or no code changes.",[22,82270,82271],{},[653,82272],{"alt":82273,"src":82274},"virtual-threads.png","/images/blog/2023/04/12/virtual-threads.png",[26,82276,82278],{"id":82277},"demo-using-virtual-threads-in-spring-applications","Demo: Using Virtual Threads in Spring Applications",[22,82280,82281],{},"To create a new Spring project that uses virtual threads, follow these steps:",[34,82283,82284,82290,82293,82353,82422,82425,82518],{},[37,82285,16962,82286,82289],{},[677,82287,2903],{"href":2901,"rel":82288},[681]," and select Maven, Java, and the latest version of Spring Boot.",[37,82291,82292],{},"Fill in the project metadata, choose Java 20 (or 19 if available), and select the \"Web\" dependency.",[37,82294,82295,82296,82298,82299,82302,82303],{},"Generate the project, open it in IntelliJ, and make sure the ",[59,82297,1765],{}," file has ",[59,82300,82301],{},"maven-compiler-plugin"," configuration setup to enable the preview feature.",[52,82304,82306],{"className":1769,"code":82305,"language":1771,"meta":57,"style":57},"\u003Cplugin>\n \u003CgroupId>org.apache.maven.plugins\u003C/groupId>\n \u003CartifactId>maven-compiler-plugin\u003C/artifactId>\n \u003Cconfiguration>\n \u003CcompilerArgs>--enable-preview\u003C/compilerArgs>\n \u003Csource>19\u003C/source>\n \u003Ctarget>19\u003C/target>\n \u003C/configuration>\n\u003C/plugin>\n",[59,82307,82308,82313,82318,82323,82328,82333,82338,82343,82348],{"__ignoreMap":57},[62,82309,82310],{"class":64,"line":65},[62,82311,82312],{},"\u003Cplugin>\n",[62,82314,82315],{"class":64,"line":76},[62,82316,82317],{}," \u003CgroupId>org.apache.maven.plugins\u003C/groupId>\n",[62,82319,82320],{"class":64,"line":82},[62,82321,82322],{}," \u003CartifactId>maven-compiler-plugin\u003C/artifactId>\n",[62,82324,82325],{"class":64,"line":89},[62,82326,82327],{}," \u003Cconfiguration>\n",[62,82329,82330],{"class":64,"line":95},[62,82331,82332],{}," \u003CcompilerArgs>--enable-preview\u003C/compilerArgs>\n",[62,82334,82335],{"class":64,"line":101},[62,82336,82337],{}," \u003Csource>19\u003C/source>\n",[62,82339,82340],{"class":64,"line":107},[62,82341,82342],{}," \u003Ctarget>19\u003C/target>\n",[62,82344,82345],{"class":64,"line":113},[62,82346,82347],{}," \u003C/configuration>\n",[62,82349,82350],{"class":64,"line":129},[62,82351,82352],{},"\u003C/plugin>\n",[37,82354,82355,82356],{},"Create a controller mapping to the root path, and return the current thread's information as a string.",[52,82357,82359],{"className":54,"code":82358,"language":56,"meta":57,"style":57},"@RestController\npublic class HomeController {\n @GetMapping(\"/\")\n public String hello() {\n return Thread.currentThread().toString();\n }\n}\n",[59,82360,82361,82367,82377,82389,82400,82414,82418],{"__ignoreMap":57},[62,82362,82363,82365],{"class":64,"line":65},[62,82364,942],{"class":72},[62,82366,2342],{"class":68},[62,82368,82369,82371,82373,82375],{"class":64,"line":76},[62,82370,116],{"class":68},[62,82372,119],{"class":68},[62,82374,25636],{"class":122},[62,82376,126],{"class":72},[62,82378,82379,82381,82383,82385,82387],{"class":64,"line":82},[62,82380,2143],{"class":72},[62,82382,2548],{"class":68},[62,82384,2109],{"class":72},[62,82386,15635],{"class":1675},[62,82388,2212],{"class":72},[62,82390,82391,82393,82395,82398],{"class":64,"line":89},[62,82392,194],{"class":68},[62,82394,2469],{"class":72},[62,82396,82397],{"class":122},"hello",[62,82399,206],{"class":72},[62,82401,82402,82404,82406,82408,82410,82412],{"class":64,"line":95},[62,82403,360],{"class":68},[62,82405,40570],{"class":72},[62,82407,40557],{"class":122},[62,82409,3229],{"class":72},[62,82411,23175],{"class":122},[62,82413,822],{"class":72},[62,82415,82416],{"class":64,"line":101},[62,82417,223],{"class":72},[62,82419,82420],{"class":64,"line":107},[62,82421,379],{"class":72},[37,82423,82424],{},"Run the application and test it by making a request to the local server. The response should display the main thread's information.",[37,82426,82427,82428,82430,82431,82434,82435,2755,82438],{},"In your ",[59,82429,56036],{}," class, create a new Bean called ",[59,82432,82433],{},"TomcatProtocolHandlerCustomizer"," and set its executor to the new ",[59,82436,82437],{},"VirtualThreadPerTaskExecutor",[52,82439,82441],{"className":54,"code":82440,"language":56,"meta":57,"style":57},"@Bean\n TomcatProtocolHandlerCustomizer\u003C?> protocolHandlerVirtualThreadExecutorCustomizer() {\n return protocolHandler -> {\n log.info(\"Configuring \" + protocolHandler + \" to use VirtualThreadPerTaskExecutor\");\n protocolHandler.setExecutor(Executors.newVirtualThreadPerTaskExecutor());\n };\n }\n",[59,82442,82443,82449,82461,82472,82494,82510,82514],{"__ignoreMap":57},[62,82444,82445,82447],{"class":64,"line":65},[62,82446,942],{"class":72},[62,82448,2146],{"class":68},[62,82450,82451,82454,82456,82459],{"class":64,"line":76},[62,82452,82453],{"class":72}," TomcatProtocolHandlerCustomizer",[62,82455,59528],{"class":68},[62,82457,82458],{"class":122}," protocolHandlerVirtualThreadExecutorCustomizer",[62,82460,206],{"class":72},[62,82462,82463,82465,82468,82470],{"class":64,"line":82},[62,82464,360],{"class":68},[62,82466,82467],{"class":72}," protocolHandler ",[62,82469,800],{"class":68},[62,82471,126],{"class":72},[62,82473,82474,82476,82478,82480,82483,82485,82487,82489,82492],{"class":64,"line":89},[62,82475,13341],{"class":72},[62,82477,12688],{"class":122},[62,82479,2109],{"class":72},[62,82481,82482],{"class":1675},"\"Configuring \"",[62,82484,4507],{"class":68},[62,82486,82467],{"class":72},[62,82488,1148],{"class":68},[62,82490,82491],{"class":1675}," \" to use VirtualThreadPerTaskExecutor\"",[62,82493,1133],{"class":72},[62,82495,82496,82499,82502,82505,82508],{"class":64,"line":95},[62,82497,82498],{"class":72}," protocolHandler.",[62,82500,82501],{"class":122},"setExecutor",[62,82503,82504],{"class":72},"(Executors.",[62,82506,82507],{"class":122},"newVirtualThreadPerTaskExecutor",[62,82509,1091],{"class":72},[62,82511,82512],{"class":64,"line":101},[62,82513,2252],{"class":72},[62,82515,82516],{"class":64,"line":107},[62,82517,223],{"class":72},[37,82519,82520],{},"Restart your application and test it again by making another request to the local server. The response should now display the virtual thread's information.",[22,82522,82523],{},"And that's it! With just a few changes, you can start using virtual threads in your Spring application and take advantage of its performance improvements.",[26,82525,1499],{"id":1498},[22,82527,82528,82529,82534,82535,2755],{},"Virtual threads offer an exciting solution to improve performance and scalability for Spring developers. With little or no code changes required, it's worth exploring how virtual threads can benefit your applications. Be sure to check out the Spring blog posts to learn more about ",[677,82530,82533],{"href":82531,"rel":82532},"https://spring.io/blog/2022/10/11/embracing-virtual-threads",[681],"embracing virtual threads"," and their ",[677,82536,82539],{"href":82537,"rel":82538},"https://spring.io/blog/2023/02/27/web-applications-and-project-loom",[681],"performance analysis",[22,82541,82542,82543,82545],{},"Happy coding, friends!",[36006,82544],{},"\nDan",[1527,82547,47058],{},{"title":57,"searchDepth":76,"depth":76,"links":82549},[82550,82551,82552,82553,82554,82555],{"id":82185,"depth":76,"text":82186},{"id":82200,"depth":76,"text":82201},{"id":82237,"depth":76,"text":82238},{"id":82264,"depth":76,"text":82265},{"id":82277,"depth":76,"text":82278},{"id":1498,"depth":76,"text":1499},{"_id":82557,"path":82558,"title":82559,"description":82560,"meta":82561,"body":82566},"content/blog/2023/03/31/videotap.md","/blog/2023/03/31/videotap","Why VideoTap is a Game-Changer for Content Creators: Convert Videos to Blog Posts in Minutes!","Are you a content creator who struggles to find enough time to produce written content alongside videos? Do you wish there was an easy way to transform your videos into compelling blog posts? Look no further than VideoTap!",{"slug":82562,"date":82563,"published":13,"tags":82564,"author":17,"cover":82565,"excerpt":-1},"videotap","2023-03-31T16:00:00.000Z",[2230],"./video-to-blogpost.png",{"type":19,"value":82567,"toc":82751},[82568,82577,82581,82589,82592,82595,82598,82602,82605,82608,82614,82618,82621,82624,82628,82631,82634,82640,82643,82649,82652,82658,82665,82671,82680,82683,82713,82717,82726,82732,82734,82745],[22,82569,82570,82571,82576],{},"Are you a content creator who struggles to find enough time to produce written content alongside videos? Do you wish there was an easy way to transform your videos into compelling blog posts? Look no further than ",[677,82572,82575],{"href":82573,"rel":82574},"https://videotapit.com/?via=dan",[681],"VideoTap","! This innovative service transcribes and converts your videos into high-quality blog posts, complete with code blocks and other engaging features. Plus, with their recent increase in video length limit to 25 minutes, most videos are a perfect fit. Say goodbye to the hassle of creating separate written content and hello to a more efficient workflow with VideoTap.",[26,82578,82580],{"id":82579},"me-as-a-content-creator","Me as a Content Creator",[22,82582,82583,82584,82588],{},"As a content creator, I produce content for various mediums. I began my blog as a means of helping others who were encountering the same problems as I was. Through this process, I developed a deep appreciation for the writing process. This love for writing has inspired me to continue posting blog entries for over 15 years. I also started a ",[677,82585,82587],{"href":38005,"rel":82586},[681],"weekly newsletter"," as a way of keeping myself writing regularly.",[22,82590,82591],{},"Throughout this process I began to get very interested in Video for the same reasons. My fellow developers were on YouTube and looking for solutions to their problems. I also realized that I could create courses as a side hustle to make a little extra money.",[22,82593,82594],{},"I enjoy writing and producing video content, but there's a problem. As I've dedicated more time to creating videos, I've become less consistent with my writing. Unless I figure out how to clone myself (which I don't think anyone wants), I will have to choose where to spend my time.",[22,82596,82597],{},"A main goal of mine over the last 2 years has been to grow my YouTube channel, so that's where I'm currently focusing my efforts. Many people may not realize the amount of time and effort that goes into creating a single video, but I can attest that there's a lot involved.",[636,82599,82601],{"id":82600},"blog-post-video","Blog Post → Video",[22,82603,82604],{},"Once I'm ready to publish a video, I often lack the energy to write a full blog post to accompany it. In an ideal scenario, I would start with a blog post. This would enable me to work on the code demo and decide exactly what I want to say, and more importantly, what I don't want to spend time on. After preparing the blog post, I could use it as a script to guide the video creation process.",[22,82606,82607],{},"However, in the real world, with a full-time job, family, and limited time, I need to be precise about how I spend my time. I would rather create two videos than one blog post plus a video. Nevertheless, I would prefer to make two videos and two blog posts instead of only one. That is why I'm excited about this new service that will allow me to do that.",[22,82609,82610],{},[653,82611],{"alt":82612,"src":82613},"Writing a new blog post","/images/blog/2023/03/31/photo-1504691342899-4d92b50853e1.jpeg",[26,82615,82617],{"id":82616},"what-is-videotap","What is VideoTap",[22,82619,82620],{},"Now that you understand the problem that VideoTap solves, let's discuss how it does so. VideoTap is a service that transcribes and converts videos into blog posts. If all it did was take the transcript and create a blog post, it wouldn't be very helpful. In fact, I've tried doing this on my own before, and I can attest that nobody wants to read a video transcript.",[22,82622,82623],{},"What sets VideoTap apart is the quality of its blog posts. Instead of simply using a transcription, they utilize GPT-4 to generate high-quality blog posts that include headings, images, code blocks, and more. This makes their articles much more compelling. I'm not sure what their secret sauce is because I tried a similar process on my own, but was unable to come up with high-quality posts.",[636,82625,82627],{"id":82626},"how-to-convert-a-video-to-a-blog-post","How to convert a video to a blog post",[22,82629,82630],{},"Using VideoTap costs $1 per minute of video you want to convert. For example, a 20 minute video would cost $20. Additionally, they offer a white glove service where they take care of the video conversion for you and send you the final results. This service costs $3 per minute of video.",[22,82632,82633],{},"Once you create a free account, you can begin the process by going to your dashboard. You can add a video by URL or connect your YouTube account. Once you add a video, the length will be determined, and you will be prompted to pay.",[22,82635,82636],{},[653,82637],{"alt":82638,"src":82639},"Ordering a new Blog Post with VideoTap","/images/blog/2023/03/31/order-new-blog-posts.png",[22,82641,82642],{},"After your payment is processed, the service will start generating a blog post. This process may take a few minutes as it involves transcribing the video, adding titles, headers, code, and formatting everything.",[22,82644,82645],{},[653,82646],{"alt":82647,"src":82648},"Generating a new blog post with VideoTap","/images/blog/2023/03/31/generating-blog-post.png",[22,82650,82651],{},"When the conversion is complete you will be taken to this screen where you can make further edits. First off I love the simplicity of this design with the image and outline on the left. The blog post isn’t perfect and this is usually where I will make my first pass on editing it.",[22,82653,82654],{},[653,82655],{"alt":82656,"src":82657},"New blog Post created by VideoTap","/images/blog/2023/03/31/new-blog-post.png",[22,82659,82660,82661,82664],{},"When the blog post is ready to export click the ",[59,82662,82663],{},"Export Blog Post"," button in the upper right hand corner. I write all of my blog posts in Markdown so the copy markdown is a great feature. It’s almost like this service was written by a developer for developers (👋🏻 Chris).",[22,82666,82667],{},[653,82668],{"alt":82669,"src":82670},"Export Blog Post from VideoTap as Markdown","/images/blog/2023/03/31/export-blog-post.png",[636,82672,82674,82675,82679],{"id":82673},"show-me-the-money-blog-posts","Show me the ",[82676,82677,82678],"del",{},"Money"," Blog Posts",[22,82681,82682],{},"I think its easy to talk about a service but what about a real world use case. I have about 10 videos in VideoTap currently and here are a few examples of ones I have converted into blog post. At the top of each blog post you can find the YouTube video that I used to create the post.",[915,82684,82685,82692,82699,82706],{},[37,82686,82687],{},[677,82688,82691],{"href":82689,"rel":82690},"https://www.danvega.dev/blog/2023/03/20/graphql-mutations/",[681],"GraphQL Mutations",[37,82693,82694],{},[677,82695,82698],{"href":82696,"rel":82697},"https://www.danvega.dev/blog/2023/03/15/spring-security-lambda-dsl/",[681],"Spring Security Configuration - Lambda DSL",[37,82700,82701],{},[677,82702,82705],{"href":82703,"rel":82704},"https://www.danvega.dev/blog/2023/03/02/spring-shell-intro/",[681],"Building Command Line Applications with Spring",[37,82707,82708],{},[677,82709,82712],{"href":82710,"rel":82711},"https://www.danvega.dev/blog/2023/02/03/native-images-graalvm/",[681],"Building Native Images with GraalVM",[636,82714,82716],{"id":82715},"youtube-generator","YouTube Generator",[22,82718,82719,82720,82725],{},"They also have a free service for ",[677,82721,82724],{"href":82722,"rel":82723},"https://videotapit.com/youtube-chapters-generator?ref=dan",[681],"generating YouTube Chapters"," which I have found really useful. This is another one of those tasks during the video creation process that I find exhausting. You just spent all this time producing, editing and publishing the video and instead of moving on to the next one you need to watch it again to figure out where the chapter marks are.",[22,82727,82728],{},[653,82729],{"alt":82730,"src":82731},"YouTube Chapter Generator from VideoTap","/images/blog/2023/03/31/youtube-chapter-generator.png",[26,82733,1499],{"id":1498},[22,82735,82736,82737,82740,82741,82744],{},"I think at this point I should be transparent and tell you that I am an affiliate for ",[677,82738,82575],{"href":82573,"rel":82739},[681],". if you know me though you know I don’t push products or services. I have almost 30k YouTube subscribers and have yet to take a single brand deal. I’m an affiliate for VideoTap because it is one of those services I absolutely love and believe in. If you wouldn’t mind using my ",[677,82742,33106],{"href":82573,"rel":82743},[681]," it will cost you nothing extra and it helps support my content creation.",[22,82746,82747,82749,82545],{},[646,82748,36004],{},[36006,82750],{},{"title":57,"searchDepth":76,"depth":76,"links":82752},[82753,82756,82762],{"id":82579,"depth":76,"text":82580,"children":82754},[82755],{"id":82600,"depth":82,"text":82601},{"id":82616,"depth":76,"text":82617,"children":82757},[82758,82759,82761],{"id":82626,"depth":82,"text":82627},{"id":82673,"depth":82,"text":82760},"Show me the Money Blog Posts",{"id":82715,"depth":82,"text":82716},{"id":1498,"depth":76,"text":1499},{"_id":82764,"path":82765,"title":82766,"description":82767,"meta":82768,"body":82773},"content/blog/2023/03/20/graphql-mutations.md","/blog/2023/03/20/graphql-mutations","Working with GraphQL Mutations in Spring Boot","In this blog post, we'll be taking a look at mutations in GraphQL, specifically using Spring Boot, GraphQL Java, and Spring Data JPA.",{"slug":82769,"date":82770,"published":13,"tags":82771,"author":17,"cover":82772,"excerpt":-1},"graphql-mutations","2023-03-20T11:00:00.000Z",[8507,2925],"./graphql-mutations.png",{"type":19,"value":82774,"toc":84307},[82775,82778,82782,82789,82802,82808,82812,82825,82858,82983,83000,83069,83088,83116,83120,83123,83128,83166,83184,83303,83312,83317,83320,83324,83346,83429,83447,83572,83584,83651,83655,83658,83662,83686,83753,83763,83782,83789,83828,83832,83844,83860,83869,83895,83915,83981,83990,84012,84015,84054,84058,84073,84184,84192,84218,84221,84299,84301,84304],[22,82776,82777],{},"In this blog post, we'll be taking a look at mutations in GraphQL, specifically using Spring Boot, GraphQL Java, and Spring Data JPA. Up until now, we've mostly been focused on getting started with GraphQL and simple operations like queries. However, there's a lot more to GraphQL, like mutations, subscriptions, and new features in recent releases. Let's dive in and create a new project to explore these concepts!",[26,82779,82781],{"id":82780},"create-a-spring-boot-project-with-graphql","Create a Spring Boot Project with GraphQL",[22,82783,82784,82785,82788],{},"To get started, we'll need a new Java Maven project. Using ",[677,82786,24606],{"href":2901,"rel":82787},[681],", create a project with the following configuration:",[915,82790,82791,82794,82796,82799],{},[37,82792,82793],{},"Project type: Maven",[37,82795,23077],{},[37,82797,82798],{},"Java: 17",[37,82800,82801],{},"Dependencies: Web, Spring Data JPA, H2, and Spring Boot GraphQL",[22,82803,82804,82807],{},[653,82805],{"alt":78726,"src":82806},"/images/blog/2023/03/20/spring-init.png","Download the generated project and open it in your favorite IDE. We're going to create a simple application that simulates a library system with books and reviews.",[636,82809,82811],{"id":82810},"set-up-entities-and-repositories","Set Up Entities and Repositories",[22,82813,82814,82815,19931,82817,82820,82821,82824],{},"First, let's create some model classes for our application. We'll use Spring Data JPA to define ",[59,82816,8545],{},[59,82818,82819],{},"Review"," entities, along with a ",[59,82822,82823],{},"BookRepository"," interface for database operations:",[34,82826,82827],{},[37,82828,15404,82829,79450,82831,82833,82834,82836,82837,976,82839,976,82841,976,82843,4201,82845,82848,82849,19931,82851,82854,82855,82857],{},[59,82830,8545],{},[59,82832,16671],{}," package and annotate it with ",[59,82835,61725],{},". Add fields for ",[59,82838,6283],{},[59,82840,3196],{},[59,82842,24905],{},[59,82844,8585],{},[59,82846,82847],{},"reviews",". Use appropriate JPA annotations, such as ",[59,82850,80421],{},[59,82852,82853],{},"@GeneratedValue"," for the ",[59,82856,6283],{}," field.",[52,82859,82861],{"className":54,"code":82860,"language":56,"meta":57,"style":57},"@Entity\npublic class Book {\n\n @Id\n @GeneratedValue\n private Integer id;\n private String title;\n private Integer pages;\n private String author;\n\n @OneToMany(cascade = CascadeType.ALL)\n @JoinColumn(name = \"book_id\")\n private List\u003CReview> reviews;\n\n // Constructors, getters, and setters\n}\n",[59,82862,82863,82869,82879,82883,82889,82895,82901,82907,82914,82920,82924,82941,82959,82970,82974,82979],{"__ignoreMap":57},[62,82864,82865,82867],{"class":64,"line":65},[62,82866,942],{"class":72},[62,82868,8999],{"class":68},[62,82870,82871,82873,82875,82877],{"class":64,"line":76},[62,82872,116],{"class":68},[62,82874,119],{"class":68},[62,82876,8833],{"class":122},[62,82878,126],{"class":72},[62,82880,82881],{"class":64,"line":82},[62,82882,79],{"emptyLinePlaceholder":13},[62,82884,82885,82887],{"class":64,"line":89},[62,82886,2143],{"class":72},[62,82888,22298],{"class":68},[62,82890,82891,82893],{"class":64,"line":95},[62,82892,2143],{"class":72},[62,82894,9022],{"class":68},[62,82896,82897,82899],{"class":64,"line":101},[62,82898,137],{"class":68},[62,82900,45372],{"class":72},[62,82902,82903,82905],{"class":64,"line":107},[62,82904,137],{"class":68},[62,82906,9036],{"class":72},[62,82908,82909,82911],{"class":64,"line":113},[62,82910,137],{"class":68},[62,82912,82913],{"class":72}," Integer pages;\n",[62,82915,82916,82918],{"class":64,"line":129},[62,82917,137],{"class":68},[62,82919,9043],{"class":72},[62,82921,82922],{"class":64,"line":134},[62,82923,79],{"emptyLinePlaceholder":13},[62,82925,82926,82928,82931,82933,82936,82938],{"class":64,"line":156},[62,82927,2143],{"class":72},[62,82929,82930],{"class":68},"OneToMany",[62,82932,2109],{"class":72},[62,82934,82935],{"class":149},"cascade",[62,82937,2556],{"class":68},[62,82939,82940],{"class":72}," CascadeType.ALL)\n",[62,82942,82943,82945,82948,82950,82952,82954,82957],{"class":64,"line":161},[62,82944,2143],{"class":72},[62,82946,82947],{"class":68},"JoinColumn",[62,82949,2109],{"class":72},[62,82951,3107],{"class":149},[62,82953,2556],{"class":68},[62,82955,82956],{"class":1675}," \"book_id\"",[62,82958,2212],{"class":72},[62,82960,82961,82963,82965,82967],{"class":64,"line":167},[62,82962,137],{"class":68},[62,82964,3079],{"class":72},[62,82966,82819],{"class":68},[62,82968,82969],{"class":72},"> reviews;\n",[62,82971,82972],{"class":64,"line":173},[62,82973,79],{"emptyLinePlaceholder":13},[62,82975,82976],{"class":64,"line":179},[62,82977,82978],{"class":85}," // Constructors, getters, and setters\n",[62,82980,82981],{"class":64,"line":185},[62,82982,379],{"class":72},[34,82984,82985],{"start":76},[37,82986,15404,82987,79450,82989,82833,82991,82836,82993,976,82995,4201,82997,2755],{},[59,82988,82819],{},[59,82990,16671],{},[59,82992,61725],{},[59,82994,6283],{},[59,82996,3196],{},[59,82998,82999],{},"comment",[52,83001,83003],{"className":54,"code":83002,"language":56,"meta":57,"style":57},"@Entity\npublic class Review {\n\n @Id\n @GeneratedValue\n private Integer id;\n private String title;\n private String comment;\n\n // Constructors, getters, and setters\n}\n",[59,83004,83005,83011,83022,83026,83032,83038,83044,83050,83057,83061,83065],{"__ignoreMap":57},[62,83006,83007,83009],{"class":64,"line":65},[62,83008,942],{"class":72},[62,83010,8999],{"class":68},[62,83012,83013,83015,83017,83020],{"class":64,"line":76},[62,83014,116],{"class":68},[62,83016,119],{"class":68},[62,83018,83019],{"class":122}," Review",[62,83021,126],{"class":72},[62,83023,83024],{"class":64,"line":82},[62,83025,79],{"emptyLinePlaceholder":13},[62,83027,83028,83030],{"class":64,"line":89},[62,83029,2143],{"class":72},[62,83031,22298],{"class":68},[62,83033,83034,83036],{"class":64,"line":95},[62,83035,2143],{"class":72},[62,83037,9022],{"class":68},[62,83039,83040,83042],{"class":64,"line":101},[62,83041,137],{"class":68},[62,83043,45372],{"class":72},[62,83045,83046,83048],{"class":64,"line":107},[62,83047,137],{"class":68},[62,83049,9036],{"class":72},[62,83051,83052,83054],{"class":64,"line":113},[62,83053,137],{"class":68},[62,83055,83056],{"class":72}," String comment;\n",[62,83058,83059],{"class":64,"line":129},[62,83060,79],{"emptyLinePlaceholder":13},[62,83062,83063],{"class":64,"line":134},[62,83064,82978],{"class":85},[62,83066,83067],{"class":64,"line":156},[62,83068,379],{"class":72},[34,83070,83071],{"start":82},[37,83072,15404,83073,81701,83075,83077,83078,83081,83082,83084,83085,83087],{},[59,83074,82823],{},[59,83076,23540],{}," package, extending ",[59,83079,83080],{},"JpaRepository"," and specifying ",[59,83083,8545],{}," as the entity type and ",[59,83086,979],{}," as the ID type:",[52,83089,83091],{"className":54,"code":83090,"language":56,"meta":57,"style":57},"public interface BookRepository extends JpaRepository\u003CBook, Integer> {}\n",[59,83092,83093],{"__ignoreMap":57},[62,83094,83095,83097,83099,83101,83103,83105,83107,83109,83111,83113],{"class":64,"line":65},[62,83096,116],{"class":68},[62,83098,8531],{"class":68},[62,83100,8534],{"class":122},[62,83102,8537],{"class":68},[62,83104,8540],{"class":122},[62,83106,760],{"class":72},[62,83108,8545],{"class":68},[62,83110,976],{"class":72},[62,83112,979],{"class":68},[62,83114,83115],{"class":72},"> {}\n",[636,83117,83119],{"id":83118},"configure-application-properties-and-sample-data","Configure Application Properties and Sample Data",[22,83121,83122],{},"Next, let's configure our application properties to specify some settings for the H2 database connection, enable the H2 console, and show SQL statements.",[22,83124,83125,83126,2004],{},"Add the following properties to the ",[59,83127,1265],{},[52,83129,83131],{"className":1269,"code":83130,"language":1271,"meta":57,"style":57},"spring.datasource.name=books\nspring.datasource.generate-unique-name=false\nspring.h2.console.enabled=true\nspring.jpa.show-sql=true\nspring.graphql.servlet.enabled=true\n",[59,83132,83133,83140,83146,83152,83159],{"__ignoreMap":57},[62,83134,83135,83137],{"class":64,"line":65},[62,83136,41985],{"class":68},[62,83138,83139],{"class":72},"=books\n",[62,83141,83142,83144],{"class":64,"line":76},[62,83143,41978],{"class":68},[62,83145,14152],{"class":72},[62,83147,83148,83150],{"class":64,"line":82},[62,83149,41993],{"class":68},[62,83151,1281],{"class":72},[62,83153,83154,83157],{"class":64,"line":89},[62,83155,83156],{"class":68},"spring.jpa.show-sql",[62,83158,1281],{"class":72},[62,83160,83161,83164],{"class":64,"line":95},[62,83162,83163],{"class":68},"spring.graphql.servlet.enabled",[62,83165,1281],{"class":72},[22,83167,83168,83169,83172,83173,83175,83176,83178,83179,83181,83182,2755],{},"We'll also need some sample data for our application. In the ",[59,83170,83171],{},"Application.java"," class, create a ",[59,83174,2066],{}," bean that initializes a ",[59,83177,8545],{}," and a ",[59,83180,82819],{}," and saves them to the database using the ",[59,83183,82823],{},[52,83185,83187],{"className":54,"code":83186,"language":56,"meta":57,"style":57},"@Bean\npublic CommandLineRunner initData(BookRepository bookRepository) {\n return args -> {\n Book book = new Book(\"Reactive Spring\", 484, \"Josh Long\");\n Review review = new Review(\"Great book!\", \"I really enjoyed this book!\");\n book.setReviews(Collections.singletonList(review));\n bookRepository.save(book);\n };\n}\n",[59,83188,83189,83195,83207,83217,83245,83268,83285,83295,83299],{"__ignoreMap":57},[62,83190,83191,83193],{"class":64,"line":65},[62,83192,942],{"class":72},[62,83194,2146],{"class":68},[62,83196,83197,83199,83201,83204],{"class":64,"line":76},[62,83198,116],{"class":68},[62,83200,62380],{"class":72},[62,83202,83203],{"class":122},"initData",[62,83205,83206],{"class":72},"(BookRepository bookRepository) {\n",[62,83208,83209,83211,83213,83215],{"class":64,"line":82},[62,83210,2599],{"class":68},[62,83212,2169],{"class":72},[62,83214,800],{"class":68},[62,83216,126],{"class":72},[62,83218,83219,83222,83224,83226,83228,83230,83233,83235,83238,83240,83243],{"class":64,"line":89},[62,83220,83221],{"class":72}," Book book ",[62,83223,146],{"class":68},[62,83225,466],{"class":68},[62,83227,8833],{"class":122},[62,83229,2109],{"class":72},[62,83231,83232],{"class":1675},"\"Reactive Spring\"",[62,83234,976],{"class":72},[62,83236,83237],{"class":149},"484",[62,83239,976],{"class":72},[62,83241,83242],{"class":1675},"\"Josh Long\"",[62,83244,1133],{"class":72},[62,83246,83247,83250,83252,83254,83256,83258,83261,83263,83266],{"class":64,"line":95},[62,83248,83249],{"class":72}," Review review ",[62,83251,146],{"class":68},[62,83253,466],{"class":68},[62,83255,83019],{"class":122},[62,83257,2109],{"class":72},[62,83259,83260],{"class":1675},"\"Great book!\"",[62,83262,976],{"class":72},[62,83264,83265],{"class":1675},"\"I really enjoyed this book!\"",[62,83267,1133],{"class":72},[62,83269,83270,83273,83276,83279,83282],{"class":64,"line":101},[62,83271,83272],{"class":72}," book.",[62,83274,83275],{"class":122},"setReviews",[62,83277,83278],{"class":72},"(Collections.",[62,83280,83281],{"class":122},"singletonList",[62,83283,83284],{"class":72},"(review));\n",[62,83286,83287,83290,83292],{"class":64,"line":107},[62,83288,83289],{"class":72}," bookRepository.",[62,83291,22562],{"class":122},[62,83293,83294],{"class":72},"(book);\n",[62,83296,83297],{"class":64,"line":113},[62,83298,40087],{"class":72},[62,83300,83301],{"class":64,"line":129},[62,83302,379],{"class":72},[22,83304,83305,83306,83311],{},"This is a great opportunity to tell you that you should pick up Josh Long's book, ",[677,83307,83310],{"href":83308,"rel":83309},"https://amzn.to/3K9d7fw",[681],"Reactive Spring",". It's a great read!",[22,83313,83314],{},[653,83315],{"alt":83310,"src":83316},"/images/blog/2023/03/20/reactive-spring-book.jpeg",[22,83318,83319],{},"At this point, you should be able to run your application, and you'll see that the book and review are saved to the database.",[636,83321,83323],{"id":83322},"create-a-graphql-query","Create a GraphQL Query",[22,83325,83326,83327,83329,83330,83332,83333,83335,83336,83338,83339,83341,83342,83345],{},"Now that we have our basic data model set up, let's create a simple GraphQL query to fetch our books. In the ",[59,83328,4097],{}," folder, create a ",[59,83331,8824],{}," directory and a file called ",[59,83334,46276],{},". In this file, define a ",[59,83337,8545],{}," type, a ",[59,83340,82819],{}," type, and a ",[59,83343,83344],{},"Query"," type.",[52,83347,83349],{"className":8822,"code":83348,"language":8824,"meta":57,"style":57},"type Book {\n id: ID\n title: String\n pages: Int\n author: String\n reviews: [Review]\n}\n\ntype Review {\n id: ID\n title: String\n comment: String\n}\n\ntype Query {\n findAllBooks: [Book]\n}\n",[59,83350,83351,83356,83361,83366,83371,83376,83381,83385,83389,83394,83398,83402,83407,83411,83415,83420,83425],{"__ignoreMap":57},[62,83352,83353],{"class":64,"line":65},[62,83354,83355],{},"type Book {\n",[62,83357,83358],{"class":64,"line":76},[62,83359,83360],{}," id: ID\n",[62,83362,83363],{"class":64,"line":82},[62,83364,83365],{}," title: String\n",[62,83367,83368],{"class":64,"line":89},[62,83369,83370],{}," pages: Int\n",[62,83372,83373],{"class":64,"line":95},[62,83374,83375],{}," author: String\n",[62,83377,83378],{"class":64,"line":101},[62,83379,83380],{}," reviews: [Review]\n",[62,83382,83383],{"class":64,"line":107},[62,83384,379],{},[62,83386,83387],{"class":64,"line":113},[62,83388,79],{"emptyLinePlaceholder":13},[62,83390,83391],{"class":64,"line":129},[62,83392,83393],{},"type Review {\n",[62,83395,83396],{"class":64,"line":134},[62,83397,83360],{},[62,83399,83400],{"class":64,"line":156},[62,83401,83365],{},[62,83403,83404],{"class":64,"line":161},[62,83405,83406],{}," comment: String\n",[62,83408,83409],{"class":64,"line":167},[62,83410,379],{},[62,83412,83413],{"class":64,"line":173},[62,83414,79],{"emptyLinePlaceholder":13},[62,83416,83417],{"class":64,"line":179},[62,83418,83419],{},"type Query {\n",[62,83421,83422],{"class":64,"line":185},[62,83423,83424],{}," findAllBooks: [Book]\n",[62,83426,83427],{"class":64,"line":191},[62,83428,379],{},[22,83430,83431,83432,83435,83436,83438,83439,83442,83443,83446],{},"In your application create a REST controller named ",[59,83433,83434],{},"BookController"," with a method that returns all books from the ",[59,83437,82823],{},". Use the ",[59,83440,83441],{},"@QueryMapping"," annotation to link the method to the ",[59,83444,83445],{},"findAllBooks"," query in your GraphQL schema.",[52,83448,83450],{"className":54,"code":83449,"language":56,"meta":57,"style":57},"@RestController\n@RequestMapping(\"/api/books\")\npublic class BookController {\n\n private final BookRepository bookRepository;\n\n public BookController(BookRepository bookRepository) {\n this.bookRepository = bookRepository;\n }\n\n @QueryMapping\n public List\u003CBook> findAllBooks() {\n return bookRepository.findAll();\n }\n}\n",[59,83451,83452,83458,83471,83481,83485,83494,83498,83512,83524,83528,83532,83539,83553,83564,83568],{"__ignoreMap":57},[62,83453,83454,83456],{"class":64,"line":65},[62,83455,942],{"class":72},[62,83457,2342],{"class":68},[62,83459,83460,83462,83464,83466,83469],{"class":64,"line":76},[62,83461,942],{"class":72},[62,83463,10592],{"class":68},[62,83465,2109],{"class":72},[62,83467,83468],{"class":1675},"\"/api/books\"",[62,83470,2212],{"class":72},[62,83472,83473,83475,83477,83479],{"class":64,"line":82},[62,83474,116],{"class":68},[62,83476,119],{"class":68},[62,83478,18573],{"class":122},[62,83480,126],{"class":72},[62,83482,83483],{"class":64,"line":89},[62,83484,79],{"emptyLinePlaceholder":13},[62,83486,83487,83489,83491],{"class":64,"line":95},[62,83488,137],{"class":68},[62,83490,458],{"class":68},[62,83492,83493],{"class":72}," BookRepository bookRepository;\n",[62,83495,83496],{"class":64,"line":101},[62,83497,79],{"emptyLinePlaceholder":13},[62,83499,83500,83502,83504,83507,83510],{"class":64,"line":107},[62,83501,194],{"class":68},[62,83503,18573],{"class":122},[62,83505,83506],{"class":72},"(BookRepository ",[62,83508,83509],{"class":889},"bookRepository",[62,83511,768],{"class":72},[62,83513,83514,83516,83519,83521],{"class":64,"line":113},[62,83515,2405],{"class":149},[62,83517,83518],{"class":72},".bookRepository ",[62,83520,146],{"class":68},[62,83522,83523],{"class":72}," bookRepository;\n",[62,83525,83526],{"class":64,"line":129},[62,83527,223],{"class":72},[62,83529,83530],{"class":64,"line":134},[62,83531,79],{"emptyLinePlaceholder":13},[62,83533,83534,83536],{"class":64,"line":156},[62,83535,2143],{"class":72},[62,83537,83538],{"class":68},"QueryMapping\n",[62,83540,83541,83543,83545,83547,83549,83551],{"class":64,"line":161},[62,83542,194],{"class":68},[62,83544,3079],{"class":72},[62,83546,8545],{"class":68},[62,83548,3135],{"class":72},[62,83550,83445],{"class":122},[62,83552,206],{"class":72},[62,83554,83555,83557,83560,83562],{"class":64,"line":167},[62,83556,360],{"class":68},[62,83558,83559],{"class":72}," bookRepository.",[62,83561,10287],{"class":122},[62,83563,822],{"class":72},[62,83565,83566],{"class":64,"line":173},[62,83567,223],{"class":72},[62,83569,83570],{"class":64,"line":179},[62,83571,379],{"class":72},[22,83573,83574,83575,83580,83581,83583],{},"At this point, you should be able to run your application and use the ",[677,83576,83579],{"href":83577,"rel":83578},"http://localhost:8080/graphiql",[681],"GraphiQL Playground"," to test your ",[59,83582,83445],{}," query.",[52,83585,83587],{"className":8822,"code":83586,"language":8824,"meta":57,"style":57},"query {\n findAllBooks {\n id\n title\n pages\n author\n reviews {\n id\n title\n comment\n }\n }\n}\n",[59,83588,83589,83594,83599,83604,83609,83614,83619,83624,83629,83634,83639,83643,83647],{"__ignoreMap":57},[62,83590,83591],{"class":64,"line":65},[62,83592,83593],{},"query {\n",[62,83595,83596],{"class":64,"line":76},[62,83597,83598],{}," findAllBooks {\n",[62,83600,83601],{"class":64,"line":82},[62,83602,83603],{}," id\n",[62,83605,83606],{"class":64,"line":89},[62,83607,83608],{}," title\n",[62,83610,83611],{"class":64,"line":95},[62,83612,83613],{}," pages\n",[62,83615,83616],{"class":64,"line":101},[62,83617,83618],{}," author\n",[62,83620,83621],{"class":64,"line":107},[62,83622,83623],{}," reviews {\n",[62,83625,83626],{"class":64,"line":113},[62,83627,83628],{}," id\n",[62,83630,83631],{"class":64,"line":129},[62,83632,83633],{}," title\n",[62,83635,83636],{"class":64,"line":134},[62,83637,83638],{}," comment\n",[62,83640,83641],{"class":64,"line":156},[62,83642,223],{},[62,83644,83645],{"class":64,"line":161},[62,83646,3731],{},[62,83648,83649],{"class":64,"line":167},[62,83650,379],{},[26,83652,83654],{"id":83653},"working-with-graphql-mutations","Working with GraphQL Mutations",[22,83656,83657],{},"Now let's explore different ways we can work with mutations in our application. A mutation is essentially an operation that makes changes to our data, such as creating, updating, or deleting records.",[636,83659,83661],{"id":83660},"simple-mutation-with-basic-types","Simple Mutation with Basic Types",[22,83663,83664,83665,83668,83669,976,83671,4201,83673,83675,83676,83438,83678,83681,83682,83685],{},"First, let's create a simple mutation that takes in basic types like strings and integers as inputs. In your controller, create a new method called ",[59,83666,83667],{},"createBook"," that accepts three parameters (",[59,83670,3196],{},[59,83672,24905],{},[59,83674,8585],{},"), and returns a ",[59,83677,8545],{},[59,83679,83680],{},"@MutationMapping"," and annotation to associate the method with a mutation field in your GraphQL schema. The ",[59,83683,83684],{},"@Argument"," annotation maps the GraphQL named arguments to your method parameters.",[52,83687,83689],{"className":54,"code":83688,"language":56,"meta":57,"style":57},"@MutationMapping\npublic Book createBook(@Argument String title, @Argument Integer pages, @Argument String author) {\n Book book = new Book(title, pages, author);\n return bookRepository.save(book);\n}\n",[59,83690,83691,83698,83725,83739,83749],{"__ignoreMap":57},[62,83692,83693,83695],{"class":64,"line":65},[62,83694,942],{"class":72},[62,83696,83697],{"class":68},"MutationMapping\n",[62,83699,83700,83702,83705,83707,83709,83712,83715,83717,83720,83722],{"class":64,"line":76},[62,83701,116],{"class":68},[62,83703,83704],{"class":72}," Book ",[62,83706,83667],{"class":122},[62,83708,2475],{"class":72},[62,83710,83711],{"class":68},"Argument",[62,83713,83714],{"class":72}," String title, @",[62,83716,83711],{"class":68},[62,83718,83719],{"class":72}," Integer pages, @",[62,83721,83711],{"class":68},[62,83723,83724],{"class":72}," String author) {\n",[62,83726,83727,83730,83732,83734,83736],{"class":64,"line":82},[62,83728,83729],{"class":72}," Book book ",[62,83731,146],{"class":68},[62,83733,466],{"class":68},[62,83735,8833],{"class":122},[62,83737,83738],{"class":72},"(title, pages, author);\n",[62,83740,83741,83743,83745,83747],{"class":64,"line":89},[62,83742,2599],{"class":68},[62,83744,83559],{"class":72},[62,83746,22562],{"class":122},[62,83748,83294],{"class":72},[62,83750,83751],{"class":64,"line":95},[62,83752,379],{"class":72},[22,83754,83755,83756,83759,83760,83762],{},"Next, update your GraphQL schema to define the ",[59,83757,83758],{},"Mutation"," type with a ",[59,83761,83667],{}," field:",[52,83764,83766],{"className":8822,"code":83765,"language":8824,"meta":57,"style":57},"type Mutation {\n createBook(title: String, pages: Int, author: String): Book\n}\n",[59,83767,83768,83773,83778],{"__ignoreMap":57},[62,83769,83770],{"class":64,"line":65},[62,83771,83772],{},"type Mutation {\n",[62,83774,83775],{"class":64,"line":76},[62,83776,83777],{}," createBook(title: String, pages: Int, author: String): Book\n",[62,83779,83780],{"class":64,"line":82},[62,83781,379],{},[22,83783,83784,83785,83788],{},"With this setup, you can now use a tool like ",[677,83786,83579],{"href":83577,"rel":83787},[681]," to run a mutation that creates a new book.",[52,83790,83792],{"className":8822,"code":83791,"language":8824,"meta":57,"style":57},"mutation CreateBook {\n createBook(title: \"My new book\", pages: 99, author: \"Dan Vega\") {\n id\n title\n pages\n author\n }\n}\n",[59,83793,83794,83799,83804,83808,83812,83816,83820,83824],{"__ignoreMap":57},[62,83795,83796],{"class":64,"line":65},[62,83797,83798],{},"mutation CreateBook {\n",[62,83800,83801],{"class":64,"line":76},[62,83802,83803],{}," createBook(title: \"My new book\", pages: 99, author: \"Dan Vega\") {\n",[62,83805,83806],{"class":64,"line":82},[62,83807,83603],{},[62,83809,83810],{"class":64,"line":89},[62,83811,83608],{},[62,83813,83814],{"class":64,"line":95},[62,83815,83613],{},[62,83817,83818],{"class":64,"line":101},[62,83819,83618],{},[62,83821,83822],{"class":64,"line":107},[62,83823,3731],{},[62,83825,83826],{"class":64,"line":113},[62,83827,379],{},[636,83829,83831],{"id":83830},"mutation-with-object-input-type","Mutation with Object Input Type",[22,83833,83834,83835,83837,83838,83840,83841,83843],{},"In more complex scenarios, you might need to accept an entire object as input for a mutation. To do this, create a new ",[59,83836,8951],{}," record in your ",[59,83839,16671],{}," package, which essentially mirrors the properties of your ",[59,83842,8545],{}," entity:",[52,83845,83847],{"className":54,"code":83846,"language":56,"meta":57,"style":57},"public record BookInput(String title, Integer pages, String author) {}\n",[59,83848,83849],{"__ignoreMap":57},[62,83850,83851,83853,83855,83857],{"class":64,"line":65},[62,83852,116],{"class":68},[62,83854,2996],{"class":68},[62,83856,8893],{"class":122},[62,83858,83859],{"class":72},"(String title, Integer pages, String author) {}\n",[22,83861,83862,83863,83865,83866,83868],{},"Define an ",[59,83864,8890],{}," type in your GraphQL schema that corresponds to your ",[59,83867,8951],{}," record:",[52,83870,83872],{"className":8822,"code":83871,"language":8824,"meta":57,"style":57},"input BookInput {\n title: String\n pages: Int\n author: String\n}\n",[59,83873,83874,83879,83883,83887,83891],{"__ignoreMap":57},[62,83875,83876],{"class":64,"line":65},[62,83877,83878],{},"input BookInput {\n",[62,83880,83881],{"class":64,"line":76},[62,83882,83365],{},[62,83884,83885],{"class":64,"line":82},[62,83886,83370],{},[62,83888,83889],{"class":64,"line":89},[62,83890,83375],{},[62,83892,83893],{"class":64,"line":95},[62,83894,379],{},[22,83896,83897,83898,46277,83901,83903,83904,83906,83907,83909,83910,19931,83912,83914],{},"Create a new method called ",[59,83899,83900],{},"addBook",[59,83902,83434],{}," that accepts a ",[59,83905,8951],{}," argument and returns a ",[59,83908,8545],{},". As before, use the ",[59,83911,83680],{},[59,83913,83684],{}," annotations:",[52,83916,83918],{"className":54,"code":83917,"language":56,"meta":57,"style":57},"@MutationMapping\npublic Book addBook(@Argument BookInput bookInput) {\n Book book = new Book(bookInput.title(), bookInput.pages(), bookInput.author());\n return bookRepository.save(book);\n}\n\n",[59,83919,83920,83926,83941,83967,83977],{"__ignoreMap":57},[62,83921,83922,83924],{"class":64,"line":65},[62,83923,942],{"class":72},[62,83925,83697],{"class":68},[62,83927,83928,83930,83932,83934,83936,83938],{"class":64,"line":76},[62,83929,116],{"class":68},[62,83931,83704],{"class":72},[62,83933,83900],{"class":122},[62,83935,2475],{"class":72},[62,83937,83711],{"class":68},[62,83939,83940],{"class":72}," BookInput bookInput) {\n",[62,83942,83943,83945,83947,83949,83951,83954,83956,83959,83961,83963,83965],{"class":64,"line":82},[62,83944,83729],{"class":72},[62,83946,146],{"class":68},[62,83948,466],{"class":68},[62,83950,8833],{"class":122},[62,83952,83953],{"class":72},"(bookInput.",[62,83955,3196],{"class":122},[62,83957,83958],{"class":72},"(), bookInput.",[62,83960,24905],{"class":122},[62,83962,83958],{"class":72},[62,83964,8585],{"class":122},[62,83966,1091],{"class":72},[62,83968,83969,83971,83973,83975],{"class":64,"line":89},[62,83970,2599],{"class":68},[62,83972,83559],{"class":72},[62,83974,22562],{"class":122},[62,83976,83294],{"class":72},[62,83978,83979],{"class":64,"line":95},[62,83980,379],{"class":72},[22,83982,83983,83984,83986,83987,83989],{},"Update your GraphQL schema to add an ",[59,83985,83900],{}," field to the ",[59,83988,83758],{}," type:",[52,83991,83993],{"className":8822,"code":83992,"language":8824,"meta":57,"style":57},"type Mutation {\n createBook(title: String, pages: Int, author: String): Book\n addBook(book: BookInput!): Book\n}\n",[59,83994,83995,83999,84003,84008],{"__ignoreMap":57},[62,83996,83997],{"class":64,"line":65},[62,83998,83772],{},[62,84000,84001],{"class":64,"line":76},[62,84002,83777],{},[62,84004,84005],{"class":64,"line":82},[62,84006,84007],{}," addBook(book: BookInput!): Book\n",[62,84009,84010],{"class":64,"line":89},[62,84011,379],{},[22,84013,84014],{},"Now you can run a mutation that creates a new book using an object input rather than individual strings and integers.",[52,84016,84018],{"className":8822,"code":84017,"language":8824,"meta":57,"style":57},"mutation AddBook {\n addBook(book: { title: \"My New Book\", pages: 199, author: \"Dan Vega\" }) {\n id\n title\n pages\n author\n }\n}\n",[59,84019,84020,84025,84030,84034,84038,84042,84046,84050],{"__ignoreMap":57},[62,84021,84022],{"class":64,"line":65},[62,84023,84024],{},"mutation AddBook {\n",[62,84026,84027],{"class":64,"line":76},[62,84028,84029],{}," addBook(book: { title: \"My New Book\", pages: 199, author: \"Dan Vega\" }) {\n",[62,84031,84032],{"class":64,"line":82},[62,84033,83603],{},[62,84035,84036],{"class":64,"line":89},[62,84037,83608],{},[62,84039,84040],{"class":64,"line":95},[62,84041,83613],{},[62,84043,84044],{"class":64,"line":101},[62,84045,83618],{},[62,84047,84048],{"class":64,"line":107},[62,84049,3731],{},[62,84051,84052],{"class":64,"line":113},[62,84053,379],{},[636,84055,84057],{"id":84056},"mutation-with-list-input-type","Mutation with List Input Type",[22,84059,84060,84061,78584,84064,84066,84067,84069,84070,84072],{},"Finally, let's create a mutation that accepts a list of objects as input. Add a new method called ",[59,84062,84063],{},"batchCreate",[59,84065,83434],{}," that takes a list of ",[59,84068,8951],{}," objects and maps each input object to a ",[59,84071,8545],{}," entity, and saves the books in the database.",[52,84074,84076],{"className":54,"code":84075,"language":56,"meta":57,"style":57},"@MutationMapping\npublic List\u003CBook> batchCreate(@Argument List\u003CBookInput> books) {\n return books.stream()\n .map(bookInput -> new Book(bookInput.title(), bookInput.pages(), bookInput.author()))\n .map(bookRepository::save)\n .collect(Collectors.toList());\n}\n",[59,84077,84078,84084,84114,84125,84154,84168,84180],{"__ignoreMap":57},[62,84079,84080,84082],{"class":64,"line":65},[62,84081,942],{"class":72},[62,84083,83697],{"class":68},[62,84085,84086,84088,84090,84092,84094,84096,84099,84101,84103,84105,84107,84109,84111],{"class":64,"line":76},[62,84087,116],{"class":68},[62,84089,3835],{"class":72},[62,84091,760],{"class":68},[62,84093,8545],{"class":72},[62,84095,2583],{"class":68},[62,84097,84098],{"class":122}," batchCreate",[62,84100,2475],{"class":72},[62,84102,83711],{"class":68},[62,84104,3835],{"class":72},[62,84106,760],{"class":68},[62,84108,8951],{"class":72},[62,84110,2583],{"class":68},[62,84112,84113],{"class":72}," books) {\n",[62,84115,84116,84118,84121,84123],{"class":64,"line":82},[62,84117,2599],{"class":68},[62,84119,84120],{"class":72}," books.",[62,84122,2621],{"class":122},[62,84124,2223],{"class":72},[62,84126,84127,84129,84131,84134,84136,84138,84140,84142,84144,84146,84148,84150,84152],{"class":64,"line":89},[62,84128,3862],{"class":72},[62,84130,4200],{"class":122},[62,84132,84133],{"class":72},"(bookInput ",[62,84135,800],{"class":68},[62,84137,466],{"class":68},[62,84139,8833],{"class":122},[62,84141,83953],{"class":72},[62,84143,3196],{"class":122},[62,84145,83958],{"class":72},[62,84147,24905],{"class":122},[62,84149,83958],{"class":72},[62,84151,8585],{"class":122},[62,84153,3890],{"class":72},[62,84155,84156,84158,84160,84163,84165],{"class":64,"line":95},[62,84157,3862],{"class":72},[62,84159,4200],{"class":122},[62,84161,84162],{"class":72},"(bookRepository",[62,84164,4451],{"class":68},[62,84166,84167],{"class":72},"save)\n",[62,84169,84170,84172,84174,84176,84178],{"class":64,"line":101},[62,84171,3862],{"class":72},[62,84173,3897],{"class":122},[62,84175,3900],{"class":72},[62,84177,3903],{"class":122},[62,84179,1091],{"class":72},[62,84181,84182],{"class":64,"line":107},[62,84183,379],{"class":72},[22,84185,84186,84187,83986,84189,84191],{},"Update your GraphQL schema to add a ",[59,84188,84063],{},[59,84190,83758],{}," type, specifying that the input list should not be empty:",[52,84193,84195],{"className":8822,"code":84194,"language":8824,"meta":57,"style":57},"type Mutation {\n createBook(title: String, pages: Int, author: String): Book\n addBook(book: BookInput!): Book\n batchCreate(books: [BookInput!]!): [Book]\n}\n",[59,84196,84197,84201,84205,84209,84214],{"__ignoreMap":57},[62,84198,84199],{"class":64,"line":65},[62,84200,83772],{},[62,84202,84203],{"class":64,"line":76},[62,84204,83777],{},[62,84206,84207],{"class":64,"line":82},[62,84208,84007],{},[62,84210,84211],{"class":64,"line":89},[62,84212,84213],{}," batchCreate(books: [BookInput!]!): [Book]\n",[62,84215,84216],{"class":64,"line":95},[62,84217,379],{},[22,84219,84220],{},"Now you can run a mutation that creates multiple books in a single operation by passing in a list of book inputs.",[52,84222,84224],{"className":8822,"code":84223,"language":8824,"meta":57,"style":57},"mutation BatchCreate {\n batchCreate(\n books: [\n { title: \"Batch Book 1\", pages: 99, author: \"New Author\" }\n { title: \"Batch Book 2\", pages: 99, author: \"New Author\" }\n { title: \"Batch Book 3\", pages: 99, author: \"New Author\" }\n { title: \"Batch Book 4\", pages: 99, author: \"New Author\" }\n { title: \"Batch Book 5\", pages: 99, author: \"New Author\" }\n ]\n ) {\n id\n title\n pages\n author\n }\n}\n",[59,84225,84226,84231,84236,84241,84246,84251,84256,84261,84266,84270,84275,84279,84283,84287,84291,84295],{"__ignoreMap":57},[62,84227,84228],{"class":64,"line":65},[62,84229,84230],{},"mutation BatchCreate {\n",[62,84232,84233],{"class":64,"line":76},[62,84234,84235],{}," batchCreate(\n",[62,84237,84238],{"class":64,"line":82},[62,84239,84240],{}," books: [\n",[62,84242,84243],{"class":64,"line":89},[62,84244,84245],{}," { title: \"Batch Book 1\", pages: 99, author: \"New Author\" }\n",[62,84247,84248],{"class":64,"line":95},[62,84249,84250],{}," { title: \"Batch Book 2\", pages: 99, author: \"New Author\" }\n",[62,84252,84253],{"class":64,"line":101},[62,84254,84255],{}," { title: \"Batch Book 3\", pages: 99, author: \"New Author\" }\n",[62,84257,84258],{"class":64,"line":107},[62,84259,84260],{}," { title: \"Batch Book 4\", pages: 99, author: \"New Author\" }\n",[62,84262,84263],{"class":64,"line":113},[62,84264,84265],{}," { title: \"Batch Book 5\", pages: 99, author: \"New Author\" }\n",[62,84267,84268],{"class":64,"line":129},[62,84269,3726],{},[62,84271,84272],{"class":64,"line":134},[62,84273,84274],{}," ) {\n",[62,84276,84277],{"class":64,"line":156},[62,84278,83603],{},[62,84280,84281],{"class":64,"line":161},[62,84282,83608],{},[62,84284,84285],{"class":64,"line":167},[62,84286,83613],{},[62,84288,84289],{"class":64,"line":173},[62,84290,83618],{},[62,84292,84293],{"class":64,"line":179},[62,84294,3731],{},[62,84296,84297],{"class":64,"line":185},[62,84298,379],{},[26,84300,1499],{"id":1498},[22,84302,84303],{},"In this blog post, we explored how to work with mutations in GraphQL, specifically using Spring Boot, GraphQL Java, and Spring Data JPA. We covered different ways of passing input to our mutations, including simple types, object input types, and list input types. With this knowledge in hand, you can create powerful GraphQL APIs that allow clients to make complex updates to your data models. Happy coding!",[1527,84305,84306],{},"html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}html pre.shiki code .s4-Ip, html code.shiki .s4-Ip{--shiki-default:#6A737D;--shiki-dark:#6A737D;--shiki-sepia:#6A737D}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .spoOn, html code.shiki .spoOn{--shiki-default:#E36209;--shiki-dark:#FFAB70;--shiki-sepia:#E36209}",{"title":57,"searchDepth":76,"depth":76,"links":84308},[84309,84314,84319],{"id":82780,"depth":76,"text":82781,"children":84310},[84311,84312,84313],{"id":82810,"depth":82,"text":82811},{"id":83118,"depth":82,"text":83119},{"id":83322,"depth":82,"text":83323},{"id":83653,"depth":76,"text":83654,"children":84315},[84316,84317,84318],{"id":83660,"depth":82,"text":83661},{"id":83830,"depth":82,"text":83831},{"id":84056,"depth":82,"text":84057},{"id":1498,"depth":76,"text":1499},{"_id":84321,"path":84322,"title":82698,"description":73331,"meta":84323,"body":84328},"content/blog/2023/03/15/spring-security-lambda-dsl.md","/blog/2023/03/15/spring-security-lambda-dsl",{"slug":84324,"date":84325,"published":13,"tags":84326,"author":17,"cover":84327,"excerpt":-1},"spring-security-lambda-dsl","2023-03-15T09:30:00.000Z",[10914],"./spring-security-lambda-dsl.png",{"type":19,"value":84329,"toc":85153},[84330,84333,84336,84343,84363,84368,84371,84375,84378,84392,84395,84461,84539,84543,84562,84745,84748,84759,84766,84770,84783,84915,84935,85039,85042,85129,85133,85136,85142,85145,85147,85150],[22,84331,84332],{},"Hello, friends! Today, we'll discuss two different ways to configure Spring Security in your applications and create a basic security configuration in a Spring Boot project using each approach. I will walk you through each method, explaining the pros and cons of each, so that you can better understand the different configurations you might come across in your projects.",[26,84334,84335],{"id":15195},"Setting Up The Project",[22,84337,84338,84339,84342],{},"To get started, we'll create a new Spring Boot project at ",[677,84340,2903],{"href":2901,"rel":84341},[681],", using the following settings:",[915,84344,84345,84348,84350,84352,84354,84357,84360],{},[37,84346,84347],{},"Project Type: Maven Project",[37,84349,18824],{},[37,84351,23077],{},[37,84353,80376],{},[37,84355,84356],{},"Group: dev.danvega",[37,84358,84359],{},"Artifact: hello-security",[37,84361,84362],{},"Dependencies: Web & Security",[22,84364,84365],{},[653,84366],{"alt":78726,"src":84367},"/images/blog/2023/03/15/spring-init.png",[22,84369,84370],{},"Once our project is generated, we'll download the ZIP file and open it in IntelliJ IDEA or whatever IDE you’re most productive in.",[26,84372,84374],{"id":84373},"creating-controllers","Creating Controllers",[22,84376,84377],{},"Next, we'll create two controllers so that we can demonstrate our security configurations:",[34,84379,84380,84386],{},[37,84381,84382,84385],{},[646,84383,84384],{},"HomeController:"," This controller will handle requests to the root (\"/\") endpoint, returning a simple \"Hello, Spring Security\" message.",[37,84387,84388,84391],{},[646,84389,84390],{},"PostController:"," This controller will handle requests to the \"/api/posts\" endpoint, returning a string representing all available posts.",[22,84393,84394],{},"With our controllers in place, we can start exploring the different Spring Security configurations.",[52,84396,84398],{"className":54,"code":84397,"language":56,"meta":57,"style":57},"@RestController\npublic class HomeController {\n\n @GetMapping(\"/\")\n String home() {\n return \"Hello, Spring Security!\";\n }\n\n}\n",[59,84399,84400,84406,84416,84420,84432,84440,84449,84453,84457],{"__ignoreMap":57},[62,84401,84402,84404],{"class":64,"line":65},[62,84403,942],{"class":72},[62,84405,2342],{"class":68},[62,84407,84408,84410,84412,84414],{"class":64,"line":76},[62,84409,116],{"class":68},[62,84411,119],{"class":68},[62,84413,25636],{"class":122},[62,84415,126],{"class":72},[62,84417,84418],{"class":64,"line":82},[62,84419,79],{"emptyLinePlaceholder":13},[62,84421,84422,84424,84426,84428,84430],{"class":64,"line":89},[62,84423,2143],{"class":72},[62,84425,2548],{"class":68},[62,84427,2109],{"class":72},[62,84429,15635],{"class":1675},[62,84431,2212],{"class":72},[62,84433,84434,84436,84438],{"class":64,"line":95},[62,84435,59508],{"class":72},[62,84437,18647],{"class":122},[62,84439,206],{"class":72},[62,84441,84442,84444,84447],{"class":64,"line":101},[62,84443,360],{"class":68},[62,84445,84446],{"class":1675}," \"Hello, Spring Security!\"",[62,84448,153],{"class":72},[62,84450,84451],{"class":64,"line":107},[62,84452,223],{"class":72},[62,84454,84455],{"class":64,"line":113},[62,84456,79],{"emptyLinePlaceholder":13},[62,84458,84459],{"class":64,"line":129},[62,84460,379],{"class":72},[52,84462,84463],{"className":54,"code":55665,"language":56,"meta":57,"style":57},[59,84464,84465,84471,84483,84493,84497,84505,84509,84521,84531,84535],{"__ignoreMap":57},[62,84466,84467,84469],{"class":64,"line":65},[62,84468,942],{"class":72},[62,84470,2342],{"class":68},[62,84472,84473,84475,84477,84479,84481],{"class":64,"line":76},[62,84474,942],{"class":72},[62,84476,10592],{"class":68},[62,84478,2109],{"class":72},[62,84480,41368],{"class":1675},[62,84482,2212],{"class":72},[62,84484,84485,84487,84489,84491],{"class":64,"line":82},[62,84486,116],{"class":68},[62,84488,119],{"class":68},[62,84490,41379],{"class":122},[62,84492,126],{"class":72},[62,84494,84495],{"class":64,"line":89},[62,84496,79],{"emptyLinePlaceholder":13},[62,84498,84499,84501,84503],{"class":64,"line":95},[62,84500,137],{"class":68},[62,84502,458],{"class":68},[62,84504,41394],{"class":72},[62,84506,84507],{"class":64,"line":101},[62,84508,79],{"emptyLinePlaceholder":13},[62,84510,84511,84513,84515,84517,84519],{"class":64,"line":107},[62,84512,194],{"class":68},[62,84514,41379],{"class":122},[62,84516,41407],{"class":72},[62,84518,41410],{"class":889},[62,84520,768],{"class":72},[62,84522,84523,84525,84527,84529],{"class":64,"line":113},[62,84524,2405],{"class":149},[62,84526,41419],{"class":72},[62,84528,146],{"class":68},[62,84530,41424],{"class":72},[62,84532,84533],{"class":64,"line":129},[62,84534,223],{"class":72},[62,84536,84537],{"class":64,"line":134},[62,84538,379],{"class":72},[26,84540,84542],{"id":84541},"chaining-style-configuration","Chaining Style Configuration",[22,84544,84545,84546,84549,84550,84552,84553,84555,84556,84558,84559,84561],{},"First, let's look at the ",[4534,84547,84548],{},"chaining style"," configuration, which is an older approach but still very valid. We'll create a new ",[59,84551,48869],{}," class called ",[59,84554,15407],{},", and add a ",[59,84557,80940],{}," bean definition to it. We'll use the ",[59,84560,80990],{}," object to define our security rules, as shown below:",[52,84563,84565],{"className":54,"code":84564,"language":56,"meta":57,"style":57},"@Configuration\n@EnableWebSecurity\npublic class SecurityConfig {\n\n @Bean\n public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n return http.authorizeRequests()\n .requestMatchers(PathRequest.to(\"/\")).permitAll()\n .requestMatchers(PathRequest.to(\"/api/posts\")).hasRole(\"ADMIN\")\n .anyRequest().authenticated()\n .and()\n .csrf().disable()\n .httpBasic()\n .and()\n .build();\n }\n\n}\n\n",[59,84566,84567,84573,84579,84589,84593,84599,84617,84629,84651,84676,84688,84697,84709,84717,84725,84733,84737,84741],{"__ignoreMap":57},[62,84568,84569,84571],{"class":64,"line":65},[62,84570,942],{"class":72},[62,84572,11133],{"class":68},[62,84574,84575,84577],{"class":64,"line":76},[62,84576,942],{"class":72},[62,84578,11461],{"class":68},[62,84580,84581,84583,84585,84587],{"class":64,"line":82},[62,84582,116],{"class":68},[62,84584,119],{"class":68},[62,84586,11470],{"class":122},[62,84588,126],{"class":72},[62,84590,84591],{"class":64,"line":89},[62,84592,79],{"emptyLinePlaceholder":13},[62,84594,84595,84597],{"class":64,"line":95},[62,84596,2143],{"class":72},[62,84598,2146],{"class":68},[62,84600,84601,84603,84605,84607,84609,84611,84613,84615],{"class":64,"line":101},[62,84602,194],{"class":68},[62,84604,15452],{"class":72},[62,84606,11490],{"class":122},[62,84608,11493],{"class":72},[62,84610,11496],{"class":889},[62,84612,5024],{"class":72},[62,84614,11501],{"class":68},[62,84616,11504],{"class":72},[62,84618,84619,84621,84624,84627],{"class":64,"line":107},[62,84620,360],{"class":68},[62,84622,84623],{"class":72}," http.",[62,84625,84626],{"class":122},"authorizeRequests",[62,84628,2223],{"class":72},[62,84630,84631,84633,84635,84638,84641,84643,84645,84647,84649],{"class":64,"line":113},[62,84632,2418],{"class":72},[62,84634,15490],{"class":122},[62,84636,84637],{"class":72},"(PathRequest.",[62,84639,84640],{"class":122},"to",[62,84642,2109],{"class":72},[62,84644,15635],{"class":1675},[62,84646,81247],{"class":72},[62,84648,11549],{"class":122},[62,84650,2223],{"class":72},[62,84652,84653,84655,84657,84659,84661,84663,84665,84667,84670,84672,84674],{"class":64,"line":129},[62,84654,2418],{"class":72},[62,84656,15490],{"class":122},[62,84658,84637],{"class":72},[62,84660,84640],{"class":122},[62,84662,2109],{"class":72},[62,84664,41368],{"class":1675},[62,84666,81247],{"class":72},[62,84668,84669],{"class":122},"hasRole",[62,84671,2109],{"class":72},[62,84673,15717],{"class":1675},[62,84675,2212],{"class":72},[62,84677,84678,84680,84682,84684,84686],{"class":64,"line":134},[62,84679,2418],{"class":72},[62,84681,11544],{"class":122},[62,84683,3229],{"class":72},[62,84685,15518],{"class":122},[62,84687,2223],{"class":72},[62,84689,84690,84692,84695],{"class":64,"line":156},[62,84691,2610],{"class":72},[62,84693,84694],{"class":122},"and",[62,84696,2223],{"class":72},[62,84698,84699,84701,84703,84705,84707],{"class":64,"line":161},[62,84700,2418],{"class":72},[62,84702,11558],{"class":122},[62,84704,3229],{"class":72},[62,84706,81306],{"class":122},[62,84708,2223],{"class":72},[62,84710,84711,84713,84715],{"class":64,"line":167},[62,84712,2418],{"class":72},[62,84714,81114],{"class":122},[62,84716,2223],{"class":72},[62,84718,84719,84721,84723],{"class":64,"line":173},[62,84720,2610],{"class":72},[62,84722,84694],{"class":122},[62,84724,2223],{"class":72},[62,84726,84727,84729,84731],{"class":64,"line":179},[62,84728,2418],{"class":72},[62,84730,2189],{"class":122},[62,84732,822],{"class":72},[62,84734,84735],{"class":64,"line":185},[62,84736,223],{"class":72},[62,84738,84739],{"class":64,"line":191},[62,84740,79],{"emptyLinePlaceholder":13},[62,84742,84743],{"class":64,"line":209},[62,84744,379],{"class":72},[22,84746,84747],{},"This configuration sets the following rules:",[34,84749,84750,84753,84756],{},[37,84751,84752],{},"Allow anyone to access the root (\"/\") endpoint.",[37,84754,84755],{},"Require the \"ADMIN\" role to access the \"/api/posts\" endpoint.",[37,84757,84758],{},"Require authentication for any other request.",[22,84760,84761,84762,84765],{},"As you can see, this approach involves chaining each configuration option using the ",[59,84763,84764],{},"and()"," method. This can become quite lengthy in more complex configurations.",[26,84767,84769],{"id":84768},"lambda-dsl-configuration","Lambda DSL Configuration",[22,84771,84772,84773,84776,84777,84779,84780,84782],{},"Now, let's explore the ",[4534,84774,84775],{},"Lambda DSL"," configuration, which offers a cleaner and more concise approach. We'll create another ",[59,84778,80940],{}," bean definition in our ",[59,84781,15407],{}," class, implementing the same security rules but using lambda expressions:",[52,84784,84786],{"className":54,"code":84785,"language":56,"meta":57,"style":57},"@Bean\npublic SecurityFilterChain securityFilterChain(HttpSecurity http) {\n return http.authorizeRequests(auth -> auth\n .requestMatchers(PathRequest.to(\"/\")).permitAll()\n .requestMatchers(PathRequest.to(\"/api/posts\")).hasRole(\"ADMIN\")\n .anyRequest().authenticated())\n .csrf(csrf -> csrf.disable())\n .httpBasic(withDefaults())\n .build();\n}\n\n",[59,84787,84788,84794,84805,84819,84839,84863,84875,84891,84903,84911],{"__ignoreMap":57},[62,84789,84790,84792],{"class":64,"line":65},[62,84791,942],{"class":72},[62,84793,2146],{"class":68},[62,84795,84796,84798,84800,84802],{"class":64,"line":76},[62,84797,116],{"class":68},[62,84799,15452],{"class":72},[62,84801,11490],{"class":122},[62,84803,84804],{"class":72},"(HttpSecurity http) {\n",[62,84806,84807,84809,84811,84813,84815,84817],{"class":64,"line":82},[62,84808,2599],{"class":68},[62,84810,84623],{"class":72},[62,84812,84626],{"class":122},[62,84814,11536],{"class":72},[62,84816,800],{"class":68},[62,84818,15483],{"class":72},[62,84820,84821,84823,84825,84827,84829,84831,84833,84835,84837],{"class":64,"line":89},[62,84822,2610],{"class":72},[62,84824,15490],{"class":122},[62,84826,84637],{"class":72},[62,84828,84640],{"class":122},[62,84830,2109],{"class":72},[62,84832,15635],{"class":1675},[62,84834,81247],{"class":72},[62,84836,11549],{"class":122},[62,84838,2223],{"class":72},[62,84840,84841,84843,84845,84847,84849,84851,84853,84855,84857,84859,84861],{"class":64,"line":95},[62,84842,2610],{"class":72},[62,84844,15490],{"class":122},[62,84846,84637],{"class":72},[62,84848,84640],{"class":122},[62,84850,2109],{"class":72},[62,84852,41368],{"class":1675},[62,84854,81247],{"class":72},[62,84856,84669],{"class":122},[62,84858,2109],{"class":72},[62,84860,15717],{"class":1675},[62,84862,2212],{"class":72},[62,84864,84865,84867,84869,84871,84873],{"class":64,"line":101},[62,84866,2610],{"class":72},[62,84868,11544],{"class":122},[62,84870,3229],{"class":72},[62,84872,15518],{"class":122},[62,84874,4460],{"class":72},[62,84876,84877,84879,84881,84883,84885,84887,84889],{"class":64,"line":107},[62,84878,3862],{"class":72},[62,84880,11558],{"class":122},[62,84882,81264],{"class":72},[62,84884,800],{"class":68},[62,84886,81269],{"class":72},[62,84888,81306],{"class":122},[62,84890,4460],{"class":72},[62,84892,84893,84895,84897,84899,84901],{"class":64,"line":113},[62,84894,3862],{"class":72},[62,84896,81114],{"class":122},[62,84898,2109],{"class":72},[62,84900,79586],{"class":122},[62,84902,4460],{"class":72},[62,84904,84905,84907,84909],{"class":64,"line":129},[62,84906,3862],{"class":72},[62,84908,2189],{"class":122},[62,84910,822],{"class":72},[62,84912,84913],{"class":64,"line":134},[62,84914,379],{"class":72},[22,84916,84917,84918,84920,84921,84923,84924,84928,84929,84931,84932,2755],{},"As you can see, the Lambda DSL configuration is shorter and easier to read. It automatically returns the ",[59,84919,80990],{}," instance, so there's no need to chain options using the ",[59,84922,84764],{}," method. If you’re curious how this works you can take a look at the ",[677,84925,80990],{"href":84926,"rel":84927},"https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/config/annotation/web/builders/HttpSecurity.html",[681]," class which exposes a method named ",[59,84930,11533],{}," which accepts an argument of type ",[59,84933,84934],{},"Customizer\u003CT>",[52,84936,84938],{"className":54,"code":84937,"language":56,"meta":57,"style":57},"public HttpSecurity authorizeHttpRequests(Customizer\u003CAuthorizeHttpRequestsConfigurer\u003CHttpSecurity>.\nAuthorizationManagerRequestMatcherRegistry> authorizeHttpRequestsCustomizer) throws Exception {\n ApplicationContext context = this.getContext();\n authorizeHttpRequestsCustomizer.customize(((AuthorizeHttpRequestsConfigurer)\n this.getOrApply(new AuthorizeHttpRequestsConfigurer(context))).getRegistry());\n return this;\n}\n",[59,84939,84940,84966,84976,84992,85003,85027,85035],{"__ignoreMap":57},[62,84941,84942,84944,84947,84949,84952,84954,84957,84959,84961,84963],{"class":64,"line":65},[62,84943,116],{"class":68},[62,84945,84946],{"class":72}," HttpSecurity ",[62,84948,11533],{"class":122},[62,84950,84951],{"class":72},"(Customizer",[62,84953,760],{"class":68},[62,84955,84956],{"class":72},"AuthorizeHttpRequestsConfigurer",[62,84958,760],{"class":68},[62,84960,80990],{"class":72},[62,84962,2583],{"class":68},[62,84964,84965],{"class":72},".\n",[62,84967,84968,84971,84973],{"class":64,"line":76},[62,84969,84970],{"class":72},"AuthorizationManagerRequestMatcherRegistry",[62,84972,2583],{"class":68},[62,84974,84975],{"class":72}," authorizeHttpRequestsCustomizer) throws Exception {\n",[62,84977,84978,84981,84983,84985,84987,84990],{"class":64,"line":82},[62,84979,84980],{"class":72}," ApplicationContext context ",[62,84982,146],{"class":68},[62,84984,9961],{"class":149},[62,84986,2755],{"class":72},[62,84988,84989],{"class":122},"getContext",[62,84991,822],{"class":72},[62,84993,84994,84997,85000],{"class":64,"line":89},[62,84995,84996],{"class":72}," authorizeHttpRequestsCustomizer.",[62,84998,84999],{"class":122},"customize",[62,85001,85002],{"class":72},"(((AuthorizeHttpRequestsConfigurer)\n",[62,85004,85005,85007,85009,85012,85014,85016,85019,85022,85025],{"class":64,"line":95},[62,85006,39124],{"class":149},[62,85008,2755],{"class":72},[62,85010,85011],{"class":122},"getOrApply",[62,85013,2109],{"class":72},[62,85015,2426],{"class":68},[62,85017,85018],{"class":122}," AuthorizeHttpRequestsConfigurer",[62,85020,85021],{"class":72},"(context))).",[62,85023,85024],{"class":122},"getRegistry",[62,85026,1091],{"class":72},[62,85028,85029,85031,85033],{"class":64,"line":101},[62,85030,2599],{"class":68},[62,85032,9961],{"class":149},[62,85034,153],{"class":72},[62,85036,85037],{"class":64,"line":107},[62,85038,379],{"class":72},[22,85040,85041],{},"That Customizer type is a Functional Interface which means its a candidate for a lambda expression.",[52,85043,85045],{"className":54,"code":85044,"language":56,"meta":57,"style":57},"@FunctionalInterface\npublic interface Customizer\u003CT> {\n void customize(T t);\n\n static \u003CT> Customizer\u003CT> withDefaults() {\n return (t) -> {\n };\n }\n}\n",[59,85046,85047,85053,85068,85083,85087,85106,85117,85121,85125],{"__ignoreMap":57},[62,85048,85049,85051],{"class":64,"line":65},[62,85050,942],{"class":72},[62,85052,62223],{"class":68},[62,85054,85055,85057,85059,85062,85064,85066],{"class":64,"line":76},[62,85056,116],{"class":68},[62,85058,8531],{"class":68},[62,85060,85061],{"class":122}," Customizer",[62,85063,760],{"class":72},[62,85065,71267],{"class":68},[62,85067,8552],{"class":72},[62,85069,85070,85072,85075,85078,85081],{"class":64,"line":82},[62,85071,11710],{"class":68},[62,85073,85074],{"class":122}," customize",[62,85076,85077],{"class":72},"(T ",[62,85079,85080],{"class":889},"t",[62,85082,1133],{"class":72},[62,85084,85085],{"class":64,"line":89},[62,85086,79],{"emptyLinePlaceholder":13},[62,85088,85089,85091,85093,85095,85098,85100,85102,85104],{"class":64,"line":95},[62,85090,70061],{"class":68},[62,85092,5607],{"class":72},[62,85094,71267],{"class":68},[62,85096,85097],{"class":72},"> Customizer\u003C",[62,85099,71267],{"class":68},[62,85101,3135],{"class":72},[62,85103,79586],{"class":122},[62,85105,206],{"class":72},[62,85107,85108,85110,85113,85115],{"class":64,"line":101},[62,85109,360],{"class":68},[62,85111,85112],{"class":72}," (t) ",[62,85114,800],{"class":68},[62,85116,126],{"class":72},[62,85118,85119],{"class":64,"line":107},[62,85120,2252],{"class":72},[62,85122,85123],{"class":64,"line":113},[62,85124,223],{"class":72},[62,85126,85127],{"class":64,"line":129},[62,85128,379],{"class":72},[26,85130,85132],{"id":85131},"testing-the-configurations","Testing the Configurations",[22,85134,85135],{},"To test our configurations, we can run the application and use the default user and generated password provided by Spring Security. When accessing the root (\"/\") endpoint, we should receive a 200 OK response with the \"Hello, Spring Security\" message. If we try to access the \"/api/posts\" endpoint without providing valid credentials, we'll get a 401 Unauthorized response.",[22,85137,85138],{},[653,85139],{"alt":85140,"src":85141},"Testing the Configuration","/images/blog/2023/03/15/test-config.png",[22,85143,85144],{},"Both of these configurations achieve the same result; ultimately, it's a matter of personal preference which one you choose to use. However, keep in mind that the Lambda DSL approach is generally more concise and easier to read, especially for more complex configurations.",[26,85146,1499],{"id":1498},[22,85148,85149],{},"In this tutorial, we explored two different Spring Security configuration approaches: chaining style and Lambda DSL. Both approaches are valid and used in various projects, but the Lambda DSL is cleaner and more concise. Experiment with both configurations and choose the one that you find the most comfortable to work with. Remember to stay up-to-date with the latest features and best practices of the Spring ecosystem to ensure that your projects are efficient and maintainable. Happy coding!",[1527,85151,85152],{},"html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .spoOn, html code.shiki .spoOn{--shiki-default:#E36209;--shiki-dark:#FFAB70;--shiki-sepia:#E36209}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}",{"title":57,"searchDepth":76,"depth":76,"links":85154},[85155,85156,85157,85158,85159,85160],{"id":15195,"depth":76,"text":84335},{"id":84373,"depth":76,"text":84374},{"id":84541,"depth":76,"text":84542},{"id":84768,"depth":76,"text":84769},{"id":85131,"depth":76,"text":85132},{"id":1498,"depth":76,"text":1499},{"_id":85162,"path":85163,"title":85164,"description":73331,"meta":85165,"body":85171},"content/blog/2023/03/12/notion-api-file-expired.md","/blog/2023/03/12/notion-api-file-expired","Notion API File Request has expired",{"slug":85166,"date":85167,"published":13,"tags":85168,"author":17,"cover":85170,"excerpt":-1},"notion-api-file-expired","2023-03-12T14:30:00.000Z",[32793,85169],"notion","./notion-cover-image.png",{"type":19,"value":85172,"toc":85856},[85173,85182,85191,85194,85197,85206,85212,85215,85221,85224,85228,85231,85237,85240,85361,85373,85384,85731,85737,85809,85816,85822,85826,85829,85835,85844,85846,85853],[22,85174,85175,85176,85181],{},"If you follow me, you know I'm a huge fan of Notion. I use it for everything in my personal and professional life. Since the release of the ",[677,85177,85180],{"href":85178,"rel":85179},"https://developers.notion.com/",[681],"Notion API",", I've been playing with it, but I've never built anything that I was going to launch publicly.",[22,85183,85184,85185,85190],{},"That's all about to change as I work on launching my wife's new blog, ",[677,85186,85189],{"href":85187,"rel":85188},"https://www.thisismymomoir.com/",[681],"This is my Momoir",". To prepare for that, I put together a simple blog called AI Blog. This is a blog that I'm writing in Notion, using Notion AI to generate blog posts.",[22,85192,85193],{},"I figured this would be a great playground for me to work out any issues I might come across before I work on launching her site. I'm building the website in Vue using Nuxt 3. There are a bunch of things I want to blog about or create YouTube videos on, but I wanted to start with this one.",[22,85195,85196],{},"After finishing the AI Blog, I wanted to deploy it on Netlify to see if there were any issues with publishing it out in the wild. I decided to statically generate the entire site because there will only be about 1-2 blog posts per week, making this a perfect scenario to prioritize performance over real-time updates.",[22,85198,85199,85200,85205],{},"When I first deployed the ",[677,85201,85204],{"href":85202,"rel":85203},"https://ai-notion-blog.netlify.app/",[681],"site"," everything looked as I expected it to:",[22,85207,85208],{},[653,85209],{"alt":85210,"src":85211},"API Blog","/images/blog/2023/03/12/api-blog-home.png",[22,85213,85214],{},"When I returned to the blog the following day, I noticed that all of the images were missing. This was puzzling, so I decided to investigate further. Using the Chrome Dev Tools, I quickly discovered that I was receiving a 403 Forbidden error on each of the images. What was particularly strange was that some of the blog posts had images that were displaying properly.",[22,85216,85217],{},[653,85218],{"alt":85219,"src":85220},"Images Missing","/images/blog/2023/03/12/images-missing.png",[22,85222,85223],{},"I decided to redeploy the website and sure enough, everything was working again. I came back a couple of hours later and the images were missing again.",[26,85225,85227],{"id":85226},"notion-page-property-file-media-type","Notion Page Property File & Media Type",[22,85229,85230],{},"I decided to investigate the cause of this issue. It turns out that the cover images are a property of the page in Notion. If you take a look at the screenshot below, you can see that the page has various properties such as status, date created, and Image. The \"Image\" property is a file type, which I use to select the thumbnail image for this post.",[22,85232,85233],{},[653,85234],{"alt":85235,"src":85236},"Notion Page Properties","/images/blog/2023/03/12/notion-page-properties.png",[22,85238,85239],{},"When you retrieve the details for a page and request a specific property, you will receive some data that looks like this. Is there anything that stands out to you?",[52,85241,85243],{"className":3671,"code":85242,"language":3673,"meta":57,"style":57},"\"Image\": {\n \"id\": \"%5EmJf\",\n \"type\": \"files\",\n \"files\": [\n {\n \"name\": \"arseny-togulev-MECKPoKJYjM-unsplash.jpg\",\n \"type\": \"file\",\n \"file\": {\n \"url\": \"https://s3.us-west-2.amazonaws.com/secure.notion-static.com\n cee6e5ed-8b97-464a-934c-864830b60afa/arseny-togulev-MECKPoKJYjM-unsplash.jpg\",\n \"expiry_time\": \"2023-03-11T20:56:53.872Z\"\n }\n }\n ]\n},\n",[59,85244,85245,85252,85264,85276,85283,85287,85299,85311,85318,85328,85335,85345,85349,85353,85357],{"__ignoreMap":57},[62,85246,85247,85250],{"class":64,"line":65},[62,85248,85249],{"class":1675},"\"Image\"",[62,85251,3688],{"class":72},[62,85253,85254,85257,85259,85262],{"class":64,"line":76},[62,85255,85256],{"class":149}," \"id\"",[62,85258,3696],{"class":72},[62,85260,85261],{"class":1675},"\"%5EmJf\"",[62,85263,3338],{"class":72},[62,85265,85266,85269,85271,85274],{"class":64,"line":82},[62,85267,85268],{"class":149}," \"type\"",[62,85270,3696],{"class":72},[62,85272,85273],{"class":1675},"\"files\"",[62,85275,3338],{"class":72},[62,85277,85278,85281],{"class":64,"line":89},[62,85279,85280],{"class":149}," \"files\"",[62,85282,3709],{"class":72},[62,85284,85285],{"class":64,"line":95},[62,85286,18260],{"class":72},[62,85288,85289,85292,85294,85297],{"class":64,"line":101},[62,85290,85291],{"class":149}," \"name\"",[62,85293,3696],{"class":72},[62,85295,85296],{"class":1675},"\"arseny-togulev-MECKPoKJYjM-unsplash.jpg\"",[62,85298,3338],{"class":72},[62,85300,85301,85304,85306,85309],{"class":64,"line":107},[62,85302,85303],{"class":149}," \"type\"",[62,85305,3696],{"class":72},[62,85307,85308],{"class":1675},"\"file\"",[62,85310,3338],{"class":72},[62,85312,85313,85316],{"class":64,"line":113},[62,85314,85315],{"class":149}," \"file\"",[62,85317,3688],{"class":72},[62,85319,85320,85323,85325],{"class":64,"line":129},[62,85321,85322],{"class":149}," \"url\"",[62,85324,3696],{"class":72},[62,85326,85327],{"class":1675},"\"https://s3.us-west-2.amazonaws.com/secure.notion-static.com\n",[62,85329,85330,85333],{"class":64,"line":134},[62,85331,85332],{"class":1675}," cee6e5ed-8b97-464a-934c-864830b60afa/arseny-togulev-MECKPoKJYjM-unsplash.jpg\"",[62,85334,3338],{"class":72},[62,85336,85337,85340,85342],{"class":64,"line":156},[62,85338,85339],{"class":149}," \"expiry_time\"",[62,85341,3696],{"class":72},[62,85343,85344],{"class":1675},"\"2023-03-11T20:56:53.872Z\"\n",[62,85346,85347],{"class":64,"line":161},[62,85348,861],{"class":72},[62,85350,85351],{"class":64,"line":167},[62,85352,533],{"class":72},[62,85354,85355],{"class":64,"line":173},[62,85356,3726],{"class":72},[62,85358,85359],{"class":64,"line":179},[62,85360,72286],{"class":72},[22,85362,85363,85364,85368,85369,85372],{},"That's right, each file and media type, including images returned by the ",[677,85365,85180],{"href":85366,"rel":85367},"https://developers.notion.com/reference/file-object",[681],", has an ",[59,85370,85371],{},"expiry_time"," field. This means that they will expire after 1 hour. If the cover image is going to expire after an hour and I'm statically generating the entire site, I only have one option, which is to download all of the cover images.",[22,85374,85375,85376,85379,85380,85383],{},"This raises some questions about build time performance when the site gets larger, but I can put that issue aside for now as I have bigger problems to deal with. To solve this, I wrote a function that will take the image URL and a destination to save it in. When working in Nuxt 3, you should save any public images to the ",[59,85377,85378],{},"/public"," directory. In this case, I am putting them into an ",[59,85381,85382],{},"images"," directory, but on a larger site, I could create a directory structure for each post.",[52,85385,85387],{"className":32791,"code":85386,"language":32793,"meta":57,"style":57},"database.results.forEach(post => {\n // cover: post.properties.Image.files[0]?.file.url,\n const imgName = post.properties.Image.files[0]?.name;\n\n posts.push({\n id: post.id,\n title: post.properties.Name.title[0].plain_text,\n slug: post.properties.Slug.rich_text[0].plain_text,\n description: post.properties.Excerpt.rich_text[0].plain_text,\n cover: post.properties.Image.files[0]?.name\n });\n // the cover image expires after 1 hour so we need to download it\n downloadImage(\n post.properties.Image.files[0]?.file.url,\n `./public/images/${imgName}`\n );\n});\n\nasync function downloadImage(url, filepath) {\n fetch(url)\n .then(\n res =>\n new Promise((resolve, reject) => {\n const dest = fs.createWriteStream(filepath);\n res.body.pipe(dest);\n res.body.on(\"end\", () =>\n resolve(filepath.split(\"/\")[3] + \" was downloaded successfully.\")\n );\n dest.on(\"error\", reject);\n })\n )\n .then(x => console.log(x));\n}\n",[59,85388,85389,85405,85410,85428,85432,85441,85446,85456,85465,85474,85484,85489,85494,85501,85511,85522,85527,85532,85536,85556,85564,85572,85580,85605,85624,85634,85651,85679,85684,85698,85703,85707,85727],{"__ignoreMap":57},[62,85390,85391,85394,85396,85398,85400,85403],{"class":64,"line":65},[62,85392,85393],{"class":72},"database.results.",[62,85395,4215],{"class":122},[62,85397,2109],{"class":72},[62,85399,38838],{"class":889},[62,85401,85402],{"class":68}," =>",[62,85404,126],{"class":72},[62,85406,85407],{"class":64,"line":76},[62,85408,85409],{"class":85}," // cover: post.properties.Image.files[0]?.file.url,\n",[62,85411,85412,85415,85418,85420,85423,85425],{"class":64,"line":82},[62,85413,85414],{"class":68}," const",[62,85416,85417],{"class":149}," imgName",[62,85419,2556],{"class":68},[62,85421,85422],{"class":72}," post.properties.Image.files[",[62,85424,1130],{"class":149},[62,85426,85427],{"class":72},"]?.name;\n",[62,85429,85430],{"class":64,"line":89},[62,85431,79],{"emptyLinePlaceholder":13},[62,85433,85434,85437,85439],{"class":64,"line":95},[62,85435,85436],{"class":72}," posts.",[62,85438,5946],{"class":122},[62,85440,50544],{"class":72},[62,85442,85443],{"class":64,"line":101},[62,85444,85445],{"class":72}," id: post.id,\n",[62,85447,85448,85451,85453],{"class":64,"line":107},[62,85449,85450],{"class":72}," title: post.properties.Name.title[",[62,85452,1130],{"class":149},[62,85454,85455],{"class":72},"].plain_text,\n",[62,85457,85458,85461,85463],{"class":64,"line":113},[62,85459,85460],{"class":72}," slug: post.properties.Slug.rich_text[",[62,85462,1130],{"class":149},[62,85464,85455],{"class":72},[62,85466,85467,85470,85472],{"class":64,"line":129},[62,85468,85469],{"class":72}," description: post.properties.Excerpt.rich_text[",[62,85471,1130],{"class":149},[62,85473,85455],{"class":72},[62,85475,85476,85479,85481],{"class":64,"line":134},[62,85477,85478],{"class":72}," cover: post.properties.Image.files[",[62,85480,1130],{"class":149},[62,85482,85483],{"class":72},"]?.name\n",[62,85485,85486],{"class":64,"line":156},[62,85487,85488],{"class":72}," });\n",[62,85490,85491],{"class":64,"line":161},[62,85492,85493],{"class":85}," // the cover image expires after 1 hour so we need to download it\n",[62,85495,85496,85499],{"class":64,"line":167},[62,85497,85498],{"class":122}," downloadImage",[62,85500,3301],{"class":72},[62,85502,85503,85506,85508],{"class":64,"line":173},[62,85504,85505],{"class":72}," post.properties.Image.files[",[62,85507,1130],{"class":149},[62,85509,85510],{"class":72},"]?.file.url,\n",[62,85512,85513,85516,85519],{"class":64,"line":179},[62,85514,85515],{"class":1675}," `./public/images/${",[62,85517,85518],{"class":72},"imgName",[62,85520,85521],{"class":1675},"}`\n",[62,85523,85524],{"class":64,"line":185},[62,85525,85526],{"class":72}," );\n",[62,85528,85529],{"class":64,"line":191},[62,85530,85531],{"class":72},"});\n",[62,85533,85534],{"class":64,"line":209},[62,85535,79],{"emptyLinePlaceholder":13},[62,85537,85538,85540,85542,85545,85547,85549,85551,85554],{"class":64,"line":220},[62,85539,21516],{"class":68},[62,85541,21929],{"class":68},[62,85543,85544],{"class":122}," downloadImage",[62,85546,2109],{"class":72},[62,85548,21936],{"class":889},[62,85550,976],{"class":72},[62,85552,85553],{"class":889},"filepath",[62,85555,768],{"class":72},[62,85557,85558,85561],{"class":64,"line":226},[62,85559,85560],{"class":122}," fetch",[62,85562,85563],{"class":72},"(url)\n",[62,85565,85566,85568,85570],{"class":64,"line":231},[62,85567,4252],{"class":72},[62,85569,36912],{"class":122},[62,85571,3301],{"class":72},[62,85573,85574,85577],{"class":64,"line":236},[62,85575,85576],{"class":889}," res",[62,85578,85579],{"class":68}," =>\n",[62,85581,85582,85585,85588,85591,85594,85596,85599,85601,85603],{"class":64,"line":242},[62,85583,85584],{"class":68}," new",[62,85586,85587],{"class":149}," Promise",[62,85589,85590],{"class":72},"((",[62,85592,85593],{"class":889},"resolve",[62,85595,976],{"class":72},[62,85597,85598],{"class":889},"reject",[62,85600,5024],{"class":72},[62,85602,21525],{"class":68},[62,85604,126],{"class":72},[62,85606,85607,85610,85613,85615,85618,85621],{"class":64,"line":247},[62,85608,85609],{"class":68}," const",[62,85611,85612],{"class":149}," dest",[62,85614,2556],{"class":68},[62,85616,85617],{"class":72}," fs.",[62,85619,85620],{"class":122},"createWriteStream",[62,85622,85623],{"class":72},"(filepath);\n",[62,85625,85626,85629,85631],{"class":64,"line":252},[62,85627,85628],{"class":72}," res.body.",[62,85630,51759],{"class":122},[62,85632,85633],{"class":72},"(dest);\n",[62,85635,85636,85638,85641,85643,85646,85648],{"class":64,"line":257},[62,85637,85628],{"class":72},[62,85639,85640],{"class":122},"on",[62,85642,2109],{"class":72},[62,85644,85645],{"class":1675},"\"end\"",[62,85647,27373],{"class":72},[62,85649,85650],{"class":68},"=>\n",[62,85652,85653,85656,85659,85661,85663,85665,85668,85670,85672,85674,85677],{"class":64,"line":271},[62,85654,85655],{"class":122}," resolve",[62,85657,85658],{"class":72},"(filepath.",[62,85660,6894],{"class":122},[62,85662,2109],{"class":72},[62,85664,15635],{"class":1675},[62,85666,85667],{"class":72},")[",[62,85669,4472],{"class":149},[62,85671,36871],{"class":72},[62,85673,1148],{"class":68},[62,85675,85676],{"class":1675}," \" was downloaded successfully.\"",[62,85678,2212],{"class":72},[62,85680,85681],{"class":64,"line":281},[62,85682,85683],{"class":72}," );\n",[62,85685,85686,85689,85691,85693,85695],{"class":64,"line":286},[62,85687,85688],{"class":72}," dest.",[62,85690,85640],{"class":122},[62,85692,2109],{"class":72},[62,85694,16691],{"class":1675},[62,85696,85697],{"class":72},", reject);\n",[62,85699,85700],{"class":64,"line":291},[62,85701,85702],{"class":72}," })\n",[62,85704,85705],{"class":64,"line":296},[62,85706,36951],{"class":72},[62,85708,85709,85711,85713,85715,85718,85720,85722,85724],{"class":64,"line":302},[62,85710,4252],{"class":72},[62,85712,36912],{"class":122},[62,85714,2109],{"class":72},[62,85716,85717],{"class":889},"x",[62,85719,85402],{"class":68},[62,85721,58268],{"class":72},[62,85723,58271],{"class":122},[62,85725,85726],{"class":72},"(x));\n",[62,85728,85729],{"class":64,"line":308},[62,85730,379],{"class":72},[22,85732,85733,85734,24909],{},"Back on the home page where I display the image I can now just reference the local ",[59,85735,85736],{},"/images",[52,85738,85740],{"className":15773,"code":85739,"language":15775,"meta":57,"style":57},"\u003CNuxtLink :to=\"`/blog/${post.slug}`\">\n \u003Cimg\n :src=\"`/images/${post.cover}`\"\n alt=\"Blog post image\"\n class=\"w-full h-48 object-cover\"\n />\n\u003C/NuxtLink>\n",[59,85741,85742,85759,85766,85776,85786,85796,85801],{"__ignoreMap":57},[62,85743,85744,85746,85749,85752,85754,85757],{"class":64,"line":65},[62,85745,760],{"class":72},[62,85747,85748],{"class":23824},"NuxtLink",[62,85750,85751],{"class":122}," :to",[62,85753,146],{"class":72},[62,85755,85756],{"class":1675},"\"`/blog/${post.slug}`\"",[62,85758,1784],{"class":72},[62,85760,85761,85763],{"class":64,"line":76},[62,85762,33056],{"class":72},[62,85764,85765],{"class":1780},"img\n",[62,85767,85768,85771,85773],{"class":64,"line":82},[62,85769,85770],{"class":122}," :src",[62,85772,146],{"class":72},[62,85774,85775],{"class":1675},"\"`/images/${post.cover}`\"\n",[62,85777,85778,85781,85783],{"class":64,"line":89},[62,85779,85780],{"class":122}," alt",[62,85782,146],{"class":72},[62,85784,85785],{"class":1675},"\"Blog post image\"\n",[62,85787,85788,85791,85793],{"class":64,"line":95},[62,85789,85790],{"class":122}," class",[62,85792,146],{"class":72},[62,85794,85795],{"class":1675},"\"w-full h-48 object-cover\"\n",[62,85797,85798],{"class":64,"line":101},[62,85799,85800],{"class":72}," />\n",[62,85802,85803,85805,85807],{"class":64,"line":107},[62,85804,1818],{"class":72},[62,85806,85748],{"class":23824},[62,85808,1784],{"class":72},[22,85810,85811,85812,85815],{},"I tried writing the download image function using Nuxt’s ",[59,85813,85814],{},"$fetch"," but I didn’t have much luck. I decided to take my issues to Twitter and ask my friends there. I don’t have any answers yet but If I get one I will be sure to update this blog post.",[22,85817,85818],{},[677,85819,85820],{"href":85820,"rel":85821},"https://twitter.com/therealdanvega/status/1634977252931960832",[681],[26,85823,85825],{"id":85824},"images-expiring-update","Images Expiring Update",[22,85827,85828],{},"I realized that after I deployed a test version of my Wife's new blog she was having the same problem on all of her post pages. This is because she was using her own images that she uploaded to Notion while I was using Unsplash Images on the AI Blog.",[22,85830,85831,85832,85834],{},"This now presents the same problem in each of her posts. I can use the same technique to download each of the images and save them to the ",[59,85833,116],{}," directory. I'm not sure I want to do this for every single image and I sure don't want to do this all at build time. My current thought process is that I might look to see if this image exists first and if it doesn't then I will save it. This of course presents a problem if there is a newer version of the same file but I don't think I am going to run into that problem right now.",[22,85836,85837,85838,85843],{},"I’m also interested in using the ",[677,85839,85842],{"href":85840,"rel":85841},"https://image.nuxtjs.org/",[681],"Nuxt Image"," module which would allow me to use a single image and translate it to different versions for different screen sizes. The Nuxt Image module has many different options when it comes to providers but this is something I haven’t done before. My next question is how do I take these images and programmatically push them to one of these providers? My small task of using Notion as a CMS has quickly created some interesting problems.",[26,85845,1499],{"id":1498},[22,85847,85848,85849,2755],{},"I’m having a lot of fun writing a blog that uses Notion as the CMS. It certainly hasn’t been easy but I am learning a lot and that always makes me happy. If you’re interested in the code for the AI Blog you can check it out ",[677,85850,8441],{"href":85851,"rel":85852},"https://github.com/danvega/ai-blog",[681],[1527,85854,85855],{},"html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .spoOn, html code.shiki .spoOn{--shiki-default:#E36209;--shiki-dark:#FFAB70;--shiki-sepia:#E36209}html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .s4-Ip, html code.shiki .s4-Ip{--shiki-default:#6A737D;--shiki-dark:#6A737D;--shiki-sepia:#6A737D}html pre.shiki code .shOWo, html code.shiki .shOWo{--shiki-default:#B31D28;--shiki-default-font-style:italic;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic;--shiki-sepia:#B31D28;--shiki-sepia-font-style:italic}html pre.shiki code .suaqH, html code.shiki .suaqH{--shiki-default:#22863A;--shiki-dark:#85E89D;--shiki-sepia:#22863A}",{"title":57,"searchDepth":76,"depth":76,"links":85857},[85858,85859,85860],{"id":85226,"depth":76,"text":85227},{"id":85824,"depth":76,"text":85825},{"id":1498,"depth":76,"text":1499},{"_id":85862,"path":85863,"title":85864,"description":73331,"meta":85865,"body":85870},"content/blog/2023/03/09/spring-boot-crash-course.md","/blog/2023/03/09/spring-boot-crash-course","Getting Started with Spring: A Beginner's Guide",{"slug":85866,"date":85867,"published":13,"tags":85868,"author":17,"cover":85869,"excerpt":-1},"spring-boot-crash-course","2023-03-09T11:30:00.000Z",[2925,48209],"./spring-boot-crash-course-new-thumbnail.png",{"type":19,"value":85871,"toc":86350},[85872,85876,85879,85882,85885,85891,85893,85896,85905,85913,85918,85921,85925,85928,85931,85938,85941,85950,85964,85969,85972,85974,85977,85980,85991,85996,86000,86009,86012,86015,86018,86028,86049,86054,86057,86060,86066,86069,86074,86079,86082,86085,86088,86092,86095,86103,86105,86108,86112,86115,86118,86121,86127,86131,86138,86143,86147,86150,86153,86157,86160,86166,86168,86171,86174,86217,86220,86243,86246,86260,86262,86327,86329,86348],[26,85873,85875],{"id":85874},"where-do-i-start","Where do I start?",[22,85877,85878],{},"I often receive the question, \"I want to learn Spring. Where do I start?\" This is usually asked by someone with Java experience who wants to learn Spring for either their current job or a job they are seeking. Another question I frequently hear is whether someone needs to learn Spring before learning Spring Boot. This article aims to answer these questions.",[22,85880,85881],{},"I decided to sit down and put together an outline for a course that would help anyone interested in learning Spring get up and running quickly. The first goal of this course is that it is accessible to anyone who wanted to learn Spring and with that I decided to make it available for free on my YouTube channel. Before we take a look at what you need to learn let’s talk about what you should know first.",[22,85883,85884],{},"Next, I wanted to ensure that the course was the appropriate length, being long enough to cover the material, but still something they can complete over a weekend. As a Udemy course instructor, I have access to statistics on students who start long courses versus those who finish them, and the numbers are rather alarming.",[22,85886,85887],{},[653,85888],{"alt":85889,"src":85890},"Where do I start learning Spring?","/images/blog/2023/03/09/photo-1501504905252-473c47e087f8.jpeg",[26,85892,2874],{"id":2873},[22,85894,85895],{},"To get started with Spring, you should have some experience with the Java Programming Language. Although it is possible to build applications with Kotlin or Groovy, most developers find it helpful to learn Java first and then pick up other JVM languages later.",[22,85897,85898,85899,85904],{},"If you want to follow the course that I have created, you will need a basic understanding of Java. If you are new to Java, you can check out my ",[677,85900,85903],{"href":85901,"rel":85902},"https://www.danvega.dev/courses",[681],"free course"," on Getting Started with Java.",[22,85906,85907,85908,85912],{},"For the latest version of Spring, we have set Java 17 as the baseline. Therefore, before proceeding, make sure that you have Java 17 installed on your system. You will need an IDE or text editor that supports Java development. I am a big fan of ",[677,85909,85911],{"href":57660,"rel":85910},[681],"IntelliJ IDEA"," Ultimate which is what I use in this course but feel free to use whatever IDE or text editor you’re most productive in.",[22,85914,85915],{},[653,85916],{"alt":2874,"src":85917},"/images/blog/2023/03/09/prerequisites.png",[22,85919,85920],{},"Once you feel comfortable that you are the ideal student for this course and have everything installed we can start by covering the basics.",[26,85922,85924],{"id":85923},"spring-framework-vs-spring-boot","Spring Framework vs Spring Boot",[22,85926,85927],{},"If you are new to Spring and have started researching, you have probably encountered the terms Spring Framework and Spring Boot. Do you need to learn both? Which one should you learn first? These are probably some of the questions running through your head, and I hope to help you answer them.",[636,85929,48209],{"id":85930},"spring-framework",[22,85932,85933,85937],{},[677,85934,48209],{"href":85935,"rel":85936},"https://spring.io/projects/spring-framework",[681]," is the foundation which all Spring applications are built upon. The great thing and probably what can be confusing for anyone new to the Spring Framework is that it can be used to build a wide range of applications.",[22,85939,85940],{},"Traditionally, Spring has been used for building web applications using Spring MVC, which enables the creation of fast, responsive applications that can connect to any data store. However, Spring introduced Spring Webflux to allow for asynchronous, non-blocking architecture, which can improve the performance and scalability of certain applications.",[22,85942,85943,85944,85949],{},"You can use Spring to break down monolithic architectures into smaller, independently deployable microservices. Building distributed architectures present a set of problems that need to be solved, but don't worry, ",[677,85945,85948],{"href":85946,"rel":85947},"https://spring.io/projects/spring-cloud",[681],"Spring Cloud"," has you covered.",[22,85951,85952,85953,85958,85959,2755],{},"Spring can be used to build serverless workloads that provide ultimate flexibility by scaling on demand, as well as scaling to zero when there is no demand. You can use ",[677,85954,85957],{"href":85955,"rel":85956},"https://spring.io/projects/spring-batch",[681],"Spring Batch"," to build automated tasks for processing large sets of data. Finally, one of my new favorite ways to use Spring is to build command-line applications with ",[677,85960,85963],{"href":85961,"rel":85962},"https://spring.io/projects/spring-shell",[681],"Spring Shell",[22,85965,85966],{},[653,85967],{"alt":48209,"src":85968},"/images/blog/2023/03/09/spring-framework.png",[22,85970,85971],{},"As you can see, Spring can be used to provide solutions for a variety of application needs. That's why when people ask me how to get started with Spring, I usually ask them what they want to use it for. By understanding the type of applications they're trying to build, they can narrow down their learning path to something much more manageable.",[636,85973,2925],{"id":57650},[22,85975,85976],{},"Now that you have a good understanding of what the Spring Framework is, let's discuss where Spring Boot fits into the picture. Surprisingly, you don't actually need Spring Boot to build Spring applications. However, as you'll learn here, you would never want to build one without it.",[22,85978,85979],{},"Spring Boot provides a lot of features out of the box for building Spring applications but I want to focus on 3 of them in this article:",[915,85981,85982,85985,85988],{},[37,85983,85984],{},"Spring Boot Starters",[37,85986,85987],{},"AutoConfiguration",[37,85989,85990],{},"Production Ready",[22,85992,85993],{},[653,85994],{"alt":2925,"src":85995},"/images/blog/2023/03/09/spring-boot.png",[22,85997,85998],{},[646,85999,85984],{},[22,86001,86002,86003,86008],{},"As I mentioned earlier, you don't necessarily need to use Spring Boot to create a Spring application. In fact, I ",[677,86004,86007],{"href":86005,"rel":86006},"https://youtu.be/e8aSyQo0nHo",[681],"created a tutorial"," on how to do this, which outlines the process. While the example I provided is relatively simple, the process can become more complicated depending on the type of application you want to build. Nonetheless, I hope this tutorial helps you get started.",[22,86010,86011],{},"Spring Boot starters are dependency descriptors that simplify the process of including libraries in your project. They provide a quick and easy way to add common functionality to your application, without the need to manually configure each library. Spring Boot starters are available for a wide range of applications, including web, data, testing, and security.",[22,86013,86014],{},"For example, suppose you want to build a web application. First, you should determine which libraries you need to meet your requirements. For a typical web application, you need Spring Core, along with an embedded servlet container like Tomcat, Spring MVC, and a library like Jackson for serializing and deserializing objects to and from JSON.",[22,86016,86017],{},"Once you have identified the dependencies of your application, you need to determine their respective versions and whether they are compatible with each other. This task becomes increasingly difficult as the complexity of your application grows.",[22,86019,86020,86021,86024,86025,86027],{},"When creating a new project at ",[677,86022,2903],{"href":45295,"rel":86023},[681],", one of the options is to select your dependencies. If you choose Spring Web, you are indicating that you want to build a traditional Spring MVC application. By doing so, you will receive a single Spring Boot Starter called ",[59,86026,47198],{},", which includes all of the necessary dependencies with the correct versions.",[52,86029,86031],{"className":1769,"code":86030,"language":1771,"meta":57,"style":57},"\u003Cdependency>\n \u003CgroupId>org.springframework.boot\u003C/groupId>\n \u003CartifactId>spring-boot-starter-web\u003C/artifactId>\n\u003C/dependency>\n",[59,86032,86033,86037,86041,86045],{"__ignoreMap":57},[62,86034,86035],{"class":64,"line":65},[62,86036,46425],{},[62,86038,86039],{"class":64,"line":76},[62,86040,54736],{},[62,86042,86043],{"class":64,"line":82},[62,86044,56937],{},[62,86046,86047],{"class":64,"line":89},[62,86048,46445],{},[22,86050,86051],{},[646,86052,86053],{},"Auto-Configuration",[22,86055,86056],{},"The next important feature of Spring Boot we need to discuss is auto-configuration. Spring Boot auto-configuration attempts to configure your application based on the dependencies that you have added.",[22,86058,86059],{},"If you create a Spring Web application, an embedded version of Tomcat is included. In a typical Java application, you would need to set up and configure Tomcat yourself. However, since Tomcat is on the classpath, Spring automatically configures it and provides sensible defaults such as setting the default port to 8080. You can always change the port through your own configuration, but Spring sets this default to get you up and running quickly.",[22,86061,86062,86063,86065],{},"Here's another example of how to include a database like ",[59,86064,81614],{}," in your project. In a typical Java application, you would need to set up and configure a data source, which can be a tedious process that slows down your development. With Spring, however, you can easily configure a data source without any extra hassle. If needed, you can always modify the configuration to fit your specific requirements.",[22,86067,86068],{},"Auto-configuration is a feature of Spring Boot that allows developers to configure their applications without writing any boilerplate code. Spring Boot combines a set of sensible default configurations, based on what dependencies you have added to your project, to help you get up and running quickly. This means that you can focus on writing business logic rather than worrying about configuration details.",[22,86070,86071],{},[646,86072,86073],{},"Production Ready Features",[22,86075,86076],{},[653,86077],{"alt":86073,"src":86078},"/images/blog/2023/03/09/photo-1580106815433-a5b1d1d53d85.jpeg",[22,86080,86081],{},"Enabling developers to build the next generation of applications is not enough if you cannot get them into production. Spring provides a range of features that can assist you in building your applications for any environment and comprehending how they are performing in that environment.",[22,86083,86084],{},"The first step in moving to production is to build an artifact suitable for the environment you are moving to. The Spring Boot Maven and Gradle plugins allow you to build an executable JAR that can be run in any environment that supports Java. For environments that don't support Java, you can use Cloud Native Buildpacks to create an optimized Docker-compatible container image. Finally, for certain workloads, it might make sense to create a native executable.",[22,86086,86087],{},"Once you move your application to production you need to ensure that it is working as expected. You can use the Spring Actuator to gain observability into your application, allowing you to monitor and troubleshoot issues as they arise. Spring Boot provides a range of features to help you build and deploy your applications for any environment, whether it be on-premise or in the cloud.",[636,86089,86091],{"id":86090},"which-one-do-i-learn-first","Which one do I learn first?",[22,86093,86094],{},"Now that we know the difference between Spring Framework and Spring Boot, we can answer the question of which one to learn first. When building your first Spring application, you will use Spring Boot. However, in the process of building it, you will inherently learn Spring Framework.",[22,86096,86097,86098,2755],{},"The Spring Framework is based on the concepts of Inversion of Control (IoC) and Dependency Injection (DI). In this crash course, you will learn about these concepts and come to understand terms like Application Context, Containers, and beans. Once you have a basic understanding of these concepts and have built something with them, you can dive deeper into the ",[677,86099,86102],{"href":86100,"rel":86101},"https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#spring-core",[681],"Spring Reference documentation",[26,86104,55439],{"id":85866},[22,86106,86107],{},"By now, I hope you have a good understanding of Spring. I believe that theory can only take you so far, and it's time to get your hands dirty. As a proponent of hands-on learning, I encourage you to build something real and tangible when learning something new.",[636,86109,86111],{"id":86110},"what-are-we-building","What are we building",[22,86113,86114],{},"This is a REST API application that will communicate with a database. To focus our application, we decided to build out a content calendar since the content has many different properties and we could include a couple of Enums.",[22,86116,86117],{},"When a client calls the REST API, it initially talks to an in-memory collection of data. Once the application is fully functional, we will move the in-memory collection to a database. You will learn about connecting to, reading, and persisting data in Java and Spring.",[22,86119,86120],{},"Ultimately, it is important to get our applications into production. Therefore, I will guide you through deploying your application and database into production using a free hosting service.",[22,86122,86123],{},[653,86124],{"alt":86125,"src":86126},"What are we building?","/images/blog/2023/03/09/what-are-we-building.png",[636,86128,86130],{"id":86129},"what-are-we-going-to-learn","What are we going to learn?",[22,86132,86133,86134,86137],{},"To start, you will build a new application from scratch using ",[677,86135,2903],{"href":45295,"rel":86136},[681],". Along the way, you will learn many core concepts in Spring, such as Bean and Dependency Injection. You will also learn how to build a REST API using Spring MVC, connect and configure a database in Spring, and use Spring Data Repositories to remove boilerplate code and focus on application requirements. Finally, you will prepare your application for production, build and deploy it.",[22,86139,86140],{},[653,86141],{"alt":86130,"src":86142},"/images/blog/2023/03/09/what-are-we-learning.png",[636,86144,86146],{"id":86145},"spring-boot-crash-course-video","Spring Boot Crash Course Video",[22,86148,86149],{},"If you didn't receive the link to the video at the beginning of the article, you can find the course here. The video is almost 4 hours long, but there are chapters listed in the YouTube description if you need to jump around. I hope you enjoy it and would love to hear your feedback.",[36089,86151],{"id":86152},"UgX5lgv4uVM",[636,86154,86156],{"id":86155},"github-repository","Github Repository",[22,86158,86159],{},"You can find all of the code for the Spring Boot Crash Course at the GitHub Repository listed below.",[22,86161,86162],{},[677,86163,86164],{"href":86164,"rel":86165},"https://github.com/danvega/content-calendar",[681],[26,86167,4098],{"id":4097},[22,86169,86170],{},"This is a list of resources I recommend when learning Spring.",[636,86172,37639],{"id":86173},"documentation",[915,86175,86176,86183,86190,86197,86203,86210],{},[37,86177,86178],{},[677,86179,86182],{"href":86180,"rel":86181},"https://docs.spring.io/spring-framework/docs/current/reference/html/",[681],"Spring Framework Reference",[37,86184,86185],{},[677,86186,86189],{"href":86187,"rel":86188},"https://docs.spring.io/spring-framework/docs/current/javadoc-api/",[681],"Spring Framework API",[37,86191,86192],{},[677,86193,86196],{"href":86194,"rel":86195},"https://docs.spring.io/spring-boot/docs/current/reference/html/index.html",[681],"Spring Boot Reference",[37,86198,86199],{},[677,86200,58064],{"href":86201,"rel":86202},"https://docs.spring.io/spring-boot/docs/current/api/",[681],[37,86204,86205],{},[677,86206,86209],{"href":86207,"rel":86208},"https://spring.io/guides",[681],"Spring Boot Guides",[37,86211,86212],{},[677,86213,86216],{"href":86214,"rel":86215},"https://tanzu.vmware.com/developer/guides/",[681],"Tanzu Developer Center Guides",[636,86218,37756],{"id":86219},"books",[915,86221,86222,86229,86236],{},[37,86223,86224],{},[677,86225,86228],{"href":86226,"rel":86227},"https://amzn.to/3WOSutb",[681],"Spring Boot Up and Running - Mark Heckler",[37,86230,86231],{},[677,86232,86235],{"href":86233,"rel":86234},"https://amzn.to/3CuCgxc",[681],"Learning Spring Boot 3.0 - Greg Turnquist",[37,86237,86238],{},[677,86239,86242],{"href":86240,"rel":86241},"https://amzn.to/3ZcI3kx",[681],"Spring Boot in Action - Craig Walls",[636,86244,37576],{"id":86245},"podcasts",[915,86247,86248,86255],{},[37,86249,86250],{},[677,86251,86254],{"href":86252,"rel":86253},"http://bootifulpodcast.fm/",[681],"Bootiful Podcast",[37,86256,86257],{},[677,86258,38010],{"href":38207,"rel":86259},[681],[636,86261,37949],{"id":37948},[915,86263,86264,86271,86278,86285,86292,86299,86306,86313,86320],{},[37,86265,86266],{},[677,86267,86270],{"href":86268,"rel":86269},"https://www.youtube.com/@SpringBootLearning",[681],"Spring Boot Learning",[37,86272,86273],{},[677,86274,86277],{"href":86275,"rel":86276},"https://www.youtube.com/playlist?list=PLgGXSWYM2FpPw8rV0tZoMiJYSCiLhPnOc",[681],"Spring Tips",[37,86279,86280],{},[677,86281,86284],{"href":86282,"rel":86283},"https://www.youtube.com/@SpringSourceDev",[681],"Spring Developer",[37,86286,86287],{},[677,86288,86291],{"href":86289,"rel":86290},"https://www.youtube.com/@amigoscode",[681],"Amigoscode",[37,86293,86294],{},[677,86295,86298],{"href":86296,"rel":86297},"https://www.youtube.com/c/JavaBrainsChannel",[681],"Java Brains",[37,86300,86301],{},[677,86302,86305],{"href":86303,"rel":86304},"https://www.youtube.com/@dashaun",[681],"DaShaun Carter",[37,86307,86308],{},[677,86309,86312],{"href":86310,"rel":86311},"https://www.youtube.com/@coffeesoftware",[681],"Coffee and Software",[37,86314,86315],{},[677,86316,86319],{"href":86317,"rel":86318},"https://www.youtube.com/@DailyCodeBuffer",[681],"Daily Code Buffer",[37,86321,86322],{},[677,86323,86326],{"href":86324,"rel":86325},"https://www.youtube.com/@MarcoCodes",[681],"Marco Codes",[26,86328,1499],{"id":1498},[22,86330,86331,86332,86337,86338,86343,86344,86347],{},"I had a lot of fun putting together this Spring Boot Crash Course. I hope it helps anyone interested in learning Spring, and that the video and this article help answer any questions you have about getting started. Please feel free to follow me on ",[677,86333,86336],{"href":86334,"rel":86335},"https://twitter.com/therealdanvega",[681],"Twitter",", Subscribe to my ",[677,86339,86342],{"href":86340,"rel":86341},"https://www.youtube.com/@danvega",[681],"YouTube channel",", or sign up for my free ",[677,86345,33895],{"href":38005,"rel":86346},[681]," to stay up to date with what I am working on.",[1527,86349,55375],{},{"title":57,"searchDepth":76,"depth":76,"links":86351},[86352,86353,86354,86359,86365,86371],{"id":85874,"depth":76,"text":85875},{"id":2873,"depth":76,"text":2874},{"id":85923,"depth":76,"text":85924,"children":86355},[86356,86357,86358],{"id":85930,"depth":82,"text":48209},{"id":57650,"depth":82,"text":2925},{"id":86090,"depth":82,"text":86091},{"id":85866,"depth":76,"text":55439,"children":86360},[86361,86362,86363,86364],{"id":86110,"depth":82,"text":86111},{"id":86129,"depth":82,"text":86130},{"id":86145,"depth":82,"text":86146},{"id":86155,"depth":82,"text":86156},{"id":4097,"depth":76,"text":4098,"children":86366},[86367,86368,86369,86370],{"id":86173,"depth":82,"text":37639},{"id":86219,"depth":82,"text":37756},{"id":86245,"depth":82,"text":37576},{"id":37948,"depth":82,"text":37949},{"id":1498,"depth":76,"text":1499},{"_id":86373,"path":86374,"title":86375,"description":86376,"meta":86377,"body":86384},"content/blog/2023/03/02/spring-shell-intro.md","/blog/2023/03/02/spring-shell-intro","Building Command Line Applications in Spring with Spring Shell","In this introduction to Spring Shell, you will learn you how to build CLI applications in a familiar programming environment using Java + Spring.",{"slug":86378,"date":86379,"published":13,"tags":86380,"author":17,"cover":86383,"excerpt":-1},"spring-shell-intro","2023-03-02T11:00:00.000Z",[86381,86382],"spring shell","Spring boot","./spring-shell-introduction.png",{"type":19,"value":86385,"toc":87487},[86386,86393,86397,86426,86431,86435,86438,86454,86603,86622,86624,86634,86666,86670,86683,86777,86783,86808,86812,86821,86830,86860,86869,86943,86950,87120,87126,87304,87310,87330,87334,87346,87364,87367,87389,87394,87398,87407,87425,87434,87473,87476,87478,87481,87484],[22,86387,86388,86389,86392],{},"Today, we're going to talk about building command line applications using ",[677,86390,85963],{"href":85961,"rel":86391},[681],". Spring Shell allows you to use the programming model, tools, frameworks, and languages that you're used to using every day to build command line applications. We're going to build a command line application called \"Dad Joke\" that fetches a random dad joke from a public API.",[26,86394,86396],{"id":86395},"setup","Setup",[34,86398,86399,86404,86407,86410,86423],{},[37,86400,34891,86401,79287],{},[677,86402,1744],{"href":2901,"rel":86403},[681],[37,86405,86406],{},"Choose the \"Java\" project type and \"Maven\" as the build tool.",[37,86408,86409],{},"Use the latest stable release of Spring Boot (3.0.3 at the time of this recording).",[37,86411,86412,86413],{},"Set the artifact name to \"DadJoke\" and use the following dependencies:\n",[915,86414,86415,86417,86420],{},[37,86416,85963],{},[37,86418,86419],{},"GraalVM Native support (more on this later)",[37,86421,86422],{},"Spring WebFlux (Reactive Web)",[37,86424,86425],{},"Generate and download the .zip file, then open it up in your favorite IDE.",[22,86427,86428],{},[653,86429],{"alt":24606,"src":86430},"/images/blog/2023/03/02/start-spring-init.png",[26,86432,86434],{"id":86433},"creating-a-hello-command","Creating a Hello Command",[22,86436,86437],{},"When building command line applications with Spring Shell, we write functionality in classes called \"commands\". Let's create a basic \"Hello Command\":",[34,86439,86440,86445],{},[37,86441,48852,86442,2755],{},[59,86443,86444],{},"commands",[37,86446,86447,86448,48324,86451,86453],{},"Create a new class ",[59,86449,86450],{},"HelloCommand",[59,86452,86444],{}," package with the following annotations and methods:",[52,86455,86457],{"className":54,"code":86456,"language":56,"meta":57,"style":57},"import org.springframework.shell.standard.ShellComponent;\nimport org.springframework.shell.standard.ShellMethod;\n\n@ShellComponent\npublic class HelloCommand {\n @ShellMethod(key = \"hello\", value = \"Say hello\")\n public String hello() {\n return \"Hello, world!\";\n }\n\n @ShellMethod(key = \"goodbye\", value = \"Say goodbye\")\n public String goodbye() {\n return \"Goodbye!\";\n }\n}\n",[59,86458,86459,86466,86473,86477,86484,86495,86522,86532,86541,86545,86549,86575,86586,86595,86599],{"__ignoreMap":57},[62,86460,86461,86463],{"class":64,"line":65},[62,86462,27875],{"class":68},[62,86464,86465],{"class":72}," org.springframework.shell.standard.ShellComponent;\n",[62,86467,86468,86470],{"class":64,"line":76},[62,86469,27875],{"class":68},[62,86471,86472],{"class":72}," org.springframework.shell.standard.ShellMethod;\n",[62,86474,86475],{"class":64,"line":82},[62,86476,79],{"emptyLinePlaceholder":13},[62,86478,86479,86481],{"class":64,"line":89},[62,86480,942],{"class":72},[62,86482,86483],{"class":68},"ShellComponent\n",[62,86485,86486,86488,86490,86493],{"class":64,"line":95},[62,86487,116],{"class":68},[62,86489,119],{"class":68},[62,86491,86492],{"class":122}," HelloCommand",[62,86494,126],{"class":72},[62,86496,86497,86499,86502,86504,86506,86508,86511,86513,86515,86517,86520],{"class":64,"line":101},[62,86498,2143],{"class":72},[62,86500,86501],{"class":68},"ShellMethod",[62,86503,2109],{"class":72},[62,86505,14914],{"class":149},[62,86507,2556],{"class":68},[62,86509,86510],{"class":1675}," \"hello\"",[62,86512,976],{"class":72},[62,86514,2553],{"class":149},[62,86516,2556],{"class":68},[62,86518,86519],{"class":1675}," \"Say hello\"",[62,86521,2212],{"class":72},[62,86523,86524,86526,86528,86530],{"class":64,"line":107},[62,86525,194],{"class":68},[62,86527,2469],{"class":72},[62,86529,82397],{"class":122},[62,86531,206],{"class":72},[62,86533,86534,86536,86539],{"class":64,"line":113},[62,86535,360],{"class":68},[62,86537,86538],{"class":1675}," \"Hello, world!\"",[62,86540,153],{"class":72},[62,86542,86543],{"class":64,"line":129},[62,86544,223],{"class":72},[62,86546,86547],{"class":64,"line":134},[62,86548,79],{"emptyLinePlaceholder":13},[62,86550,86551,86553,86555,86557,86559,86561,86564,86566,86568,86570,86573],{"class":64,"line":156},[62,86552,2143],{"class":72},[62,86554,86501],{"class":68},[62,86556,2109],{"class":72},[62,86558,14914],{"class":149},[62,86560,2556],{"class":68},[62,86562,86563],{"class":1675}," \"goodbye\"",[62,86565,976],{"class":72},[62,86567,2553],{"class":149},[62,86569,2556],{"class":68},[62,86571,86572],{"class":1675}," \"Say goodbye\"",[62,86574,2212],{"class":72},[62,86576,86577,86579,86581,86584],{"class":64,"line":161},[62,86578,194],{"class":68},[62,86580,2469],{"class":72},[62,86582,86583],{"class":122},"goodbye",[62,86585,206],{"class":72},[62,86587,86588,86590,86593],{"class":64,"line":167},[62,86589,360],{"class":68},[62,86591,86592],{"class":1675}," \"Goodbye!\"",[62,86594,153],{"class":72},[62,86596,86597],{"class":64,"line":173},[62,86598,223],{"class":72},[62,86600,86601],{"class":64,"line":179},[62,86602,379],{"class":72},[22,86604,3521,86605,86608,86609,86612,86613,86615,86616,86618,86619,86621],{},[59,86606,86607],{},"@ShellComponent"," annotation indicates that this class contains shell methods (annotated with ",[59,86610,86611],{},"@ShellMethod","). The ",[59,86614,86611],{}," annotation specifies the command name and the description. The ",[59,86617,14914],{}," argument is the command you will use to execute the method and the ",[59,86620,2553],{}," is the description that will appear in the help documentation.",[26,86623,2291],{"id":2290},[22,86625,86626,86627,19931,86630,86633],{},"When you run the Spring Boot application, it will launch the Spring Shell and you can type the command names and execute the ",[59,86628,86629],{},"hello()",[59,86631,86632],{},"goodbye()"," methods directly from the command line.",[52,86635,86637],{"className":1663,"code":86636,"language":1665,"meta":57,"style":57},"$ hello\nHello, world!\n$ goodbye\nGoodbye!\n",[59,86638,86639,86646,86654,86661],{"__ignoreMap":57},[62,86640,86641,86643],{"class":64,"line":65},[62,86642,19949],{"class":122},[62,86644,86645],{"class":1675}," hello\n",[62,86647,86648,86651],{"class":64,"line":76},[62,86649,86650],{"class":122},"Hello,",[62,86652,86653],{"class":1675}," world!\n",[62,86655,86656,86658],{"class":64,"line":82},[62,86657,19949],{"class":122},[62,86659,86660],{"class":1675}," goodbye\n",[62,86662,86663],{"class":64,"line":89},[62,86664,86665],{"class":122},"Goodbye!\n",[636,86667,86669],{"id":86668},"customizing-shell-methods-with-shelloption","Customizing Shell Methods with ShellOption",[22,86671,86672,86673,86676,86677,86679,86680,86682],{},"You can use the ",[59,86674,86675],{},"@ShellOption"," annotation to customize the handling of shell method parameters. For instance, you can add an optional ",[59,86678,3107],{}," parameter to the ",[59,86681,86629],{}," method:",[52,86684,86686],{"className":54,"code":86685,"language":56,"meta":57,"style":57},"import org.springframework.shell.standard.ShellOption;\n\n@ShellMethod(key = \"hello\", value = \"Say hello\")\npublic String hello(@ShellOption(value = \"name\", defaultValue = \"world\") String name) {\n return \"Hello, \" + name + \"!\";\n}\n",[59,86687,86688,86695,86699,86723,86757,86773],{"__ignoreMap":57},[62,86689,86690,86692],{"class":64,"line":65},[62,86691,27875],{"class":68},[62,86693,86694],{"class":72}," org.springframework.shell.standard.ShellOption;\n",[62,86696,86697],{"class":64,"line":76},[62,86698,79],{"emptyLinePlaceholder":13},[62,86700,86701,86703,86705,86707,86709,86711,86713,86715,86717,86719,86721],{"class":64,"line":82},[62,86702,942],{"class":72},[62,86704,86501],{"class":68},[62,86706,2109],{"class":72},[62,86708,14914],{"class":149},[62,86710,2556],{"class":68},[62,86712,86510],{"class":1675},[62,86714,976],{"class":72},[62,86716,2553],{"class":149},[62,86718,2556],{"class":68},[62,86720,86519],{"class":1675},[62,86722,2212],{"class":72},[62,86724,86725,86727,86729,86731,86733,86736,86738,86740,86742,86745,86747,86749,86751,86754],{"class":64,"line":89},[62,86726,116],{"class":68},[62,86728,2469],{"class":72},[62,86730,82397],{"class":122},[62,86732,2475],{"class":72},[62,86734,86735],{"class":68},"ShellOption",[62,86737,2109],{"class":72},[62,86739,2553],{"class":149},[62,86741,2556],{"class":68},[62,86743,86744],{"class":1675}," \"name\"",[62,86746,976],{"class":72},[62,86748,17908],{"class":149},[62,86750,2556],{"class":68},[62,86752,86753],{"class":1675}," \"world\"",[62,86755,86756],{"class":72},") String name) {\n",[62,86758,86759,86761,86763,86765,86767,86769,86771],{"class":64,"line":95},[62,86760,2599],{"class":68},[62,86762,78809],{"class":1675},[62,86764,4507],{"class":68},[62,86766,32157],{"class":72},[62,86768,1148],{"class":68},[62,86770,32162],{"class":1675},[62,86772,153],{"class":72},[62,86774,86775],{"class":64,"line":101},[62,86776,379],{"class":72},[22,86778,86779,86780,86782],{},"Now, when you run the ",[59,86781,82397],{}," command with an argument, it will display a personalized greeting:",[52,86784,86786],{"className":1663,"code":86785,"language":1665,"meta":57,"style":57},"$ hello --name Dan\nHello, Dan!\n",[59,86787,86788,86801],{"__ignoreMap":57},[62,86789,86790,86792,86795,86798],{"class":64,"line":65},[62,86791,19949],{"class":122},[62,86793,86794],{"class":1675}," hello",[62,86796,86797],{"class":149}," --name",[62,86799,86800],{"class":1675}," Dan\n",[62,86802,86803,86805],{"class":64,"line":76},[62,86804,86650],{"class":122},[62,86806,86807],{"class":1675}," Dan!\n",[26,86809,86811],{"id":86810},"fetching-a-random-dad-joke","Fetching a Random Dad Joke",[22,86813,86814,86815,86820],{},"Let's build the main functionality of our command line application: fetching a random dad joke from the ",[677,86816,86819],{"href":86817,"rel":86818},"https://icanhazdadjoke.com/api",[681],"icanhazdadjoke.com"," public API.",[22,86822,86823,86824,86826,86827,1266],{},"Create a new package ",[59,86825,16671],{}," and a new record class ",[59,86828,86829],{},"DadJokeResponse",[52,86831,86833],{"className":54,"code":86832,"language":56,"meta":57,"style":57},"public record DadJokeResponse(String id, String joke, int status) {\n\n}\n",[59,86834,86835,86852,86856],{"__ignoreMap":57},[62,86836,86837,86839,86841,86844,86847,86849],{"class":64,"line":65},[62,86838,116],{"class":68},[62,86840,2996],{"class":68},[62,86842,86843],{"class":122}," DadJokeResponse",[62,86845,86846],{"class":72},"(String id, String joke, ",[62,86848,747],{"class":68},[62,86850,86851],{"class":72}," status) {\n",[62,86853,86854],{"class":64,"line":76},[62,86855,79],{"emptyLinePlaceholder":13},[62,86857,86858],{"class":64,"line":82},[62,86859,379],{"class":72},[22,86861,86823,86862,86865,86866,1266],{},[59,86863,86864],{},"client"," and a new interface ",[59,86867,86868],{},"DadJokeClient",[52,86870,86872],{"className":54,"code":86871,"language":56,"meta":57,"style":57},"package dev.danvega.dadjoke.client;\n\nimport dev.danvega.dadjoke.model.DadJokeResponse;\nimport org.springframework.web.service.annotation.GetExchange;\n\npublic interface DadJokeClient {\n\n @GetExchange(\"/\")\n DadJokeResponse random();\n}\n",[59,86873,86874,86881,86885,86892,86899,86903,86914,86918,86930,86939],{"__ignoreMap":57},[62,86875,86876,86878],{"class":64,"line":65},[62,86877,69],{"class":68},[62,86879,86880],{"class":72}," dev.danvega.dadjoke.client;\n",[62,86882,86883],{"class":64,"line":76},[62,86884,79],{"emptyLinePlaceholder":13},[62,86886,86887,86889],{"class":64,"line":82},[62,86888,27875],{"class":68},[62,86890,86891],{"class":72}," dev.danvega.dadjoke.model.DadJokeResponse;\n",[62,86893,86894,86896],{"class":64,"line":89},[62,86895,27875],{"class":68},[62,86897,86898],{"class":72}," org.springframework.web.service.annotation.GetExchange;\n",[62,86900,86901],{"class":64,"line":95},[62,86902,79],{"emptyLinePlaceholder":13},[62,86904,86905,86907,86909,86912],{"class":64,"line":101},[62,86906,116],{"class":68},[62,86908,8531],{"class":68},[62,86910,86911],{"class":122}," DadJokeClient",[62,86913,126],{"class":72},[62,86915,86916],{"class":64,"line":107},[62,86917,79],{"emptyLinePlaceholder":13},[62,86919,86920,86922,86924,86926,86928],{"class":64,"line":113},[62,86921,2143],{"class":72},[62,86923,39452],{"class":68},[62,86925,2109],{"class":72},[62,86927,15635],{"class":1675},[62,86929,2212],{"class":72},[62,86931,86932,86935,86937],{"class":64,"line":129},[62,86933,86934],{"class":72}," DadJokeResponse ",[62,86936,41143],{"class":122},[62,86938,822],{"class":72},[62,86940,86941],{"class":64,"line":134},[62,86942,379],{"class":72},[22,86944,86945,86946,86949],{},"Update the ",[59,86947,86948],{},"DadJokeCommands"," class to fetch a random dad joke and output it to the command line:",[52,86951,86953],{"className":54,"code":86952,"language":56,"meta":57,"style":57},"import org.springframework.shell.standard.ShellMethod;\nimport org.springframework.shell.standard.ShellComponent;\nimport org.springframework.beans.factory.annotation.Autowired;\n\n@ShellComponent\npublic class DadJokeCommands {\n\n private final DadJokeClient dadJokeClient;\n\n @Autowired\n public DadJokeCommands(DadJokeClient dadJokeClient) {\n this.dadJokeClient = dadJokeClient;\n }\n\n @ShellMethod(key = \"random\", value = \"Get a random dad joke\")\n public String getRandomDadJoke() {\n DadJokeResponse response = dadJokeClient.random();\n return response.joke();\n }\n}\n",[59,86954,86955,86961,86967,86974,86978,86984,86995,86999,87008,87012,87018,87032,87044,87048,87052,87077,87088,87102,87112,87116],{"__ignoreMap":57},[62,86956,86957,86959],{"class":64,"line":65},[62,86958,27875],{"class":68},[62,86960,86472],{"class":72},[62,86962,86963,86965],{"class":64,"line":76},[62,86964,27875],{"class":68},[62,86966,86465],{"class":72},[62,86968,86969,86971],{"class":64,"line":82},[62,86970,27875],{"class":68},[62,86972,86973],{"class":72}," org.springframework.beans.factory.annotation.Autowired;\n",[62,86975,86976],{"class":64,"line":89},[62,86977,79],{"emptyLinePlaceholder":13},[62,86979,86980,86982],{"class":64,"line":95},[62,86981,942],{"class":72},[62,86983,86483],{"class":68},[62,86985,86986,86988,86990,86993],{"class":64,"line":101},[62,86987,116],{"class":68},[62,86989,119],{"class":68},[62,86991,86992],{"class":122}," DadJokeCommands",[62,86994,126],{"class":72},[62,86996,86997],{"class":64,"line":107},[62,86998,79],{"emptyLinePlaceholder":13},[62,87000,87001,87003,87005],{"class":64,"line":113},[62,87002,137],{"class":68},[62,87004,458],{"class":68},[62,87006,87007],{"class":72}," DadJokeClient dadJokeClient;\n",[62,87009,87010],{"class":64,"line":129},[62,87011,79],{"emptyLinePlaceholder":13},[62,87013,87014,87016],{"class":64,"line":134},[62,87015,2143],{"class":72},[62,87017,11687],{"class":68},[62,87019,87020,87022,87024,87027,87030],{"class":64,"line":156},[62,87021,194],{"class":68},[62,87023,86992],{"class":122},[62,87025,87026],{"class":72},"(DadJokeClient ",[62,87028,87029],{"class":889},"dadJokeClient",[62,87031,768],{"class":72},[62,87033,87034,87036,87039,87041],{"class":64,"line":161},[62,87035,2405],{"class":149},[62,87037,87038],{"class":72},".dadJokeClient ",[62,87040,146],{"class":68},[62,87042,87043],{"class":72}," dadJokeClient;\n",[62,87045,87046],{"class":64,"line":167},[62,87047,223],{"class":72},[62,87049,87050],{"class":64,"line":173},[62,87051,79],{"emptyLinePlaceholder":13},[62,87053,87054,87056,87058,87060,87062,87064,87066,87068,87070,87072,87075],{"class":64,"line":179},[62,87055,2143],{"class":72},[62,87057,86501],{"class":68},[62,87059,2109],{"class":72},[62,87061,14914],{"class":149},[62,87063,2556],{"class":68},[62,87065,41110],{"class":1675},[62,87067,976],{"class":72},[62,87069,2553],{"class":149},[62,87071,2556],{"class":68},[62,87073,87074],{"class":1675}," \"Get a random dad joke\"",[62,87076,2212],{"class":72},[62,87078,87079,87081,87083,87086],{"class":64,"line":185},[62,87080,194],{"class":68},[62,87082,2469],{"class":72},[62,87084,87085],{"class":122},"getRandomDadJoke",[62,87087,206],{"class":72},[62,87089,87090,87093,87095,87098,87100],{"class":64,"line":191},[62,87091,87092],{"class":72}," DadJokeResponse response ",[62,87094,146],{"class":68},[62,87096,87097],{"class":72}," dadJokeClient.",[62,87099,41143],{"class":122},[62,87101,822],{"class":72},[62,87103,87104,87106,87108,87110],{"class":64,"line":209},[62,87105,360],{"class":68},[62,87107,12760],{"class":72},[62,87109,35122],{"class":122},[62,87111,822],{"class":72},[62,87113,87114],{"class":64,"line":220},[62,87115,223],{"class":72},[62,87117,87118],{"class":64,"line":226},[62,87119,379],{"class":72},[22,87121,87122,87123],{},"Ask Spring to turn that interface into an implementation at runtime by using the ",[59,87124,87125],{},"HttpServiceProxyFactory",[52,87127,87129],{"className":54,"code":87128,"language":56,"meta":57,"style":57},"@SpringBootApplication\npublic class Application {\n\n public static void main(String[] args) {\n SpringApplication.run(Application.class, args);\n }\n\n @Bean\n DadJokeClient dadJokeClient() {\n WebClient client = WebClient.builder()\n .baseUrl(\"https://icanhazdadjoke.com\")\n .defaultHeader(\"Accept\",\"application/json\")\n .build();\n\n HttpServiceProxyFactory factory = HttpServiceProxyFactory\n .builder(WebClientAdapter.forClient(client))\n .build();\n return factory.createClient(DadJokeClient.class);\n }\n}\n",[59,87130,87131,87137,87147,87151,87171,87179,87183,87187,87193,87202,87214,87227,87245,87253,87257,87265,87277,87285,87296,87300],{"__ignoreMap":57},[62,87132,87133,87135],{"class":64,"line":65},[62,87134,942],{"class":72},[62,87136,2079],{"class":68},[62,87138,87139,87141,87143,87145],{"class":64,"line":76},[62,87140,116],{"class":68},[62,87142,119],{"class":68},[62,87144,2088],{"class":122},[62,87146,126],{"class":72},[62,87148,87149],{"class":64,"line":82},[62,87150,79],{"emptyLinePlaceholder":13},[62,87152,87153,87155,87157,87159,87161,87163,87165,87167,87169],{"class":64,"line":89},[62,87154,194],{"class":68},[62,87156,2101],{"class":68},[62,87158,200],{"class":68},[62,87160,2106],{"class":122},[62,87162,2109],{"class":72},[62,87164,973],{"class":68},[62,87166,2114],{"class":72},[62,87168,2117],{"class":889},[62,87170,768],{"class":72},[62,87172,87173,87175,87177],{"class":64,"line":95},[62,87174,2124],{"class":72},[62,87176,2127],{"class":122},[62,87178,2130],{"class":72},[62,87180,87181],{"class":64,"line":101},[62,87182,223],{"class":72},[62,87184,87185],{"class":64,"line":107},[62,87186,79],{"emptyLinePlaceholder":13},[62,87188,87189,87191],{"class":64,"line":113},[62,87190,2143],{"class":72},[62,87192,2146],{"class":68},[62,87194,87195,87198,87200],{"class":64,"line":129},[62,87196,87197],{"class":72}," DadJokeClient ",[62,87199,87029],{"class":122},[62,87201,206],{"class":72},[62,87203,87204,87206,87208,87210,87212],{"class":64,"line":134},[62,87205,47811],{"class":72},[62,87207,146],{"class":68},[62,87209,47816],{"class":72},[62,87211,2160],{"class":122},[62,87213,2223],{"class":72},[62,87215,87216,87218,87220,87222,87225],{"class":64,"line":156},[62,87217,2418],{"class":72},[62,87219,11188],{"class":122},[62,87221,2109],{"class":72},[62,87223,87224],{"class":1675},"\"https://icanhazdadjoke.com\"",[62,87226,2212],{"class":72},[62,87228,87229,87231,87234,87236,87239,87241,87243],{"class":64,"line":161},[62,87230,2418],{"class":72},[62,87232,87233],{"class":122},"defaultHeader",[62,87235,2109],{"class":72},[62,87237,87238],{"class":1675},"\"Accept\"",[62,87240,32225],{"class":72},[62,87242,18356],{"class":1675},[62,87244,2212],{"class":72},[62,87246,87247,87249,87251],{"class":64,"line":167},[62,87248,2418],{"class":72},[62,87250,2189],{"class":122},[62,87252,822],{"class":72},[62,87254,87255],{"class":64,"line":173},[62,87256,79],{"emptyLinePlaceholder":13},[62,87258,87259,87261,87263],{"class":64,"line":179},[62,87260,45188],{"class":72},[62,87262,146],{"class":68},[62,87264,45193],{"class":72},[62,87266,87267,87269,87271,87273,87275],{"class":64,"line":185},[62,87268,2610],{"class":72},[62,87270,2160],{"class":122},[62,87272,47854],{"class":72},[62,87274,47857],{"class":122},[62,87276,45208],{"class":72},[62,87278,87279,87281,87283],{"class":64,"line":191},[62,87280,2610],{"class":72},[62,87282,2189],{"class":122},[62,87284,822],{"class":72},[62,87286,87287,87289,87291,87293],{"class":64,"line":209},[62,87288,360],{"class":68},[62,87290,45223],{"class":72},[62,87292,45226],{"class":122},[62,87294,87295],{"class":72},"(DadJokeClient.class);\n",[62,87297,87298],{"class":64,"line":220},[62,87299,223],{"class":72},[62,87301,87302],{"class":64,"line":226},[62,87303,379],{"class":72},[22,87305,87306,87307,87309],{},"Run the Spring Boot application and test the ",[59,87308,41143],{}," command:",[52,87311,87313],{"className":1663,"code":87312,"language":1665,"meta":57,"style":57},"$ random\nWhy don't scientists trust atoms? Because they make up everything.\n",[59,87314,87315,87322],{"__ignoreMap":57},[62,87316,87317,87319],{"class":64,"line":65},[62,87318,19949],{"class":122},[62,87320,87321],{"class":1675}," random\n",[62,87323,87324,87327],{"class":64,"line":76},[62,87325,87326],{"class":122},"Why",[62,87328,87329],{"class":1675}," don't scientists trust atoms? Because they make up everything.\n",[26,87331,87333],{"id":87332},"building-a-native-application","Building a Native Application",[22,87335,87336,87337,19931,87340,87342,87343,87345],{},"To make our command line application fast and portable, let's build a native application using GraalVM. Configure ",[59,87338,87339],{},"Logback",[59,87341,2925],{}," properties in ",[59,87344,1265],{}," to remove unnecessary logging and disable the Spring Boot banner:",[52,87347,87349],{"className":1269,"code":87348,"language":1271,"meta":57,"style":57},"logging.level.root=off\nspring.main.banner-mode=off\n",[59,87350,87351,87358],{"__ignoreMap":57},[62,87352,87353,87356],{"class":64,"line":65},[62,87354,87355],{"class":68},"logging.level.root",[62,87357,3588],{"class":72},[62,87359,87360,87362],{"class":64,"line":76},[62,87361,3585],{"class":68},[62,87363,3588],{"class":72},[22,87365,87366],{},"Build the native application using Maven:",[52,87368,87370],{"className":1663,"code":87369,"language":1665,"meta":57,"style":57},"$ ./mvnw -Pnative -DskipTests=true clean package\n",[59,87371,87372],{"__ignoreMap":57},[62,87373,87374,87376,87379,87382,87385,87387],{"class":64,"line":65},[62,87375,19949],{"class":122},[62,87377,87378],{"class":1675}," ./mvnw",[62,87380,87381],{"class":149}," -Pnative",[62,87383,87384],{"class":149}," -DskipTests=true",[62,87386,3642],{"class":1675},[62,87388,3645],{"class":1675},[22,87390,87391,87392,24909],{},"The native application will be generated in the ",[59,87393,3651],{},[26,87395,87397],{"id":87396},"using-the-native-application","Using the Native Application",[22,87399,87400,87401,34867,87404,15770],{},"Create an alias for the native application in your shell configuration file (e.g., ",[59,87402,87403],{},".bashrc",[59,87405,87406],{},".zshrc",[52,87408,87410],{"className":1663,"code":87409,"language":1665,"meta":57,"style":57},"alias dadjoke=\"path/to/your/target/DadJoke\"\n",[59,87411,87412],{"__ignoreMap":57},[62,87413,87414,87417,87420,87422],{"class":64,"line":65},[62,87415,87416],{"class":68},"alias",[62,87418,87419],{"class":72}," dadjoke",[62,87421,146],{"class":68},[62,87423,87424],{"class":1675},"\"path/to/your/target/DadJoke\"\n",[34,87426,87427],{},[37,87428,87429,87430,87433],{},"Run the native application by typing ",[59,87431,87432],{},"dadjoke"," in your command line:",[52,87435,87437],{"className":1663,"code":87436,"language":1665,"meta":57,"style":57},"$ dadjoke\nDad Joke> random\nWhy don't some couples go to the gym? Because some relationships don't work out.\n",[59,87438,87439,87446,87460],{"__ignoreMap":57},[62,87440,87441,87443],{"class":64,"line":65},[62,87442,19949],{"class":122},[62,87444,87445],{"class":1675}," dadjoke\n",[62,87447,87448,87451,87454,87456,87458],{"class":64,"line":76},[62,87449,87450],{"class":122},"Dad",[62,87452,87453],{"class":1675}," Jok",[62,87455,890],{"class":72},[62,87457,2583],{"class":68},[62,87459,87321],{"class":1675},[62,87461,87462,87464,87467,87470],{"class":64,"line":82},[62,87463,87326],{"class":122},[62,87465,87466],{"class":1675}," don't some couples go to the gym? Because some relationships don't",[62,87468,87469],{"class":1675}," work",[62,87471,87472],{"class":1675}," out.\n",[22,87474,87475],{},"Now you can enjoy random dad jokes anytime you want right from your command line!",[26,87477,1499],{"id":1498},[22,87479,87480],{},"In this tutorial, we built a command line application using Spring Shell that fetches random dad jokes from a public API. We demonstrated how to create shell commands, fetch data from an API, and build a native application using GraalVM for fast startup times.",[22,87482,87483],{},"As you can see, Spring Shell is a powerful tool that allows you to build command line applications using the familiar Spring Boot environment. Give it a try for your next CLI project!",[1527,87485,87486],{},"html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .spoOn, html code.shiki .spoOn{--shiki-default:#E36209;--shiki-dark:#FFAB70;--shiki-sepia:#E36209}",{"title":57,"searchDepth":76,"depth":76,"links":87488},[87489,87490,87491,87494,87495,87496,87497],{"id":86395,"depth":76,"text":86396},{"id":86433,"depth":76,"text":86434},{"id":2290,"depth":76,"text":2291,"children":87492},[87493],{"id":86668,"depth":82,"text":86669},{"id":86810,"depth":76,"text":86811},{"id":87332,"depth":76,"text":87333},{"id":87396,"depth":76,"text":87397},{"id":1498,"depth":76,"text":1499},{"_id":87499,"path":87500,"title":87501,"description":73331,"meta":87502,"body":87508},"content/blog/2023/02/03/native-images-graalvm.md","/blog/2023/02/03/native-images-graalvm","Building Native Images in Java with GraalVM",{"slug":87503,"date":87504,"published":13,"tags":87505,"author":17,"cover":87507,"excerpt":-1},"native-images-graalvm","2023-02-03T10:00:00.000Z",[56,87506],"graalvm","./graalvm-dynamic-new.png",{"type":19,"value":87509,"toc":88032},[87510,87513,87515,87518,87522,87529,87533,87542,87548,87578,87615,87680,87745,87755,87878,87888,87892,87898,87901,87914,87920,87936,87954,88017,88025,88027,88030],[22,87511,87512],{},"When building native executables with GraalVM, you might run into issues when your application uses dynamic language features of the JVM. This article will walk you through a practical example of how to work with these types of features and provide metadata to GraalVM's Native Image Builder.",[26,87514,62931],{"id":62930},[22,87516,87517],{},"When you build a native executable without any dynamic language features, everything works as expected. However, when you introduce dynamic features like reflection, you need to instruct GraalVM where the different dynamic features in your application are.",[26,87519,87521],{"id":87520},"graalvm-reachability-metadata","GraalVM Reachability Metadata",[22,87523,87524,87525,87528],{},"GraalVM provides a way to include metadata about the dynamic features of your JVM application. You can supply metadata to the Native Image Builder by providing JSON files stored in the ",[59,87526,87527],{},"META-INF/native-image"," directory. This article will demonstrate this approach using a sample Java project.",[26,87530,87532],{"id":87531},"creating-a-java-project-with-dynamic-features","Creating a Java Project with Dynamic Features",[22,87534,87535,87536,87541],{},"First, create a new Java project with Maven and the GraalVM Native Maven plugin. In this example, the project will use Java, Maven, and the ",[677,87537,87540],{"href":87538,"rel":87539},"https://www.graalvm.org/",[681],"GraalVM JDK 17.22.3",". Create a simple \"Hello World\" application with no dynamic features to start.",[22,87543,87544],{},[653,87545],{"alt":87546,"src":87547},"Create new Java Project in IntelliJ","/images/blog/2023/02/03/intellij-new-project.png",[22,87549,87550,87551,87554,87555,19931,87558,87561,87562,87564,87565,87561,87568,19931,87570,87572,87573,87575,87576,22831],{},"Next, introduce dynamic capabilities in the form of a ",[59,87552,87553],{},"Message"," interface, with two implementations: ",[59,87556,87557],{},"NiceMessage",[59,87559,87560],{},"MeanMessage",". The ",[59,87563,87553],{}," interface will have a single method called ",[59,87566,87567],{},"printMessage()",[59,87569,87557],{},[59,87571,87560],{}," classes will each implement the ",[59,87574,87553],{}," interface with their versions of the ",[59,87577,87567],{},[52,87579,87581],{"className":54,"code":87580,"language":56,"meta":57,"style":57},"public interface Message {\n\n void printMessage();\n\n}\n",[59,87582,87583,87594,87598,87607,87611],{"__ignoreMap":57},[62,87584,87585,87587,87589,87592],{"class":64,"line":65},[62,87586,116],{"class":68},[62,87588,8531],{"class":68},[62,87590,87591],{"class":122}," Message",[62,87593,126],{"class":72},[62,87595,87596],{"class":64,"line":76},[62,87597,79],{"emptyLinePlaceholder":13},[62,87599,87600,87602,87605],{"class":64,"line":82},[62,87601,11710],{"class":68},[62,87603,87604],{"class":122}," printMessage",[62,87606,822],{"class":72},[62,87608,87609],{"class":64,"line":89},[62,87610,79],{"emptyLinePlaceholder":13},[62,87612,87613],{"class":64,"line":95},[62,87614,379],{"class":72},[52,87616,87618],{"className":54,"code":87617,"language":56,"meta":57,"style":57},"public class NiceMessage implements Message {\n\n @Override\n public void printMessage() {\n System.out.println(\"This is a nice message!\");\n }\n\n}\n",[59,87619,87620,87635,87639,87645,87655,87668,87672,87676],{"__ignoreMap":57},[62,87621,87622,87624,87626,87629,87631,87633],{"class":64,"line":65},[62,87623,116],{"class":68},[62,87625,119],{"class":68},[62,87627,87628],{"class":122}," NiceMessage",[62,87630,13520],{"class":68},[62,87632,87591],{"class":122},[62,87634,126],{"class":72},[62,87636,87637],{"class":64,"line":76},[62,87638,79],{"emptyLinePlaceholder":13},[62,87640,87641,87643],{"class":64,"line":82},[62,87642,2143],{"class":72},[62,87644,13555],{"class":68},[62,87646,87647,87649,87651,87653],{"class":64,"line":89},[62,87648,194],{"class":68},[62,87650,200],{"class":68},[62,87652,87604],{"class":122},[62,87654,206],{"class":72},[62,87656,87657,87659,87661,87663,87666],{"class":64,"line":95},[62,87658,27297],{"class":72},[62,87660,2244],{"class":122},[62,87662,2109],{"class":72},[62,87664,87665],{"class":1675},"\"This is a nice message!\"",[62,87667,1133],{"class":72},[62,87669,87670],{"class":64,"line":101},[62,87671,223],{"class":72},[62,87673,87674],{"class":64,"line":107},[62,87675,79],{"emptyLinePlaceholder":13},[62,87677,87678],{"class":64,"line":113},[62,87679,379],{"class":72},[52,87681,87683],{"className":54,"code":87682,"language":56,"meta":57,"style":57},"public class MeanMessage implements Message {\n\n @Override\n public void printMessage() {\n System.out.println(\"This is a mean message!\");\n }\n\n}\n",[59,87684,87685,87700,87704,87710,87720,87733,87737,87741],{"__ignoreMap":57},[62,87686,87687,87689,87691,87694,87696,87698],{"class":64,"line":65},[62,87688,116],{"class":68},[62,87690,119],{"class":68},[62,87692,87693],{"class":122}," MeanMessage",[62,87695,13520],{"class":68},[62,87697,87591],{"class":122},[62,87699,126],{"class":72},[62,87701,87702],{"class":64,"line":76},[62,87703,79],{"emptyLinePlaceholder":13},[62,87705,87706,87708],{"class":64,"line":82},[62,87707,2143],{"class":72},[62,87709,13555],{"class":68},[62,87711,87712,87714,87716,87718],{"class":64,"line":89},[62,87713,194],{"class":68},[62,87715,200],{"class":68},[62,87717,87604],{"class":122},[62,87719,206],{"class":72},[62,87721,87722,87724,87726,87728,87731],{"class":64,"line":95},[62,87723,27297],{"class":72},[62,87725,2244],{"class":122},[62,87727,2109],{"class":72},[62,87729,87730],{"class":1675},"\"This is a mean message!\"",[62,87732,1133],{"class":72},[62,87734,87735],{"class":64,"line":101},[62,87736,223],{"class":72},[62,87738,87739],{"class":64,"line":107},[62,87740,79],{"emptyLinePlaceholder":13},[62,87742,87743],{"class":64,"line":113},[62,87744,379],{"class":72},[22,87746,87747,87748,34867,87750,87752,87753,22831],{},"The main class of the application will dynamically invoke either the ",[59,87749,87557],{},[59,87751,87560],{}," implementation based on a program argument. To accomplish this, the main class will use reflection to dynamically load and call the target implementation's ",[59,87754,87567],{},[52,87756,87758],{"className":54,"code":87757,"language":56,"meta":57,"style":57},"public class Application {\n\n public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException,\n InvocationTargetException, InstantiationException, IllegalAccessException {\n Class\u003C?> clazz = Class.forName(\"dev.danvega.\" + args[0]);\n clazz.getMethod(\"printMessage\").invoke(clazz.getConstructor().newInstance());\n }\n\n}\n",[59,87759,87760,87770,87774,87799,87804,87837,87866,87870,87874],{"__ignoreMap":57},[62,87761,87762,87764,87766,87768],{"class":64,"line":65},[62,87763,116],{"class":68},[62,87765,119],{"class":68},[62,87767,2088],{"class":122},[62,87769,126],{"class":72},[62,87771,87772],{"class":64,"line":76},[62,87773,79],{"emptyLinePlaceholder":13},[62,87775,87776,87778,87780,87782,87784,87786,87788,87790,87792,87794,87796],{"class":64,"line":82},[62,87777,194],{"class":68},[62,87779,2101],{"class":68},[62,87781,200],{"class":68},[62,87783,2106],{"class":122},[62,87785,2109],{"class":72},[62,87787,973],{"class":68},[62,87789,2114],{"class":72},[62,87791,2117],{"class":889},[62,87793,5024],{"class":72},[62,87795,11501],{"class":68},[62,87797,87798],{"class":72}," ClassNotFoundException, NoSuchMethodException,\n",[62,87800,87801],{"class":64,"line":89},[62,87802,87803],{"class":72}," InvocationTargetException, InstantiationException, IllegalAccessException {\n",[62,87805,87806,87809,87811,87814,87816,87819,87822,87824,87827,87829,87832,87834],{"class":64,"line":95},[62,87807,87808],{"class":72}," Class\u003C",[62,87810,5668],{"class":68},[62,87812,87813],{"class":72},"> clazz ",[62,87815,146],{"class":68},[62,87817,87818],{"class":72}," Class.",[62,87820,87821],{"class":122},"forName",[62,87823,2109],{"class":72},[62,87825,87826],{"class":1675},"\"dev.danvega.\"",[62,87828,4507],{"class":68},[62,87830,87831],{"class":72}," args[",[62,87833,1130],{"class":149},[62,87835,87836],{"class":72},"]);\n",[62,87838,87839,87842,87844,87846,87849,87851,87854,87857,87860,87862,87864],{"class":64,"line":101},[62,87840,87841],{"class":72}," clazz.",[62,87843,13290],{"class":122},[62,87845,2109],{"class":72},[62,87847,87848],{"class":1675},"\"printMessage\"",[62,87850,15503],{"class":72},[62,87852,87853],{"class":122},"invoke",[62,87855,87856],{"class":72},"(clazz.",[62,87858,87859],{"class":122},"getConstructor",[62,87861,3229],{"class":72},[62,87863,27146],{"class":122},[62,87865,1091],{"class":72},[62,87867,87868],{"class":64,"line":107},[62,87869,223],{"class":72},[62,87871,87872],{"class":64,"line":113},[62,87873,79],{"emptyLinePlaceholder":13},[62,87875,87876],{"class":64,"line":129},[62,87877,379],{"class":72},[22,87879,87880,87881,87883,87884,87887],{},"When you run the application with your desired argument (e.g., ",[59,87882,87557],{},"), it will work correctly. However, when you attempt to build a native image with GraalVM, it will fail at runtime with a ",[59,87885,87886],{},"ClassNotFoundException",". This is because GraalVM does not include the dynamic classes in the final binary since it cannot discover them through static analysis.",[26,87889,87891],{"id":87890},"using-a-tracing-agent","Using a Tracing Agent",[22,87893,87894,87895,87897],{},"To resolve this issue, you can use a tracing agent to supply metadata about the dynamic features in your application. The tracing agent is a JVM command-line option that you can use when running your application. It generates JSON configuration files in the ",[59,87896,87527],{}," directory to inform GraalVM's Native Image Builder about the dynamically reachable classes and methods.",[22,87899,87900],{},"For example, in IntelliJ IDEA, go to the \"Edit Configurations\" menu and add the following VM option:",[52,87902,87904],{"className":32625,"code":87903,"language":32627,"meta":57,"style":57},"-agentlib:native-image-agent=config-output-dir=src/main/resources/META-INF/native-image\n",[59,87905,87906],{"__ignoreMap":57},[62,87907,87908,87911],{"class":64,"line":65},[62,87909,87910],{"class":122},"-agentlib:native-image-agent",[62,87912,87913],{"class":1675},"=config-output-dir=src/main/resources/META-INF/native-image\n",[22,87915,87916],{},[653,87917],{"alt":87918,"src":87919},"VM Options in IntelliJ","/images/blog/2023/02/03/vm-options.png",[22,87921,87922,87923,87926,87927,87930,87931,19931,87933,87935],{},"Now, when you run your application with the tracing agent enabled, GraalVM will generate the required configuration files, such as ",[59,87924,87925],{},"reflect-config.json",", in the specified output directory (",[59,87928,87929],{},"src/main/resources/META-INF/native-image","). These files tell GraalVM about the dynamically reachable classes and methods, such as the ",[59,87932,87557],{},[59,87934,87560],{}," implementations.",[22,87937,87938,87939,87941,87942,87944,87945,87947,87948,87950,87951,87953],{},"However, as you might have noticed, the generated configuration files only include information about the ",[59,87940,87557],{}," class since that was the only class dynamically invoked during application runtime with the tracing agent enabled. To add the ",[59,87943,87560],{}," class, you can either run the application again with the ",[59,87946,87560],{}," argument and the tracing agent enabled, or you can manually edit the ",[59,87949,87925],{}," file and add a new entry for the ",[59,87952,87560],{}," class, like this:",[52,87955,87957],{"className":3671,"code":87956,"language":3673,"meta":57,"style":57},"{\n \"name\": \"dev.danvega.MeanMessage\",\n \"methods\": [\n {\n \"name\": \"printMessage\",\n \"parameterTypes\": []\n }\n ]\n}\n",[59,87958,87959,87963,87975,87982,87986,87997,88005,88009,88013],{"__ignoreMap":57},[62,87960,87961],{"class":64,"line":65},[62,87962,3680],{"class":72},[62,87964,87965,87968,87970,87973],{"class":64,"line":76},[62,87966,87967],{"class":149}," \"name\"",[62,87969,3696],{"class":72},[62,87971,87972],{"class":1675},"\"dev.danvega.MeanMessage\"",[62,87974,3338],{"class":72},[62,87976,87977,87980],{"class":64,"line":82},[62,87978,87979],{"class":149}," \"methods\"",[62,87981,3709],{"class":72},[62,87983,87984],{"class":64,"line":89},[62,87985,49857],{"class":72},[62,87987,87988,87991,87993,87995],{"class":64,"line":95},[62,87989,87990],{"class":149}," \"name\"",[62,87992,3696],{"class":72},[62,87994,87848],{"class":1675},[62,87996,3338],{"class":72},[62,87998,87999,88002],{"class":64,"line":101},[62,88000,88001],{"class":149}," \"parameterTypes\"",[62,88003,88004],{"class":72},": []\n",[62,88006,88007],{"class":64,"line":107},[62,88008,223],{"class":72},[62,88010,88011],{"class":64,"line":113},[62,88012,7661],{"class":72},[62,88014,88015],{"class":64,"line":129},[62,88016,379],{"class":72},[22,88018,88019,88020,19931,88022,88024],{},"After updating the configuration files, rebuild the native image with GraalVM. This time, you'll be able to successfully run the native image with both ",[59,88021,87557],{},[59,88023,87560],{}," arguments.",[26,88026,1499],{"id":1498},[22,88028,88029],{},"In this article, we created a Java project with dynamic features and learned how to provide metadata to GraalVM's Native Image Builder using JSON configuration files generated by a tracing agent. This approach helps GraalVM understand the dynamic features of your JVM application and build native images accordingly. By following these steps, you can ensure that your dynamic JVM applications can be successfully compiled to native executables using GraalVM.",[1527,88031,81537],{},{"title":57,"searchDepth":76,"depth":76,"links":88033},[88034,88035,88036,88037,88038],{"id":62930,"depth":76,"text":62931},{"id":87520,"depth":76,"text":87521},{"id":87531,"depth":76,"text":87532},{"id":87890,"depth":76,"text":87891},{"id":1498,"depth":76,"text":1499},{"_id":88040,"path":88041,"title":88042,"description":73331,"meta":88043,"body":88048},"content/blog/2023/01/31/graphql-custom-scalars.md","/blog/2023/01/31/graphql-custom-scalars","GraphQL Scalars - How to use Custom Scalars in Java",{"slug":88044,"date":88045,"published":13,"tags":88046,"author":17,"cover":88047,"excerpt":-1},"graphql-custom-scalars","2023-01-31T08:00:00.000Z",[2925,8507],"./graphql-scalars-thumbnail.png",{"type":19,"value":88049,"toc":89086},[88050,88065,88071,88074,88077,88081,88084,88092,88097,88100,88104,88118,88184,88187,88223,88226,88488,88492,88495,88518,88521,88561,88565,88568,88675,88678,88682,88693,88772,88775,88778,88782,88798,88872,88875,88885,88909,88912,88989,89060,89069,89072,89074,89077,89080,89083],[22,88051,88052,88053,976,88055,976,88057,976,88060,4201,88062,88064],{},"Whether you're new to GraphQL or a seasoned user, understanding the scalar types in GraphQL is important. Within the GraphQL specification, there are five defined scalar types: ",[646,88054,747],{},[646,88056,76477],{},[646,88058,88059],{},"string",[646,88061,76509],{},[646,88063,8845],{},". These will work for many components of your schema definition. But what happens when you need a custom scalar? Unfortunately, you'll inevitably need to create a custom scalar at some point.",[22,88066,88067],{},[653,88068],{"alt":88069,"src":88070},"GraphQL Scalar Types","/images/blog/2023/01/31/graphql-scalar-types.png",[22,88072,88073],{},"That's what we'll be doing today. We're going to create a new Spring Boot 3 project that includes Spring for GraphQL. We'll be defining our schema and integrating built-in scalars. Then, we'll discuss how to bring in custom scalars when needed.",[22,88075,88076],{},"So, let's dive into some code.",[26,88078,88080],{"id":88079},"setting-up-a-new-project-with-graphql","Setting Up a New Project with GraphQL",[22,88082,88083],{},"For those of you who have built a Spring Boot application, you'll be familiar with the process of how we set up a new project.",[22,88085,88086,88087,19931,88089,2755],{},"We'll start by heading to start.spring.io, where we'll input our project details. I'm going to use Maven for the project type, with Java as the language. As for dependencies, we're going to include ",[646,88088,1756],{},[646,88090,88091],{},"Spring for GraphQL",[22,88093,88094],{},[653,88095],{"alt":24606,"src":88096},"/images/blog/2023/01/31/scalars-spring-init.png",[22,88098,88099],{},"Once our metadata is filled in, we can hit Generate, which will download a zip file. Open this in your preferred IDE - I typically use IntelliJ Ultimate.",[26,88101,88103],{"id":88102},"create-a-model","Create a Model",[22,88105,88106,88107,19931,88109,88111,88112,88114,88115,88117],{},"Now that our application is set up and ready to go, the first thing we're going to do is create a new model named Product. We'll give it the properties ",[646,88108,6283],{},[646,88110,3196],{},", with ",[646,88113,6283],{}," being an integer and ",[646,88116,3196],{}," a string.",[52,88119,88121],{"className":54,"code":88120,"language":56,"meta":57,"style":57},"@Entity\npublic class Product {\n\n @Id\n @GeneratedValue\n private Integer id;\n private String title;\n\n // constructors, getters, setters and toSring\n\n}\n",[59,88122,88123,88129,88139,88143,88149,88155,88161,88167,88171,88176,88180],{"__ignoreMap":57},[62,88124,88125,88127],{"class":64,"line":65},[62,88126,942],{"class":72},[62,88128,8999],{"class":68},[62,88130,88131,88133,88135,88137],{"class":64,"line":76},[62,88132,116],{"class":68},[62,88134,119],{"class":68},[62,88136,9584],{"class":122},[62,88138,126],{"class":72},[62,88140,88141],{"class":64,"line":82},[62,88142,79],{"emptyLinePlaceholder":13},[62,88144,88145,88147],{"class":64,"line":89},[62,88146,2143],{"class":72},[62,88148,22298],{"class":68},[62,88150,88151,88153],{"class":64,"line":95},[62,88152,2143],{"class":72},[62,88154,9022],{"class":68},[62,88156,88157,88159],{"class":64,"line":101},[62,88158,137],{"class":68},[62,88160,45372],{"class":72},[62,88162,88163,88165],{"class":64,"line":107},[62,88164,137],{"class":68},[62,88166,9036],{"class":72},[62,88168,88169],{"class":64,"line":113},[62,88170,79],{"emptyLinePlaceholder":13},[62,88172,88173],{"class":64,"line":129},[62,88174,88175],{"class":85}," // constructors, getters, setters and toSring\n",[62,88177,88178],{"class":64,"line":134},[62,88179,79],{"emptyLinePlaceholder":13},[62,88181,88182],{"class":64,"line":156},[62,88183,379],{"class":72},[22,88185,88186],{},"Next, we'll create a repository package to be able to fetch or persist data from the database.",[52,88188,88190],{"className":54,"code":88189,"language":56,"meta":57,"style":57},"public interface ProductRepository extends ListCrudRepository\u003CProduct, Integer> {\n\n}\n",[59,88191,88192,88215,88219],{"__ignoreMap":57},[62,88193,88194,88196,88198,88201,88203,88205,88207,88209,88211,88213],{"class":64,"line":65},[62,88195,116],{"class":68},[62,88197,8531],{"class":68},[62,88199,88200],{"class":122}," ProductRepository",[62,88202,8537],{"class":68},[62,88204,45494],{"class":122},[62,88206,760],{"class":72},[62,88208,4044],{"class":68},[62,88210,976],{"class":72},[62,88212,979],{"class":68},[62,88214,8552],{"class":72},[62,88216,88217],{"class":64,"line":76},[62,88218,79],{"emptyLinePlaceholder":13},[62,88220,88221],{"class":64,"line":82},[62,88222,379],{"class":72},[22,88224,88225],{},"In order to actually pull this data, we'll create a Command Line Runner which will include a few new products that we can later test.",[52,88227,88229],{"className":54,"code":88228,"language":56,"meta":57,"style":57},"@SpringBootApplication\npublic class Application {\n\n public static void main(String[] args) {\n SpringApplication.run(Application.class, args);\n }\n\n @Bean\n CommandLineRunner commandLineRunner(ProductRepository repository) {\n return args -> {\n List\u003CProduct> products = List.of(\n new Product(\"Product 1\",true,1.99F,new BigDecimal(9.99),LocalDateTime.now()),\n new Product(\"Product 2\",false, 3.99F,new BigDecimal(9.99),LocalDateTime.now()),\n new Product(\"Product 3\", true, 19.99F,new BigDecimal(9.99),LocalDateTime.now())\n );\n repository.saveAll(products);\n repository.findAll().forEach(System.out::println);\n };\n }\n\n}\n",[59,88230,88231,88237,88247,88251,88271,88279,88283,88287,88293,88306,88316,88332,88371,88407,88443,88447,88456,88472,88476,88480,88484],{"__ignoreMap":57},[62,88232,88233,88235],{"class":64,"line":65},[62,88234,942],{"class":72},[62,88236,2079],{"class":68},[62,88238,88239,88241,88243,88245],{"class":64,"line":76},[62,88240,116],{"class":68},[62,88242,119],{"class":68},[62,88244,2088],{"class":122},[62,88246,126],{"class":72},[62,88248,88249],{"class":64,"line":82},[62,88250,79],{"emptyLinePlaceholder":13},[62,88252,88253,88255,88257,88259,88261,88263,88265,88267,88269],{"class":64,"line":89},[62,88254,194],{"class":68},[62,88256,2101],{"class":68},[62,88258,200],{"class":68},[62,88260,2106],{"class":122},[62,88262,2109],{"class":72},[62,88264,973],{"class":68},[62,88266,2114],{"class":72},[62,88268,2117],{"class":889},[62,88270,768],{"class":72},[62,88272,88273,88275,88277],{"class":64,"line":95},[62,88274,2124],{"class":72},[62,88276,2127],{"class":122},[62,88278,2130],{"class":72},[62,88280,88281],{"class":64,"line":101},[62,88282,223],{"class":72},[62,88284,88285],{"class":64,"line":107},[62,88286,79],{"emptyLinePlaceholder":13},[62,88288,88289,88291],{"class":64,"line":113},[62,88290,2143],{"class":72},[62,88292,2146],{"class":68},[62,88294,88295,88297,88299,88302,88304],{"class":64,"line":129},[62,88296,2151],{"class":72},[62,88298,2154],{"class":122},[62,88300,88301],{"class":72},"(ProductRepository ",[62,88303,23540],{"class":889},[62,88305,768],{"class":72},[62,88307,88308,88310,88312,88314],{"class":64,"line":134},[62,88309,360],{"class":68},[62,88311,2169],{"class":72},[62,88313,800],{"class":68},[62,88315,126],{"class":72},[62,88317,88318,88320,88322,88324,88326,88328,88330],{"class":64,"line":156},[62,88319,62403],{"class":72},[62,88321,4044],{"class":68},[62,88323,9917],{"class":72},[62,88325,146],{"class":68},[62,88327,3499],{"class":72},[62,88329,3298],{"class":122},[62,88331,3301],{"class":72},[62,88333,88334,88336,88338,88340,88343,88345,88347,88349,88352,88354,88356,88359,88361,88364,88367,88369],{"class":64,"line":161},[62,88335,85584],{"class":68},[62,88337,9584],{"class":122},[62,88339,2109],{"class":72},[62,88341,88342],{"class":1675},"\"Product 1\"",[62,88344,32225],{"class":72},[62,88346,21775],{"class":149},[62,88348,32225],{"class":72},[62,88350,88351],{"class":149},"1.99F",[62,88353,32225],{"class":72},[62,88355,2426],{"class":68},[62,88357,88358],{"class":122}," BigDecimal",[62,88360,2109],{"class":72},[62,88362,88363],{"class":149},"9.99",[62,88365,88366],{"class":72},"),LocalDateTime.",[62,88368,43561],{"class":122},[62,88370,7120],{"class":72},[62,88372,88373,88375,88377,88379,88382,88384,88386,88388,88391,88393,88395,88397,88399,88401,88403,88405],{"class":64,"line":167},[62,88374,85584],{"class":68},[62,88376,9584],{"class":122},[62,88378,2109],{"class":72},[62,88380,88381],{"class":1675},"\"Product 2\"",[62,88383,32225],{"class":72},[62,88385,50950],{"class":149},[62,88387,976],{"class":72},[62,88389,88390],{"class":149},"3.99F",[62,88392,32225],{"class":72},[62,88394,2426],{"class":68},[62,88396,88358],{"class":122},[62,88398,2109],{"class":72},[62,88400,88363],{"class":149},[62,88402,88366],{"class":72},[62,88404,43561],{"class":122},[62,88406,7120],{"class":72},[62,88408,88409,88411,88413,88415,88418,88420,88422,88424,88427,88429,88431,88433,88435,88437,88439,88441],{"class":64,"line":173},[62,88410,85584],{"class":68},[62,88412,9584],{"class":122},[62,88414,2109],{"class":72},[62,88416,88417],{"class":1675},"\"Product 3\"",[62,88419,976],{"class":72},[62,88421,21775],{"class":149},[62,88423,976],{"class":72},[62,88425,88426],{"class":149},"19.99F",[62,88428,32225],{"class":72},[62,88430,2426],{"class":68},[62,88432,88358],{"class":122},[62,88434,2109],{"class":72},[62,88436,88363],{"class":149},[62,88438,88366],{"class":72},[62,88440,43561],{"class":122},[62,88442,4460],{"class":72},[62,88444,88445],{"class":64,"line":179},[62,88446,51023],{"class":72},[62,88448,88449,88451,88453],{"class":64,"line":185},[62,88450,52625],{"class":72},[62,88452,62170],{"class":122},[62,88454,88455],{"class":72},"(products);\n",[62,88457,88458,88460,88462,88464,88466,88468,88470],{"class":64,"line":191},[62,88459,52625],{"class":72},[62,88461,10287],{"class":122},[62,88463,3229],{"class":72},[62,88465,4215],{"class":122},[62,88467,4520],{"class":72},[62,88469,4451],{"class":68},[62,88471,4525],{"class":72},[62,88473,88474],{"class":64,"line":209},[62,88475,2252],{"class":72},[62,88477,88478],{"class":64,"line":220},[62,88479,223],{"class":72},[62,88481,88482],{"class":64,"line":226},[62,88483,79],{"emptyLinePlaceholder":13},[62,88485,88486],{"class":64,"line":231},[62,88487,379],{"class":72},[26,88489,88491],{"id":88490},"defining-our-schema","Defining our Schema",[22,88493,88494],{},"After successfully creating our products and ensuring they're in the database, let's move onto defining our GraphQL schema.",[22,88496,88497,88498,88500,88501,88504,88505,88507,88508,88510,88511,19931,88513,88515,88516,2755],{},"In ",[59,88499,46280],{}," directory, we will create a new file named **",[59,88502,88503],{},"**schema.graphqls",". Here we will start with our object type, ",[646,88506,4044],{},". With ID and Title being our two types, we will declare ",[646,88509,6283],{}," as an ",[646,88512,8845],{},[646,88514,3196],{}," as a ",[646,88517,973],{},[22,88519,88520],{},"Next, we define our 'Query' type. We want a way to get all of our products and this will return a collection of products.",[52,88522,88524],{"className":8822,"code":88523,"language":8824,"meta":57,"style":57},"type Product {\n id: ID!\n title: String\n}\n\ntype Query {\n allProducts: [Product]!\n}\n",[59,88525,88526,88531,88535,88540,88544,88548,88552,88557],{"__ignoreMap":57},[62,88527,88528],{"class":64,"line":65},[62,88529,88530],{},"type Product {\n",[62,88532,88533],{"class":64,"line":76},[62,88534,46575],{},[62,88536,88537],{"class":64,"line":82},[62,88538,88539],{}," title: String\n",[62,88541,88542],{"class":64,"line":89},[62,88543,379],{},[62,88545,88546],{"class":64,"line":95},[62,88547,79],{"emptyLinePlaceholder":13},[62,88549,88550],{"class":64,"line":101},[62,88551,83419],{},[62,88553,88554],{"class":64,"line":107},[62,88555,88556],{}," allProducts: [Product]!\n",[62,88558,88559],{"class":64,"line":113},[62,88560,379],{},[26,88562,88564],{"id":88563},"controller-creation-and-testing","Controller Creation and Testing",[22,88566,88567],{},"Now it's time to create our product controller, along with a 'Find All' method, which will pull all of our products from the database. Finally, we link our GraphQL 'allProducts' query to our repository's 'findAll' method.",[52,88569,88571],{"className":54,"code":88570,"language":56,"meta":57,"style":57},"@Controller\npublic class ProductController {\n\n private final ProductRepository repository;\n\n public ProductController(ProductRepository repository) {\n this.repository = repository;\n }\n\n @QueryMapping\n public List\u003CProduct> allProducts() {\n return repository.findAll();\n }\n}\n",[59,88572,88573,88579,88590,88594,88602,88606,88618,88628,88632,88636,88642,88657,88667,88671],{"__ignoreMap":57},[62,88574,88575,88577],{"class":64,"line":65},[62,88576,942],{"class":72},[62,88578,16624],{"class":68},[62,88580,88581,88583,88585,88588],{"class":64,"line":76},[62,88582,116],{"class":68},[62,88584,119],{"class":68},[62,88586,88587],{"class":122}," ProductController",[62,88589,126],{"class":72},[62,88591,88592],{"class":64,"line":82},[62,88593,79],{"emptyLinePlaceholder":13},[62,88595,88596,88598,88600],{"class":64,"line":89},[62,88597,137],{"class":68},[62,88599,458],{"class":68},[62,88601,4005],{"class":72},[62,88603,88604],{"class":64,"line":95},[62,88605,79],{"emptyLinePlaceholder":13},[62,88607,88608,88610,88612,88614,88616],{"class":64,"line":101},[62,88609,194],{"class":68},[62,88611,88587],{"class":122},[62,88613,88301],{"class":72},[62,88615,23540],{"class":889},[62,88617,768],{"class":72},[62,88619,88620,88622,88624,88626],{"class":64,"line":107},[62,88621,2405],{"class":149},[62,88623,23549],{"class":72},[62,88625,146],{"class":68},[62,88627,23554],{"class":72},[62,88629,88630],{"class":64,"line":113},[62,88631,223],{"class":72},[62,88633,88634],{"class":64,"line":129},[62,88635,79],{"emptyLinePlaceholder":13},[62,88637,88638,88640],{"class":64,"line":134},[62,88639,2143],{"class":72},[62,88641,83538],{"class":68},[62,88643,88644,88646,88648,88650,88652,88655],{"class":64,"line":156},[62,88645,194],{"class":68},[62,88647,3079],{"class":72},[62,88649,4044],{"class":68},[62,88651,3135],{"class":72},[62,88653,88654],{"class":122},"allProducts",[62,88656,206],{"class":72},[62,88658,88659,88661,88663,88665],{"class":64,"line":161},[62,88660,360],{"class":68},[62,88662,4069],{"class":72},[62,88664,10287],{"class":122},[62,88666,822],{"class":72},[62,88668,88669],{"class":64,"line":167},[62,88670,223],{"class":72},[62,88672,88673],{"class":64,"line":173},[62,88674,379],{"class":72},[22,88676,88677],{},"Once we restart our application, we can navigate to localhost:8080/graphiql and test whether our query returns the products we stored earlier.",[26,88679,88681],{"id":88680},"dealing-with-four-built-in-scalars","Dealing with Four Built-in Scalars",[22,88683,88684,88685,88688,88689,88692],{},"With our application working so far, let's move on to implementing some more built-in scalars - the boolean and float types. For our Product model, we'll add two new properties: ",[646,88686,88687],{},"onSale"," (a boolean) and ",[646,88690,88691],{},"weight"," (a float). Remember to update your schema definition to include these new fields.",[52,88694,88696],{"className":54,"code":88695,"language":56,"meta":57,"style":57},"@Entity\npublic class Product {\n\n @Id\n @GeneratedValue\n private Integer id;\n private String title;\n private Boolean isOnSale;\n private Float weight;\n\n // constructors, getters, setters and toSring\n\n}\n",[59,88697,88698,88704,88714,88718,88724,88730,88736,88742,88749,88756,88760,88764,88768],{"__ignoreMap":57},[62,88699,88700,88702],{"class":64,"line":65},[62,88701,942],{"class":72},[62,88703,8999],{"class":68},[62,88705,88706,88708,88710,88712],{"class":64,"line":76},[62,88707,116],{"class":68},[62,88709,119],{"class":68},[62,88711,9584],{"class":122},[62,88713,126],{"class":72},[62,88715,88716],{"class":64,"line":82},[62,88717,79],{"emptyLinePlaceholder":13},[62,88719,88720,88722],{"class":64,"line":89},[62,88721,2143],{"class":72},[62,88723,22298],{"class":68},[62,88725,88726,88728],{"class":64,"line":95},[62,88727,2143],{"class":72},[62,88729,9022],{"class":68},[62,88731,88732,88734],{"class":64,"line":101},[62,88733,137],{"class":68},[62,88735,45372],{"class":72},[62,88737,88738,88740],{"class":64,"line":107},[62,88739,137],{"class":68},[62,88741,9036],{"class":72},[62,88743,88744,88746],{"class":64,"line":113},[62,88745,137],{"class":68},[62,88747,88748],{"class":72}," Boolean isOnSale;\n",[62,88750,88751,88753],{"class":64,"line":129},[62,88752,137],{"class":68},[62,88754,88755],{"class":72}," Float weight;\n",[62,88757,88758],{"class":64,"line":134},[62,88759,79],{"emptyLinePlaceholder":13},[62,88761,88762],{"class":64,"line":156},[62,88763,88175],{"class":85},[62,88765,88766],{"class":64,"line":161},[62,88767,79],{"emptyLinePlaceholder":13},[62,88769,88770],{"class":64,"line":167},[62,88771,379],{"class":72},[22,88773,88774],{},"Now, if we rerun our application and add 'onSale' and 'weight' to our query, we should successfully receive back the details we stored for them.",[22,88776,88777],{},"So, what do we do when we need some custom scalars? Let's investigate how to handle this.",[26,88779,88781],{"id":88780},"implementing-custom-scalars","Implementing Custom Scalars",[22,88783,88784,88785,88788,88789,88792,88793,88515,88795,2755],{},"Let's add a ",[646,88786,88787],{},"price"," to our Product model, as a ",[646,88790,88791],{},"BigDecimal",", and a ",[646,88794,73532],{},[646,88796,88797],{},"LocalDateTime",[52,88799,88801],{"className":54,"code":88800,"language":56,"meta":57,"style":57},"@Entity\npublic class Product {\n\n @Id\n @GeneratedValue\n private Integer id;\n private String title;\n private Boolean isOnSale;\n private Float weight;\n private BigDecimal price;\n private LocalDateTime dateCreated;\n",[59,88802,88803,88809,88819,88823,88829,88835,88841,88847,88853,88859,88865],{"__ignoreMap":57},[62,88804,88805,88807],{"class":64,"line":65},[62,88806,942],{"class":72},[62,88808,8999],{"class":68},[62,88810,88811,88813,88815,88817],{"class":64,"line":76},[62,88812,116],{"class":68},[62,88814,119],{"class":68},[62,88816,9584],{"class":122},[62,88818,126],{"class":72},[62,88820,88821],{"class":64,"line":82},[62,88822,79],{"emptyLinePlaceholder":13},[62,88824,88825,88827],{"class":64,"line":89},[62,88826,2143],{"class":72},[62,88828,22298],{"class":68},[62,88830,88831,88833],{"class":64,"line":95},[62,88832,2143],{"class":72},[62,88834,9022],{"class":68},[62,88836,88837,88839],{"class":64,"line":101},[62,88838,137],{"class":68},[62,88840,45372],{"class":72},[62,88842,88843,88845],{"class":64,"line":107},[62,88844,137],{"class":68},[62,88846,9036],{"class":72},[62,88848,88849,88851],{"class":64,"line":113},[62,88850,137],{"class":68},[62,88852,88748],{"class":72},[62,88854,88855,88857],{"class":64,"line":129},[62,88856,137],{"class":68},[62,88858,88755],{"class":72},[62,88860,88861,88863],{"class":64,"line":134},[62,88862,137],{"class":68},[62,88864,9595],{"class":72},[62,88866,88867,88869],{"class":64,"line":156},[62,88868,137],{"class":68},[62,88870,88871],{"class":72}," LocalDateTime dateCreated;\n",[22,88873,88874],{},"Unfortunately, these two properties aren't covered by our built-in scalars, so we are going to need to use custom scalars.",[22,88876,88877,88878,88881,88882,2755],{},"The first place to look for help with this is the GraphQL Java documentation. Herein, we also find details of the ",[4534,88879,88880],{},"Extended Scalars"," - which includes special types like BigDecimal and BigInteger - but this requires a separate library, the ",[4534,88883,88884],{},"GraphQL Java Extended Scalars Library",[52,88886,88887],{"className":1769,"code":46418,"language":1771,"meta":57,"style":57},[59,88888,88889,88893,88897,88901,88905],{"__ignoreMap":57},[62,88890,88891],{"class":64,"line":65},[62,88892,46425],{},[62,88894,88895],{"class":64,"line":76},[62,88896,46430],{},[62,88898,88899],{"class":64,"line":82},[62,88900,46435],{},[62,88902,88903],{"class":64,"line":89},[62,88904,46440],{},[62,88906,88907],{"class":64,"line":95},[62,88908,46445],{},[22,88910,88911],{},"To use it, we need to add the library to our Maven dependencies and create a new config bean for the extended scalars. This allows us to declare the scalar in the GraphQL schema definition.",[52,88913,88915],{"className":54,"code":88914,"language":56,"meta":57,"style":57},"@Configuration\npublic class GraphQlConfig {\n\n @Bean\n public RuntimeWiringConfigurer runtimeWiringConfigurer() {\n return wiringBuilder -> wiringBuilder\n .scalar(ExtendedScalars.GraphQLBigDecimal);\n\n }\n\n}\n",[59,88916,88917,88923,88934,88938,88944,88954,88964,88973,88977,88981,88985],{"__ignoreMap":57},[62,88918,88919,88921],{"class":64,"line":65},[62,88920,942],{"class":72},[62,88922,11133],{"class":68},[62,88924,88925,88927,88929,88932],{"class":64,"line":76},[62,88926,116],{"class":68},[62,88928,119],{"class":68},[62,88930,88931],{"class":122}," GraphQlConfig",[62,88933,126],{"class":72},[62,88935,88936],{"class":64,"line":82},[62,88937,79],{"emptyLinePlaceholder":13},[62,88939,88940,88942],{"class":64,"line":89},[62,88941,2143],{"class":72},[62,88943,2146],{"class":68},[62,88945,88946,88948,88950,88952],{"class":64,"line":95},[62,88947,194],{"class":68},[62,88949,46507],{"class":72},[62,88951,46510],{"class":122},[62,88953,206],{"class":72},[62,88955,88956,88958,88960,88962],{"class":64,"line":101},[62,88957,360],{"class":68},[62,88959,46519],{"class":72},[62,88961,800],{"class":68},[62,88963,46524],{"class":72},[62,88965,88966,88968,88970],{"class":64,"line":107},[62,88967,2418],{"class":72},[62,88969,46531],{"class":122},[62,88971,88972],{"class":72},"(ExtendedScalars.GraphQLBigDecimal);\n",[62,88974,88975],{"class":64,"line":113},[62,88976,79],{"emptyLinePlaceholder":13},[62,88978,88979],{"class":64,"line":129},[62,88980,223],{"class":72},[62,88982,88983],{"class":64,"line":134},[62,88984,79],{"emptyLinePlaceholder":13},[62,88986,88987],{"class":64,"line":156},[62,88988,379],{"class":72},[52,88990,88992],{"className":8822,"code":88991,"language":8824,"meta":57,"style":57},"scalar BigDecimal\nscalar LocalDateTime\n\ntype Product {\n id: ID!\n title: String\n isOnSale: Boolean\n weight: Float\n price: BigDecimal\n dateCreated: LocalDateTime\n}\n\ntype Query {\n allProducts: [Product]!\n}\n",[59,88993,88994,88999,89004,89008,89012,89016,89020,89025,89030,89035,89040,89044,89048,89052,89056],{"__ignoreMap":57},[62,88995,88996],{"class":64,"line":65},[62,88997,88998],{},"scalar BigDecimal\n",[62,89000,89001],{"class":64,"line":76},[62,89002,89003],{},"scalar LocalDateTime\n",[62,89005,89006],{"class":64,"line":82},[62,89007,79],{"emptyLinePlaceholder":13},[62,89009,89010],{"class":64,"line":89},[62,89011,88530],{},[62,89013,89014],{"class":64,"line":95},[62,89015,46575],{},[62,89017,89018],{"class":64,"line":101},[62,89019,88539],{},[62,89021,89022],{"class":64,"line":107},[62,89023,89024],{}," isOnSale: Boolean\n",[62,89026,89027],{"class":64,"line":113},[62,89028,89029],{}," weight: Float\n",[62,89031,89032],{"class":64,"line":129},[62,89033,89034],{}," price: BigDecimal\n",[62,89036,89037],{"class":64,"line":134},[62,89038,89039],{}," dateCreated: LocalDateTime\n",[62,89041,89042],{"class":64,"line":156},[62,89043,379],{},[62,89045,89046],{"class":64,"line":161},[62,89047,79],{"emptyLinePlaceholder":13},[62,89049,89050],{"class":64,"line":167},[62,89051,83419],{},[62,89053,89054],{"class":64,"line":173},[62,89055,88556],{},[62,89057,89058],{"class":64,"line":179},[62,89059,379],{},[22,89061,89062,89063,89066,89067,2755],{},"The LocalDateTime type isn't covered in the Extended Scalars but another project – ",[4534,89064,89065],{},"GraphQL Java DateTime"," - can take care of this one. Include this new dependency and update your ",[59,89068,46276],{},[22,89070,89071],{},"Having made these changes, let's rerun our application one last time and see if we are successful.",[26,89073,1499],{"id":1498},[22,89075,89076],{},"As we can see, we have now successfully used all of the five built-in scalar types, and even created some custom scalars for our application.",[22,89078,89079],{},"Remember though, before you go all out creating your own custom scalar, always check if there's an existing scalar that could do the job for you.",[22,89081,89082],{},"GraphQL is great. It makes building APIs easy and manageable, and I love using it with Spring. I hope you found this tutorial helpful, and remember, happy coding!",[1527,89084,89085],{},"html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .s4-Ip, html code.shiki .s4-Ip{--shiki-default:#6A737D;--shiki-dark:#6A737D;--shiki-sepia:#6A737D}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .spoOn, html code.shiki .spoOn{--shiki-default:#E36209;--shiki-dark:#FFAB70;--shiki-sepia:#E36209}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}",{"title":57,"searchDepth":76,"depth":76,"links":89087},[89088,89089,89090,89091,89092,89093,89094],{"id":88079,"depth":76,"text":88080},{"id":88102,"depth":76,"text":88103},{"id":88490,"depth":76,"text":88491},{"id":88563,"depth":76,"text":88564},{"id":88680,"depth":76,"text":88681},{"id":88780,"depth":76,"text":88781},{"id":1498,"depth":76,"text":1499},{"_id":89096,"path":89097,"title":89098,"description":89099,"meta":89100,"body":89105},"content/blog/2023/01/27/jakarta-ee-10-uuid.md","/blog/2023/01/27/jakarta-ee-10-uuid","Easily Implement UUIDs as Primary Keys in Spring Boot 3","In this tutorial you will learn how to use one of the new features in Jakarta EE 10 in a Spring Boot 3 application.",{"slug":89101,"date":89102,"published":13,"tags":89103,"author":17,"cover":89104,"excerpt":-1},"jakarta-ee-10-uuid","2023-01-27T10:00:00.000Z",[2925],"./jakarta_ee_10_uuid.png",{"type":19,"value":89106,"toc":89600},[89107,89110,89114,89117,89128,89131,89133,89140,89143,89148,89152,89167,89177,89299,89302,89306,89312,89348,89350,89356,89359,89549,89552,89587,89592,89594,89597],[22,89108,89109],{},"In this blog post, we will take a trip down memory lane and discuss the evolution of Jakarta EE (formerly known as Java EE) and its impact on enterprise development in Java. We will explore the role of the Eclipse Foundation in furthering the development of Jakarta EE and how it has led to the introduction of new APIs, fixes, and features. This article will also delve into one specific change in Jakarta EE 10, the ability to use a UUID as a primary key, and how it can be easily implemented in a Spring Boot 3 project using Spring Data JPA.",[26,89111,89113],{"id":89112},"what-is-jakarta-ee","What is Jakarta EE",[22,89115,89116],{},"Jakarta EE is an open-source platform for developing enterprise-level applications in Java, maintained and managed by the Eclipse Foundation. It provides secure, scalable, and extensible tools for creating, deploying, and managing applications. Formerly known as Java EE (Enterprise Edition), it has been available since 1999. In recent years, Jakarta EE has been updated with new APIs, bug fixes, and features, giving developers more options for creating enterprise applications.",[22,89118,89119,89120,89123,89124,89127],{},"The real question is: why are we discussing this now, and why should Spring developers care? This is because Spring Framework 6 now has a baseline on Jakarta EE 9 and 10. The two versions are necessary because we will use 10 wherever possible, and fall back to 9 when we can't. Jakarta EE 9 was the version where all the namespace changes from ",[59,89121,89122],{},"javax.*"," to ",[59,89125,89126],{},"jakarta.*"," were made, without any changes to the APIs. In Jakarta EE 10, we start to see the evolution of the APIs, which includes new features.",[22,89129,89130],{},"Now that you have some background on Jakarta EE and why we are discussing it in the context of a Spring Application, it's time to write some code. Create a new Spring Boot project and use one of the new features from Jakarta EE 10.",[26,89132,67383],{"id":67382},[22,89134,89135,89136,89139],{},"To create a new project, go to ",[677,89137,2903],{"href":45295,"rel":89138},[681],", select Maven as the project type, Java as the language, and the latest stable version of Spring Boot. Then, fill in your project metadata and select the Web, Spring Data JPA, and H2 dependencies.",[22,89141,89142],{},"Once you have entered the details, you can download the project as a zip file and unzip it. Then, you can import the project into your preferred Integrated Development Environment (IDE) and begin development.",[22,89144,89145],{},[653,89146],{"alt":24606,"src":89147},"/images/blog/2023/01/27/start-spring-io.png",[636,89149,89151],{"id":89150},"spring-data-jpa-entity","Spring Data JPA Entity",[22,89153,89154,89155,89157,89158,89160,89161,89163,89164,89166],{},"With our project created, it's time to write some code. Let's start by creating a new class called ",[59,89156,4044],{},". When creating an Entity class in Spring Data JPA you need to mark it with the ",[59,89159,61725],{}," annotation. Next, you need to create a new field called ",[59,89162,6283],{}," and annotate it with ",[59,89165,80421],{},". Nothing you have done so far is new, but here is where things get interesting.",[22,89168,89169,89170,89172,89173,89176],{},"You could always annotate the ID field with ",[59,89171,82853],{}," . This annotation defines the primary key generation strategy that the persistence provider must use to generate the primary key. Jakarta EE 10 now adds the ",[59,89174,89175],{},"GenerationType"," for a UUID, so that you can use Universally Unique Identifiers (UUIDs) as the primary key.",[52,89178,89180],{"className":54,"code":89179,"language":56,"meta":57,"style":57},"@Entity\npublic class Product {\n\n @Id\n @GeneratedValue(strategy = GenerationType.UUID)\n private UUID id;\n private String title;\n\n public Product() {\n }\n\n public Product(String title) {\n this.title = title;\n }\n\n // getters and setters\n}\n",[59,89181,89182,89188,89198,89202,89208,89225,89232,89238,89242,89250,89254,89258,89270,89282,89286,89290,89295],{"__ignoreMap":57},[62,89183,89184,89186],{"class":64,"line":65},[62,89185,942],{"class":72},[62,89187,8999],{"class":68},[62,89189,89190,89192,89194,89196],{"class":64,"line":76},[62,89191,116],{"class":68},[62,89193,119],{"class":68},[62,89195,9584],{"class":122},[62,89197,126],{"class":72},[62,89199,89200],{"class":64,"line":82},[62,89201,79],{"emptyLinePlaceholder":13},[62,89203,89204,89206],{"class":64,"line":89},[62,89205,2143],{"class":72},[62,89207,22298],{"class":68},[62,89209,89210,89212,89215,89217,89220,89222],{"class":64,"line":95},[62,89211,2143],{"class":72},[62,89213,89214],{"class":68},"GeneratedValue",[62,89216,2109],{"class":72},[62,89218,89219],{"class":149},"strategy",[62,89221,2556],{"class":68},[62,89223,89224],{"class":72}," GenerationType.UUID)\n",[62,89226,89227,89229],{"class":64,"line":101},[62,89228,137],{"class":68},[62,89230,89231],{"class":72}," UUID id;\n",[62,89233,89234,89236],{"class":64,"line":107},[62,89235,137],{"class":68},[62,89237,9036],{"class":72},[62,89239,89240],{"class":64,"line":113},[62,89241,79],{"emptyLinePlaceholder":13},[62,89243,89244,89246,89248],{"class":64,"line":129},[62,89245,194],{"class":68},[62,89247,9584],{"class":122},[62,89249,206],{"class":72},[62,89251,89252],{"class":64,"line":134},[62,89253,223],{"class":72},[62,89255,89256],{"class":64,"line":156},[62,89257,79],{"emptyLinePlaceholder":13},[62,89259,89260,89262,89264,89266,89268],{"class":64,"line":161},[62,89261,194],{"class":68},[62,89263,9584],{"class":122},[62,89265,1049],{"class":72},[62,89267,3196],{"class":889},[62,89269,768],{"class":72},[62,89271,89272,89274,89277,89279],{"class":64,"line":167},[62,89273,2405],{"class":149},[62,89275,89276],{"class":72},".title ",[62,89278,146],{"class":68},[62,89280,89281],{"class":72}," title;\n",[62,89283,89284],{"class":64,"line":173},[62,89285,223],{"class":72},[62,89287,89288],{"class":64,"line":179},[62,89289,79],{"emptyLinePlaceholder":13},[62,89291,89292],{"class":64,"line":185},[62,89293,89294],{"class":85}," // getters and setters\n",[62,89296,89297],{"class":64,"line":191},[62,89298,379],{"class":72},[22,89300,89301],{},"As I mentioned in the video I don’t want to get into the pros/cons of using a UUID as the primary key. There were a lot of really good comments in the Twitter thread I linked to above so if you’re interested in that please take a chance to read through that.",[636,89303,89305],{"id":89304},"spring-data-repository","Spring Data Repository",[22,89307,89308,89309,89311],{},"Now that you have an Entity in place you need to create a Repository. The Spring Data ",[59,89310,45474],{}," is an interface that extends the Spring Data Repository interface and adds support for the CRUD (Create, Read, Update and Delete) operations on a list of objects. It provides basic methods for saving and retrieving data from a list of objects, and also includes methods for pagination, sorting and querying. This makes it an ideal choice for applications that require the ability to perform basic CRUD operations on a list of objects.",[52,89313,89315],{"className":54,"code":89314,"language":56,"meta":57,"style":57},"public interface ProductRepository extends ListCrudRepository\u003CProduct, UUID> {\n\n}\n",[59,89316,89317,89340,89344],{"__ignoreMap":57},[62,89318,89319,89321,89323,89325,89327,89329,89331,89333,89335,89338],{"class":64,"line":65},[62,89320,116],{"class":68},[62,89322,8531],{"class":68},[62,89324,88200],{"class":122},[62,89326,8537],{"class":68},[62,89328,45494],{"class":122},[62,89330,760],{"class":72},[62,89332,4044],{"class":68},[62,89334,976],{"class":72},[62,89336,89337],{"class":68},"UUID",[62,89339,8552],{"class":72},[62,89341,89342],{"class":64,"line":76},[62,89343,79],{"emptyLinePlaceholder":13},[62,89345,89346],{"class":64,"line":82},[62,89347,379],{"class":72},[636,89349,61684],{"id":61683},[22,89351,89352,89353,89355],{},"Now that you have a repository in place you can create some new products and persist them to the database. A ",[59,89354,2066],{}," is a Spring Boot interface that is used to execute code when the application starts up. It is useful for performing any setup tasks that need to be done before the application can be used. It can also be used to run simple tasks that need to be executed at startup, such as setting up a database or loading data from an external source. It provides an easy way to execute code when the application starts, making it a useful tool for developers.",[22,89357,89358],{},"In the following example you will create 3 new products, save them to the database and then print out all of the records in the db so that you can view the generated IDs.",[52,89360,89362],{"className":54,"code":89361,"language":56,"meta":57,"style":57},"@SpringBootApplication\npublic class Application {\n\n public static void main(String[] args) {\n SpringApplication.run(Application.class, args);\n }\n\n @Bean\n CommandLineRunner commandLineRunner(ProductRepository productRepository) {\n return args -> {\n List\u003CProduct> products = List.of(new Product(\"Product 1\"),\n new Product(\"Product 2\"),\n new Product(\"Product 3\"));\n productRepository.saveAll(products);\n\n productRepository.findAll().stream().forEach(System.out::println);\n };\n }\n\n}\n",[59,89363,89364,89370,89380,89384,89404,89412,89416,89420,89426,89439,89449,89475,89488,89500,89509,89513,89533,89537,89541,89545],{"__ignoreMap":57},[62,89365,89366,89368],{"class":64,"line":65},[62,89367,942],{"class":72},[62,89369,2079],{"class":68},[62,89371,89372,89374,89376,89378],{"class":64,"line":76},[62,89373,116],{"class":68},[62,89375,119],{"class":68},[62,89377,2088],{"class":122},[62,89379,126],{"class":72},[62,89381,89382],{"class":64,"line":82},[62,89383,79],{"emptyLinePlaceholder":13},[62,89385,89386,89388,89390,89392,89394,89396,89398,89400,89402],{"class":64,"line":89},[62,89387,194],{"class":68},[62,89389,2101],{"class":68},[62,89391,200],{"class":68},[62,89393,2106],{"class":122},[62,89395,2109],{"class":72},[62,89397,973],{"class":68},[62,89399,2114],{"class":72},[62,89401,2117],{"class":889},[62,89403,768],{"class":72},[62,89405,89406,89408,89410],{"class":64,"line":95},[62,89407,2124],{"class":72},[62,89409,2127],{"class":122},[62,89411,2130],{"class":72},[62,89413,89414],{"class":64,"line":101},[62,89415,223],{"class":72},[62,89417,89418],{"class":64,"line":107},[62,89419,79],{"emptyLinePlaceholder":13},[62,89421,89422,89424],{"class":64,"line":113},[62,89423,2143],{"class":72},[62,89425,2146],{"class":68},[62,89427,89428,89430,89432,89434,89437],{"class":64,"line":129},[62,89429,2151],{"class":72},[62,89431,2154],{"class":122},[62,89433,88301],{"class":72},[62,89435,89436],{"class":889},"productRepository",[62,89438,768],{"class":72},[62,89440,89441,89443,89445,89447],{"class":64,"line":134},[62,89442,360],{"class":68},[62,89444,2169],{"class":72},[62,89446,800],{"class":68},[62,89448,126],{"class":72},[62,89450,89451,89453,89455,89457,89459,89461,89463,89465,89467,89469,89471,89473],{"class":64,"line":156},[62,89452,62403],{"class":72},[62,89454,4044],{"class":68},[62,89456,9917],{"class":72},[62,89458,146],{"class":68},[62,89460,3499],{"class":72},[62,89462,3298],{"class":122},[62,89464,2109],{"class":72},[62,89466,2426],{"class":68},[62,89468,9584],{"class":122},[62,89470,2109],{"class":72},[62,89472,88342],{"class":1675},[62,89474,3324],{"class":72},[62,89476,89477,89480,89482,89484,89486],{"class":64,"line":161},[62,89478,89479],{"class":68}," new",[62,89481,9584],{"class":122},[62,89483,2109],{"class":72},[62,89485,88381],{"class":1675},[62,89487,3324],{"class":72},[62,89489,89490,89492,89494,89496,89498],{"class":64,"line":167},[62,89491,89479],{"class":68},[62,89493,9584],{"class":122},[62,89495,2109],{"class":72},[62,89497,88417],{"class":1675},[62,89499,6979],{"class":72},[62,89501,89502,89505,89507],{"class":64,"line":173},[62,89503,89504],{"class":72}," productRepository.",[62,89506,62170],{"class":122},[62,89508,88455],{"class":72},[62,89510,89511],{"class":64,"line":179},[62,89512,79],{"emptyLinePlaceholder":13},[62,89514,89515,89517,89519,89521,89523,89525,89527,89529,89531],{"class":64,"line":185},[62,89516,89504],{"class":72},[62,89518,10287],{"class":122},[62,89520,3229],{"class":72},[62,89522,2621],{"class":122},[62,89524,3229],{"class":72},[62,89526,4215],{"class":122},[62,89528,4520],{"class":72},[62,89530,4451],{"class":68},[62,89532,4525],{"class":72},[62,89534,89535],{"class":64,"line":191},[62,89536,2252],{"class":72},[62,89538,89539],{"class":64,"line":209},[62,89540,223],{"class":72},[62,89542,89543],{"class":64,"line":220},[62,89544,79],{"emptyLinePlaceholder":13},[62,89546,89547],{"class":64,"line":226},[62,89548,379],{"class":72},[22,89550,89551],{},"If you run the application you should see the following output:",[52,89553,89555],{"className":1663,"code":89554,"language":1665,"meta":57,"style":57},"Product{id=fe63db2b-a76e-47aa-a1cb-e613b0b987e8, title='Product 1'}\nProduct{id=2236597b-1366-4cad-9a05-77ebb283acd3, title='Product 2'}\nProduct{id=c9f0596a-cfbc-48a7-8202-1ddd058120c0, title='Product 3'}\n",[59,89556,89557,89567,89577],{"__ignoreMap":57},[62,89558,89559,89561,89564],{"class":64,"line":65},[62,89560,4044],{"class":122},[62,89562,89563],{"class":1675},"{id=fe63db2b-a76e-47aa-a1cb-e613b0b987e8,",[62,89565,89566],{"class":1675}," title='Product 1'}\n",[62,89568,89569,89571,89574],{"class":64,"line":76},[62,89570,4044],{"class":122},[62,89572,89573],{"class":1675},"{id=2236597b-1366-4cad-9a05-77ebb283acd3,",[62,89575,89576],{"class":1675}," title='Product 2'}\n",[62,89578,89579,89581,89584],{"class":64,"line":82},[62,89580,4044],{"class":122},[62,89582,89583],{"class":1675},"{id=c9f0596a-cfbc-48a7-8202-1ddd058120c0,",[62,89585,89586],{"class":1675}," title='Product 3'}\n",[22,89588,89589],{},[653,89590],{"alt":2291,"src":89591},"/images/blog/2023/01/27/run_app.png",[26,89593,1499],{"id":1498},[22,89595,89596],{},"The transition to Jakarta EE 9/10 was not easy, but it was necessary for the Spring Framework. Going forward, the Spring Framework and its associated libraries can benefit from the evolution of the Jakarta EE APIs.",[1527,89598,89599],{},"html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}html pre.shiki code .spoOn, html code.shiki .spoOn{--shiki-default:#E36209;--shiki-dark:#FFAB70;--shiki-sepia:#E36209}html pre.shiki code .s4-Ip, html code.shiki .s4-Ip{--shiki-default:#6A737D;--shiki-dark:#6A737D;--shiki-sepia:#6A737D}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}",{"title":57,"searchDepth":76,"depth":76,"links":89601},[89602,89603,89608],{"id":89112,"depth":76,"text":89113},{"id":67382,"depth":76,"text":67383,"children":89604},[89605,89606,89607],{"id":89150,"depth":82,"text":89151},{"id":89304,"depth":82,"text":89305},{"id":61683,"depth":82,"text":61684},{"id":1498,"depth":76,"text":1499},{"_id":89610,"path":89611,"title":38355,"description":89612,"meta":89613,"body":89618},"content/blog/2023/01/01/happy-new-year-2023.md","/blog/2023/01/01/happy-new-year-2023","Happy New Year! I want to take a few minutes and talk about a few of my priorities as we head into the new year.",{"slug":89614,"date":89615,"published":13,"tags":89616,"author":17,"cover":89617,"excerpt":-1},"happy-new-year-2023","2023-01-01T10:00:00.000Z",[20701,33475],"kostiantyn-li-pTfOKdj8whk-unsplash.jpg",{"type":19,"value":89619,"toc":89880},[89620,89634,89645,89649,89657,89660,89663,89677,89679,89682,89685,89691,89694,89697,89699,89707,89713,89716,89719,89722,89725,89728,89734,89738,89746,89748,89751,89754,89767,89770,89773,89777,89785,89788,89791,89795,89798,89801,89805,89808,89814,89817,89820,89823,89826,89863,89866,89872,89875,89877],[22,89621,89622,89623,89627,89628,89633],{},"It’s that time of year again. If you missed ",[677,89624,37875],{"href":89625,"rel":89626},"https://www.danvega.dev/blog/2022/12/29/2022-reflections/",[681]," I spent some time reflecting on 2022 before setting some goals for the new year. I mentioned this in ",[677,89629,89632],{"href":89630,"rel":89631},"https://www.danvega.dev/blog/2022/01/01/happy-new-year-2022/",[681],"last year's new year's post"," but I have 2 small kids at home so I am hesitant to attach the word goal but I will give it a shot.",[22,89635,89636,89637,89644],{},"I have read Michael Hyatt’s book ",[646,89638,89639],{},[677,89640,89643],{"href":89641,"rel":89642},"https://amzn.to/3VvC1sd",[681],"Your Best Year"," a couple of times now, so I know that by not creating SMART goals, I am setting myself up for failure. With that, I will list some things I want to focus on in the new year and try to revisit the idea of setting a more measurable goal later. Even as I am writing this it reminds me a lot of when I say I will come back and write those tests later 🤦♂️🤣",[26,89646,89648],{"id":89647},"professional-goals","Professional Goals",[22,89650,89651,89652,89656],{},"I will be hitting my 1 year anniversary as a ",[677,89653,37107],{"href":89654,"rel":89655},"https://tanzu.vmware.com/developer/team/dan-vega/",[681]," in January and couldn’t be more excited about the year ahead. While I feel like I have some breadth across the Spring Ecosystem of projects there is still a lot I have to learn in terms of depth.",[22,89658,89659],{},"That is why a big focus this year for me is to keep doing what I did last year. I want to learn as much as I can and take what I have learned and share that with the community. I will do that by listening to our community on places like YouTube, Twitter, Facebook, Stack Overflow, and Spring Office Hours. Whatever questions are facing you the most are the subjects I will try and dive deeper into.",[22,89661,89662],{},"I love writing articles, creating videos, and live streaming but nothing quite beats an in-person event. This is why I spent some time towards the end of last year identifying which events I could submit proposals to. My goal here is to speak at 8-10 different in-person conferences and meetups. I may adjust this goal when I learn more about our plans at work but for now, here are the appearances I have scheduled:",[915,89664,89665,89668,89671,89674],{},[37,89666,89667],{},"CodeMash",[37,89669,89670],{},"Cleveland Java User Group",[37,89672,89673],{},"Spring Essentials Tour Stop (San Francisco)",[37,89675,89676],{},"DevNexus",[26,89678,37949],{"id":37948},[22,89680,89681],{},"I had a great year on YouTube last year and it gave me a glimpse of what could be. While I am happy with how consistent I was at times and the quality of content I produced I believe that I have a lot to learn and room to grow.",[22,89683,89684],{},"The first item I want to focus on is just being consistent and coming up with a good plan. I think consistency is a big part of why successful YouTubers are successful. I also have so many different types of videos that I work on from shorts to live streams to full-length tutorials. I think coming up with a consistent schedule of when I post what type of content is going to help me. If I can just post 1 of each per week that would be 144 uploads for the year and I think that would be a great goal.",[22,89686,89687],{},[653,89688],{"alt":89689,"src":89690},"christian-wiediger-NmGzVG5Wsg8-unsplash.jpg","/images/blog/2023/01/01/christian-wiediger-NmGzVG5Wsg8-unsplash.jpg",[22,89692,89693],{},"While I am on that subject a focus of mine last year was repurposing content and I need to get better at that on YouTube. If I prepare content to work on during a live stream I should use that across multiple shorts as well as full-length tutorials. It takes time to prepare content and If I am creating new content for each type of video I am not going to be very productive.",[22,89695,89696],{},"I know my way around video editing software but I wouldn’t call myself much of an editor. I need to get better at editing and this involves cuts, transitions, motion graphics, and more. I understand that the most important part of the video is the content but if you can tell a great story with visuals to keep the audience engaged it will only lead to great things. This is all in the name of creating better-quality videos.",[26,89698,38010],{"id":38009},[22,89700,89701,89702,89706],{},"Last year DaShaun and I started a new show on Tanzu TV called ",[677,89703,38010],{"href":89704,"rel":89705},"https://tanzu.vmware.com/developer/tv/spring-office-hours/",[681],". This was our chance to talk about what was new in the Spring community, show off any cool things we were working on and answer questions from the community.",[22,89708,89709],{},[653,89710],{"alt":89711,"src":89712},"spring-office-hours.png","/images/blog/2023/01/01/spring-office-hours.png",[22,89714,89715],{},"We did some shows without really telling anyone before we officially launched the show in May. During the summer we were traveling a lot for the SpringOne Tour and ended up not being as consistent as we would have liked. Towards the end of the year, we found a new home on Tuesdays at 3:30 EDT to hopefully include our friends all over and I think that really worked out well.",[22,89717,89718],{},"This is something both of us really look forward to each week and that isn’t going to change in the new year. I think some things we do want to improve on are scheduling shows in advance as well as automating that along with other tasks like show notes.",[22,89720,89721],{},"Finally, we do have a big idea for this show that we are working on but I don’t want to let the cat out of the bag just yet. We want to do our research on this particular thing before we decide to move forward with it. As soon as we can figure it out though we will be sure to let you know!",[26,89723,8474],{"id":89724},"danvegadev",[22,89726,89727],{},"My personal website is one of the things that I am most proud of. It has certainly evolved over the years but its purpose never has. This is a place where you can learn more about me as well as get access to all of the content that I produce.",[22,89729,89730],{},[653,89731],{"alt":89732,"src":89733},"danega_dev_homepage.png","/images/blog/2023/01/01/danega_dev_homepage.png",[636,89735,89737],{"id":89736},"moving-to-nuxt-3","Moving to Nuxt 3",[22,89739,89740,89741,89745],{},"I mentioned this in ",[677,89742,89744],{"href":89630,"rel":89743},[681],"last year's new year’s goals"," but I would like to move my website over to Nuxt 3. This never panned out last year because Nuxt 3 didn’t get released until the end of the year. I think one of the big questions I still have to answer is how I am going to convert all of my blog posts over to the format that Nuxt 3 is looking for. I currently have all of the images in the same folder as the post and Nuxt would like them in a public folder. If you are doing the same and found a solution for this please reach out.",[636,89747,37931],{"id":37930},[22,89749,89750],{},"The biggest thing I need to figure out what to do with my website is where I am going to focus my writing efforts. Last year I wrote a lousy 6 blog posts while sending out 32 editions of my newsletter. I enjoy writing and don’t want to stop doing it but at the same time, I have to be smart with my time.",[22,89752,89753],{},"If I am being honest when it comes to the blog I want the ability to post the occasional random blog post. What I really want on the blog are posts about the tutorials I am working on. The time I spend working on these tutorials though is for my YouTube channel. The question is how I can create a quick blog post that summarizes the YouTube video and links to it so I can get some cross-promotion between the blog and Youtube.",[22,89755,89756,89761,89762,89766],{},[677,89757,89760],{"href":89758,"rel":89759},"https://twitter.com/chris__sev",[681],"Chris Sev"," has a solution to this problem called ",[677,89763,82575],{"href":89764,"rel":89765},"https://videotapit.com/",[681],". It will transcribe your video and create a blog post it. My only concern here is that my videos don’t really transcribe to a blog post all that well. It doesn’t feel like something I would write but I also haven’t spent a ton of time testing out this wonderful tool. This is something I need to spend more time playing in the new year.",[22,89768,89769],{},"It’s also been an up-and-down year with the newsletter. I haven’t really been able to grow my subscriber base and in fact, I lose around 5-10 subscribers every time I send out a newsletter. I also haven’t put a lot of effort into driving newsletter subscribers. I know a lot of people create landing pages with something to entice someone into subscribing and I just haven’t done that.",[22,89771,89772],{},"I love to write but I also don’t want to write without a specific plan. I need to figure this out 🙇♂️",[26,89774,89776],{"id":89775},"reading","Reading",[22,89778,89779,89780,2755],{},"I went on a trip this year to Ocean Isle NC for a friend's wedding and it was an amazing getaway. It was great to see my good friend so happy and the wedding was very special. On top of that, I get to spend some time with friends, run alongside the ocean, and even managed to read 2 books on that trip. If you’re interested in reading more about that I talked it about in ",[677,89781,89784],{"href":89782,"rel":89783},"https://www.danvega.dev/newsletter/deep-work/",[681],"my newsletter",[22,89786,89787],{},"I was reminded how much I love reading and I need to make it more of a priority. I really do love learning new things but I also realized that it doesn’t need to just be related to work. I have gotten into history lately and have some great books lined up about the history of mankind.",[22,89789,89790],{},"I don’t have a goal for the number of books I want to read next year, I just need to make more time for it. If I can get the kids to sleep (on their own) at a decent time that is probably my best chance to find a regular reading cadence.",[26,89792,89794],{"id":89793},"health-fitness","Health & Fitness",[22,89796,89797],{},"I talked a little bit about this in my 2022 reflections but Health and Fitness must take priority over most things throughout this year. I am going to continue not drinking for the foreseeable future. My goal is to get off all medicine if possible so anything I can do that will contribute to that goal will be my focus.",[22,89799,89800],{},"I really enjoy running and spent the last part of this year running a lot. I want to run a half marathon this year and would like to sign up for the Cleveland Half in May. I also want to put in a lot of miles this year so I am setting a goal of running 600 miles for the year. This will of course rely on me not getting injured so I am going to strength train 2x per week on top of running 4x week.",[26,89802,89804],{"id":89803},"java-champions","Java Champions",[22,89806,89807],{},"I listed this in your last year's goals but this is going to continue to be a top goal of mine until it happens. Yes, I did say until it happens because I believe if you want something in life you need to believe in yourself and work hard at it.",[22,89809,3521,89810,89813],{},[677,89811,89804],{"href":37147,"rel":89812},[681]," Program consists of a unique group of skilled Java technologists and community leaders sponsored by Oracle. These are leaders, influencers, and enablers who help grow the size of the Java community.",[22,89815,89816],{},"While I have been a fan of Java and Spring I spend most of my time talking about Spring. I am going to focus more this year on general Java content. If there are questions you have about Java and would like me to create a tutorial or explanation about please feel free to reach out.",[26,89818,27663],{"id":89819},"artificial-intelligence-ai",[22,89821,89822],{},"There are some really amazing tools in the AI / Machine Learning space and I think we are only going to see more in the new year. When ChatGPT exploded towards the end of last year I received a lot of questions from developers wondering if this was going to take their job.",[22,89824,89825],{},"I don’t have a crystal ball and can’t see into the future but what I would say is you’re safe for now. With any industry, I think it would be a mistake to ignore AI. I think you can use AI as another tool in your tool belt. I’m excited about the future of AI and I will be particularly paying attention to the following services:",[915,89827,89828,89835,89842,89849,89856],{},[37,89829,89830],{},[677,89831,89834],{"href":89832,"rel":89833},"https://chat.openai.com/chat",[681],"ChatGPT",[37,89836,89837],{},[677,89838,89841],{"href":89839,"rel":89840},"https://openai.com/dall-e-2/",[681],"DALLE",[37,89843,89844],{},[677,89845,89848],{"href":89846,"rel":89847},"https://www.midjourney.com/home/?callbackUrl=%2Fapp%2F",[681],"Midjourney",[37,89850,89851],{},[677,89852,89855],{"href":89853,"rel":89854},"https://github.com/features/copilot",[681],"Github Copilot",[37,89857,89858],{},[677,89859,89862],{"href":89860,"rel":89861},"https://quillbot.com/",[681],"QuillBot",[22,89864,89865],{},"I spent some time over the break playing around with Midjourney and like ChatGPT the trick is understanding what to ask the service for. I was looking to create a new image for a landing page related to Spring Boot. After playing around with different prompts I came up with the following image which I really love.",[22,89867,89868],{},[653,89869],{"alt":89870,"src":89871},"Spring Boot AI","/images/blog/2023/01/01/spring_boot_ai.png",[22,89873,89874],{},"Going back to my writing blog posts about my YouTube videos. There has to be a solution here somewhere, just need to figure it out 😉",[26,89876,1499],{"id":1498},[22,89878,89879],{},"Another year has come and gone and after doing some reflecting I was able to sit down and put together this list of priorities for 2023. I look forward to revisiting this list at the end of the year to see what I actually followed through on.",{"title":57,"searchDepth":76,"depth":76,"links":89881},[89882,89883,89884,89885,89889,89890,89891,89892,89893],{"id":89647,"depth":76,"text":89648},{"id":37948,"depth":76,"text":37949},{"id":38009,"depth":76,"text":38010},{"id":89724,"depth":76,"text":8474,"children":89886},[89887,89888],{"id":89736,"depth":82,"text":89737},{"id":37930,"depth":82,"text":37931},{"id":89775,"depth":76,"text":89776},{"id":89793,"depth":76,"text":89794},{"id":89803,"depth":76,"text":89804},{"id":89819,"depth":76,"text":27663},{"id":1498,"depth":76,"text":1499},{"_id":89895,"path":89896,"title":89897,"description":89898,"meta":89899,"body":89904},"content/blog/2022/12/29/2022-reflections.md","/blog/2022/12/29/2022-reflections","2022 Reflections","In this article I am going to spend some time reflecting because I believe it's a good reminder of the good and the bad as I began to set some goals for next year.",{"slug":89900,"date":89901,"published":13,"tags":89902,"author":17,"cover":89903,"excerpt":-1},"2022-reflections","2022-12-29T16:00:00.000Z",[20701],"./jose-m-reyes-0GBxtiFvzXE-unsplash.jpg",{"type":19,"value":89905,"toc":90162},[89906,89909,89911,89920,89923,89931,89934,89978,89984,89986,89989,89992,89995,89998,90035,90039,90048,90052,90055,90058,90061,90064,90068,90071,90074,90077,90081,90096,90099,90102,90106,90125,90129,90132,90136,90150,90154,90157,90159],[22,89907,89908],{},"It’s hard to believe that another year has passed but I’m guessing this is something we say at the end of every year. I like to sit down over Christmas break and spend some time reflecting because I believe it's a good reminder of the good and the bad as I began to set some goals for next year.",[26,89910,37107],{"id":37106},[22,89912,89913,89914,89919],{},"This was one of my best years professionally and it began in January when ",[677,89915,89918],{"href":89916,"rel":89917},"https://www.danvega.dev/blog/2022/01/24/im-joining-vmware/",[681],"I started my dream job"," as a Spring Developer Advocate at VMware. I joined an amazing company and a team that I absolutely love working with every single day. I often joke that I was an advocate for Spring long before I joined VMware but this has literally been a dream come true.",[22,89921,89922],{},"I remember that feeling on Sunday evening of dreading the new work week and the stress that would follow. Now I sit down on Sunday with a smile on my face and plan out what I get to work on during the upcoming week. I absolutely love creating content and helping developers solve their problems.",[22,89924,89925,89926,89930],{},"When I joined DaShaun and I started talking about creating a place for Spring Developers to ask their questions. After some brainstorming sessions, we created Spring Office Hours, and honestly, it’s been one of the highlights of my year. This is a weekly live stream where we discuss what’s new in the Spring community and answer any questions you might have. If you want to learn more about the show head over to the ",[677,89927,89929],{"href":89704,"rel":89928},[681],"Tanzu Developer Center"," and watch previous episodes.",[22,89932,89933],{},"I spent a lot of this year learning as much as I could and teaching what I learned back to the community. My plan is to continue that in 2023 because the Spring ecosystem is vast and there is so much more I hope to learn. It was an amazing year of traveling and giving presentations on topics I am passionate about and it’s something I hope to continue in the new year. Here is a short list of some of the presentations I gave in 2022:",[915,89935,89936,89943,89950,89957,89964,89971],{},[37,89937,89938],{},[677,89939,89942],{"href":89940,"rel":89941},"https://tanzu.vmware.com/developer/springone-tour/2022/toronto/",[681],"SpringOne Tour Toronto",[37,89944,89945],{},[677,89946,89949],{"href":89947,"rel":89948},"https://us.vuejs.org/",[681],"VueConf US",[37,89951,89952],{},[677,89953,89956],{"href":89954,"rel":89955},"https://tanzu.vmware.com/developer/springone-tour/2022/seattle/",[681],"SpringOne Tour Seattle",[37,89958,89959],{},[677,89960,89963],{"href":89961,"rel":89962},"https://www.kcdc.info/",[681],"Kansas City Developer Conference (KCDC)",[37,89965,89966],{},[677,89967,89970],{"href":89968,"rel":89969},"https://www.codeonthebeach.com/",[681],"Code On The Beach",[37,89972,89973],{},[677,89974,89977],{"href":89975,"rel":89976},"https://tanzu.vmware.com/developer/springone-tour/2022/new-york/",[681],"SpringOne Tour NewYork",[22,89979,89980],{},[653,89981],{"alt":89982,"src":89983},"dan-nyc-talk.jpeg","/images/blog/2022/12/29/dan-nyc-talk.jpeg",[26,89985,37949],{"id":37948},[22,89987,89988],{},"I ended up crossing a huge milestone towards the end of the year and passing 20,000 subscribers on YouTube. I think I heard somewhere that only 1% of YouTubers hit this mark and that is just amazing company to be in 🤩 It’s been an amazing journey getting to this point and I have learned so much since I uploaded my first video in October of 2013.",[22,89990,89991],{},"I think what I am most excited about is that I have barely scratched the surface of what I could do with this channel. I know I have a lot of room to grow and I have big plans for my channel in the new year. Here is a video I published when I hit 20k thanking my subscribers and looking back at some of my older videos.",[36089,89993],{"id":89994},"hsMKOirkLZU",[22,89996,89997],{},"I ended up posting 71 videos this year which exceeded my lofty goal of 1x per week for a total of 52 videos. I also ended up live streaming 24 times which was the exact number I set as a goal at the beginning of the year. I'm proud of the content I published on my channel and across YouTube and here are a few of my most popular videos:",[915,89999,90000,90007,90014,90021,90028],{},[37,90001,90002],{},[677,90003,90006],{"href":90004,"rel":90005},"https://www.youtube.com/watch?v=4L4LEnawcO8",[681],"What’s new in Spring Boot 2.6",[37,90008,90009],{},[677,90010,90013],{"href":90011,"rel":90012},"https://www.youtube.com/watch?v=q_RLfOB7axQ",[681],"Building REST APIs in Spring Boot",[37,90015,90016],{},[677,90017,90020],{"href":90018,"rel":90019},"https://studio.youtube.com/video/KYNR5js2cXE/edit",[681],"Spring Security JWT: How to secure your Spring Boot REST APIs with JSON Web Tokens",[37,90022,90023],{},[677,90024,90027],{"href":90025,"rel":90026},"https://studio.youtube.com/video/s4X4SJv2RrU/edit",[681],"Spring Security without the WebSecurityConfigurerAdapter",[37,90029,90030],{},[677,90031,90034],{"href":90032,"rel":90033},"https://studio.youtube.com/video/TR254zh-f3c/edit",[681],"Spring Boot 3 - What’s new in Spring Framework 6 and Spring Boot 3.0",[26,90036,90038],{"id":90037},"_7-questions-to-successfully-reflect-on-2022","7 QUESTIONS TO SUCCESSFULLY REFLECT ON 2022",[22,90040,90041,90042,90047],{},"I’m a huge fan of Jay Shetty and his podcast. On a ",[677,90043,90046],{"href":90044,"rel":90045},"https://jayshetty.me/podcast/7-powerful-questions-to-successfully-reflect-on-2022-and-end-the-year-with-confidence/",[681],"recent episode",", he proposed answering the following 7 questions as a way to reflect on the past year. I thought this was a great idea to learn from it and take what I have learned into the new year.",[636,90049,90051],{"id":90050},"what-is-the-challenge-that-youve-overcome-this-year","What is the challenge that you’ve overcome this year?",[22,90053,90054],{},"I have been struggling with my health and my battle with type 2 diabetes. Towards the end of last year, my numbers were not good and they didn’t get better through the first half of this year. I ended up having to go on a 3rd medicine and that really started to scare me. Suddenly this wasn’t something I was managing and could begin to have pretty serious consequences.",[22,90056,90057],{},"After asking myself some hard questions and confronting some truths I realized that a big part of my problem was alcohol. In the middle of August, I decided that I was going to stop drinking. I didn’t know for how long and If I am being honest I still don’t know the answer to that question.",[22,90059,90060],{},"What I do know is that my A1C went from 11.1 all the way down to 5.6 in November and I am getting healthier every single day. I eat better, exercise regularly and I am much more focused on a daily basis. In the month of December, I am hoping to cross 100 miles of running indoors on a treadmill which is an all-time best for me by a long shot.",[22,90062,90063],{},"While I am thankful for this decision and my progress I know it's just the beginning. I want to be off of all medicine one day and manage this through good lifestyle choices. I don’t know if that’s possible but I am working towards that goal every single day.",[636,90065,90067],{"id":90066},"whats-a-surprise-you-dealt-with","What’s a surprise you dealt with?",[22,90069,90070],{},"A big surprise I had to deal with this year was getting Covid for the first time. We were very careful for a long time because when the pandemic hit my 2nd daughter was born. We ended up going on a family trip with our friends and their 2 kids to Hilton Head, SC.",[22,90072,90073],{},"On our way home I started feeling weak and honestly didn’t think much of it. The next day I still didn’t feel great and decided to take a test because I was supposed to travel the next day for my first in-person event in Chicago. What’s even crazier is that all of these people I spent every single hour with were just fine.",[22,90075,90076],{},"I was really bummed out that I had to miss my first SpringOne Tour event but I told myself it was just the first of many. I’m not sure what next year will bring but I hope to travel and talk code in a city near you!",[636,90078,90080],{"id":90079},"what-is-something-you-bought-this-year","What is something you bought this year?",[22,90082,90083,90084,90089,90090,90095],{},"I bought a ",[677,90085,90088],{"href":90086,"rel":90087},"https://amzn.to/3Vq4u2M",[681],"new treadmill"," last January and it was one of the best purchases I have ever made. I’m normally not a big fan of running inside but this one changed all of that for me. This treadmill came with an LCD and you could sign up for a service called ",[677,90091,90094],{"href":90092,"rel":90093},"https://www.ifit.com/",[681],"iFit"," which has guided workouts on it.",[22,90097,90098],{},"I really enjoyed the 5k & 10k series that took me all over the world. There is something very subversive about these runs and being transported to a remote island on the other side of the planet. I also enjoy that they are all different kinds of workouts like tempo, interval, and long runs. The trainer also changes the speed for you and the incline automatically aligns with the hills they are running on.",[22,90100,90101],{},"This is a reminder to invest in anything that makes you happy and for me, this treadmill has done a lot for me this year.",[636,90103,90105],{"id":90104},"whats-the-best-bookpodcast-you-readlistened-to","What’s the best book/podcast you read/listened to?",[22,90107,90108,90109,90114,90115,90120,90121,2755],{},"I discovered ",[677,90110,90113],{"href":90111,"rel":90112},"https://www.calnewport.com/",[681],"Cal Newport"," this year and for that reason, he is going to be both the best book and podcast I listened to this year. I really enjoyed his book ",[677,90116,90119],{"href":90117,"rel":90118},"https://amzn.to/3Cadi60",[681],"Deep Work"," which I wrote a review on in a ",[677,90122,90124],{"href":89782,"rel":90123},[681],"previous newsletter",[636,90126,90128],{"id":90127},"what-are-your-blind-spots-for-next-year","What are your blind spots for next year?",[22,90130,90131],{},"VMWare was acquired by Broadcom this year and this does leave a little bit of uncertainty for me. I am not sure what will happen because of this acquisition but a lot of that is out of my hands. I love the company I work for and the team I get to work with every single day. It’s because of this I am going to keep a positive attitude and keep on doing what I love to do until I am told otherwise.",[636,90133,90135],{"id":90134},"what-made-you-the-happiest-this-year","What made you the happiest this year?",[915,90137,90138,90141,90144,90147],{},[37,90139,90140],{},"My family and I got to take some really amazing trips this year. It was great to see so many beaches and the highlight was going to Disney World. My girls love to travel and I love seeing them happy.",[37,90142,90143],{},"Seeing my girls grow up and realizing just how freaking lucky I am to be their dad #girldad",[37,90145,90146],{},"My work makes me happy and the people I get to help every day puts a big smile on my face.",[37,90148,90149],{},"Running has improved my physical and mental health and makes me so happy.",[636,90151,90153],{"id":90152},"whos-the-person-you-couldnt-have-gotten-through-this-year-without","Who’s the person you couldn’t have gotten through this year without?",[22,90155,90156],{},"This is without a doubt, my wife. She is my person, my best friend, my love, and my rock. I always dreamed about the life I have now and I owe a lot of that to her ❤️",[26,90158,1499],{"id":1498},[22,90160,90161],{},"I had a lot of fun sitting down` and reflecting on the past year. Like every year it had its up and downs but I choose to focus on the positives and learn from my mistakes as I move into the new year. Be sure to stay on the lookout as I post my goals for 2023.",{"title":57,"searchDepth":76,"depth":76,"links":90163},[90164,90165,90166,90175],{"id":37106,"depth":76,"text":37107},{"id":37948,"depth":76,"text":37949},{"id":90037,"depth":76,"text":90038,"children":90167},[90168,90169,90170,90171,90172,90173,90174],{"id":90050,"depth":82,"text":90051},{"id":90066,"depth":82,"text":90067},{"id":90079,"depth":82,"text":90080},{"id":90104,"depth":82,"text":90105},{"id":90127,"depth":82,"text":90128},{"id":90134,"depth":82,"text":90135},{"id":90152,"depth":82,"text":90153},{"id":1498,"depth":76,"text":1499},{"_id":90177,"path":90178,"title":90179,"description":73331,"meta":90180,"body":90185},"content/blog/2022/12/19/spring-proxy-bean-methods.md","/blog/2022/12/19/spring-proxy-bean-methods","Spring Boot Configuration proxy bean methods",{"slug":90181,"date":90182,"published":13,"tags":90183,"author":17,"cover":90184,"excerpt":-1},"spring-proxy-bean-methods","2022-12-19T10:00:00.000Z",[2925],"./proxy-bean-methods.png",{"type":19,"value":90186,"toc":90810},[90187,90200,90204,90209,90216,90222,90226,90229,90340,90351,90355,90362,90422,90432,90571,90574,90580,90584,90589,90611,90614,90617,90721,90724,90799,90802,90804,90807],[22,90188,90189,90190,90192,90193,90196,90197,90199],{},"In this blog post, we will explore the configuration classes in Spring and explain how setting a property on the ",[59,90191,48869],{}," annotation of ",[59,90194,90195],{},"proxyBeanMethods=false"," can resolve certain issues. We will guide you through creating a few services that depend on a RestTemplate using the ",[59,90198,48869],{}," annotation. Along the way, we will examine a problem and how to address it.",[26,90201,90203],{"id":90202},"create-a-new-project-using-the-spring-initializr","Create a new project using the Spring Initializr",[22,90205,90206],{},[653,90207],{"alt":24606,"src":90208},"/images/blog/2022/12/19/spring-init.png",[22,90210,90211,90212,90215],{},"Let's start by creating a new project at ",[677,90213,2903],{"href":45295,"rel":90214},[681],". I have bookmarked this site, and I recommend you do the same. I am currently creating a Runners application for an upcoming Spring Workshop, and we'll use this to start the tutorial.",[22,90217,90218,90219,90221],{},"We'll choose Maven and Java, using the latest version of Spring Boot 3.0. We will create a group called ",[59,90220,23065],{}," with this project's artifact, Runners. The only dependency we need right now is the web, and we'll use a rest template underneath the hood.",[26,90223,90225],{"id":90224},"setting-up-the-services","Setting up the Services",[22,90227,90228],{},"Let's create a couple of classes that will be the services that we'll use in our application. These classes will require the use of a rest template. We'll create a Run Service and a Track Service in our application, each with a rest template as a dependency.",[52,90230,90232],{"className":54,"code":90231,"language":56,"meta":57,"style":57},"public class RunService {\n private final RestTemplate restTemplate;\n public RunService(RestTemplate restTemplate) {\n this.restTemplate = restTemplate;\n }\n}\n\npublic class TrackService {\n private final RestTemplate restTemplate;\n public TrackService(RestTemplate restTemplate) {\n this.restTemplate = restTemplate;\n }\n}\n",[59,90233,90234,90245,90254,90267,90279,90283,90287,90291,90302,90310,90322,90332,90336],{"__ignoreMap":57},[62,90235,90236,90238,90240,90243],{"class":64,"line":65},[62,90237,116],{"class":68},[62,90239,119],{"class":68},[62,90241,90242],{"class":122}," RunService",[62,90244,126],{"class":72},[62,90246,90247,90249,90251],{"class":64,"line":76},[62,90248,137],{"class":68},[62,90250,458],{"class":68},[62,90252,90253],{"class":72}," RestTemplate restTemplate;\n",[62,90255,90256,90258,90260,90263,90265],{"class":64,"line":82},[62,90257,194],{"class":68},[62,90259,90242],{"class":122},[62,90261,90262],{"class":72},"(RestTemplate ",[62,90264,55998],{"class":889},[62,90266,768],{"class":72},[62,90268,90269,90271,90274,90276],{"class":64,"line":89},[62,90270,2405],{"class":149},[62,90272,90273],{"class":72},".restTemplate ",[62,90275,146],{"class":68},[62,90277,90278],{"class":72}," restTemplate;\n",[62,90280,90281],{"class":64,"line":95},[62,90282,223],{"class":72},[62,90284,90285],{"class":64,"line":101},[62,90286,379],{"class":72},[62,90288,90289],{"class":64,"line":107},[62,90290,79],{"emptyLinePlaceholder":13},[62,90292,90293,90295,90297,90300],{"class":64,"line":113},[62,90294,116],{"class":68},[62,90296,119],{"class":68},[62,90298,90299],{"class":122}," TrackService",[62,90301,126],{"class":72},[62,90303,90304,90306,90308],{"class":64,"line":129},[62,90305,137],{"class":68},[62,90307,458],{"class":68},[62,90309,90253],{"class":72},[62,90311,90312,90314,90316,90318,90320],{"class":64,"line":134},[62,90313,194],{"class":68},[62,90315,90299],{"class":122},[62,90317,90262],{"class":72},[62,90319,55998],{"class":889},[62,90321,768],{"class":72},[62,90323,90324,90326,90328,90330],{"class":64,"line":156},[62,90325,2405],{"class":149},[62,90327,90273],{"class":72},[62,90329,146],{"class":68},[62,90331,90278],{"class":72},[62,90333,90334],{"class":64,"line":161},[62,90335,223],{"class":72},[62,90337,90338],{"class":64,"line":167},[62,90339,379],{"class":72},[22,90341,90342,90343,90345,90346,2755],{},"We're not annotating these with \"@Service\" because we'll create these using the \"app Bean\" annotation. If you’re new to Spring Beans and what the ",[59,90344,55429],{}," annotation does please check out my post on ",[677,90347,90350],{"href":90348,"rel":90349},"https://www.danvega.dev/blog/2017/05/17/spring-component-vs-bean/",[681],"Component vs Bean",[26,90352,90354],{"id":90353},"creating-a-configuration-class","Creating a Configuration Class",[22,90356,90357,90358,90361],{},"Let's create a configuration class called ",[59,90359,90360],{},"MyAppConfig"," We'll start by creating a bean of type RestTemplate, which will be used in both of our services.",[52,90363,90365],{"className":54,"code":90364,"language":56,"meta":57,"style":57},"@Configuration\npublic class MyAppConfig {\n @Bean\n public RestTemplate restTemplate() {\n return new RestTemplateBuilder().build();\n }\n}\n",[59,90366,90367,90373,90384,90390,90400,90414,90418],{"__ignoreMap":57},[62,90368,90369,90371],{"class":64,"line":65},[62,90370,942],{"class":72},[62,90372,11133],{"class":68},[62,90374,90375,90377,90379,90382],{"class":64,"line":76},[62,90376,116],{"class":68},[62,90378,119],{"class":68},[62,90380,90381],{"class":122}," MyAppConfig",[62,90383,126],{"class":72},[62,90385,90386,90388],{"class":64,"line":82},[62,90387,2143],{"class":72},[62,90389,2146],{"class":68},[62,90391,90392,90394,90396,90398],{"class":64,"line":89},[62,90393,194],{"class":68},[62,90395,55995],{"class":72},[62,90397,55998],{"class":122},[62,90399,206],{"class":72},[62,90401,90402,90404,90406,90408,90410,90412],{"class":64,"line":95},[62,90403,360],{"class":68},[62,90405,466],{"class":68},[62,90407,56009],{"class":122},[62,90409,3229],{"class":72},[62,90411,2189],{"class":122},[62,90413,822],{"class":72},[62,90415,90416],{"class":64,"line":101},[62,90417,223],{"class":72},[62,90419,90420],{"class":64,"line":107},[62,90421,379],{"class":72},[22,90423,90424,90425,19931,90428,90431],{},"We have created a Singleton of RestTemplate, which we can now use in both of our services. We can create two more beans called ",[59,90426,90427],{},"RunService",[59,90429,90430],{},"TrackService"," . We'll return a new instance of both of these services. The constructor in both of these services needs an instance of rest template, so we'll call that corresponding method.",[52,90433,90435],{"className":54,"code":90434,"language":56,"meta":57,"style":57},"@Configuration\npublic class MyAppConfig {\n @Bean\n public RestTemplate restTemplate() {\n return new RestTemplateBuilder().build();\n }\n\n @Bean\n public RunService runService() {\n return new RunService(restTemplate());\n }\n\n @Bean\n public TrackService trackService() {\n return new TrackService(restTemplate());\n }\n}\n",[59,90436,90437,90443,90453,90459,90469,90483,90487,90491,90497,90509,90523,90527,90531,90537,90549,90563,90567],{"__ignoreMap":57},[62,90438,90439,90441],{"class":64,"line":65},[62,90440,942],{"class":72},[62,90442,11133],{"class":68},[62,90444,90445,90447,90449,90451],{"class":64,"line":76},[62,90446,116],{"class":68},[62,90448,119],{"class":68},[62,90450,90381],{"class":122},[62,90452,126],{"class":72},[62,90454,90455,90457],{"class":64,"line":82},[62,90456,2143],{"class":72},[62,90458,2146],{"class":68},[62,90460,90461,90463,90465,90467],{"class":64,"line":89},[62,90462,194],{"class":68},[62,90464,55995],{"class":72},[62,90466,55998],{"class":122},[62,90468,206],{"class":72},[62,90470,90471,90473,90475,90477,90479,90481],{"class":64,"line":95},[62,90472,360],{"class":68},[62,90474,466],{"class":68},[62,90476,56009],{"class":122},[62,90478,3229],{"class":72},[62,90480,2189],{"class":122},[62,90482,822],{"class":72},[62,90484,90485],{"class":64,"line":101},[62,90486,223],{"class":72},[62,90488,90489],{"class":64,"line":107},[62,90490,79],{"emptyLinePlaceholder":13},[62,90492,90493,90495],{"class":64,"line":113},[62,90494,2143],{"class":72},[62,90496,2146],{"class":68},[62,90498,90499,90501,90504,90507],{"class":64,"line":129},[62,90500,194],{"class":68},[62,90502,90503],{"class":72}," RunService ",[62,90505,90506],{"class":122},"runService",[62,90508,206],{"class":72},[62,90510,90511,90513,90515,90517,90519,90521],{"class":64,"line":134},[62,90512,360],{"class":68},[62,90514,466],{"class":68},[62,90516,90242],{"class":122},[62,90518,2109],{"class":72},[62,90520,55998],{"class":122},[62,90522,1091],{"class":72},[62,90524,90525],{"class":64,"line":156},[62,90526,223],{"class":72},[62,90528,90529],{"class":64,"line":161},[62,90530,79],{"emptyLinePlaceholder":13},[62,90532,90533,90535],{"class":64,"line":167},[62,90534,2143],{"class":72},[62,90536,2146],{"class":68},[62,90538,90539,90541,90544,90547],{"class":64,"line":173},[62,90540,194],{"class":68},[62,90542,90543],{"class":72}," TrackService ",[62,90545,90546],{"class":122},"trackService",[62,90548,206],{"class":72},[62,90550,90551,90553,90555,90557,90559,90561],{"class":64,"line":179},[62,90552,360],{"class":68},[62,90554,466],{"class":68},[62,90556,90299],{"class":122},[62,90558,2109],{"class":72},[62,90560,55998],{"class":122},[62,90562,1091],{"class":72},[62,90564,90565],{"class":64,"line":185},[62,90566,223],{"class":72},[62,90568,90569],{"class":64,"line":191},[62,90570,379],{"class":72},[22,90572,90573],{},"When we run our application, we have no errors, and everything works fine. However, Spring creates a proxy class using the CGlib code generation library. The reason behind this is Spring doesn't want to call the method that returns a new RestTemplate every time a bean is created. It only wants one instance of RestTemplate. When we create a new service class, it retrieves the already-created instance of RestTemplate using the proxy class.",[22,90575,90576],{},[653,90577],{"alt":90578,"src":90579},"Proxy","/images/blog/2022/12/19/proxy.png",[26,90581,90583],{"id":90582},"fixing-the-problem","Fixing the Problem",[22,90585,90586,90587,43329],{},"Using a proxy class most of the time isn’t an issue, but we want to get rid of it because it's using the CGlib library, which won’t work for native executables. I’m also a big fan of avoiding proxies in any situation I can so even If you’re not building a native executable I find this to be a best practice. We can disable this by setting a property proxy bean methods equal to false on the ",[59,90588,48869],{},[52,90590,90592],{"className":54,"code":90591,"language":56,"meta":57,"style":57},"@Configuration(proxyBeanMethods = false)\n",[59,90593,90594],{"__ignoreMap":57},[62,90595,90596,90598,90600,90602,90605,90607,90609],{"class":64,"line":65},[62,90597,942],{"class":72},[62,90599,1998],{"class":68},[62,90601,2109],{"class":72},[62,90603,90604],{"class":149},"proxyBeanMethods",[62,90606,2556],{"class":68},[62,90608,1165],{"class":149},[62,90610,2212],{"class":72},[22,90612,90613],{},"Now we have a new problem. If we set proxyBeanMethods to false, every time we create a service, it calls the method that returns a new RestTemplate instance. This eliminates the benefit we previously had of only creating one instance of RestTemplate.",[22,90615,90616],{},"The solution to this problem is to make RestTemplate an argument to our service classes. Spring already has an instance of RestTemplate since we've created it using the MyAppConfig class. Therefore, we can just pass an instance of RestTemplate to the constructor of our service classes.",[52,90618,90619],{"className":54,"code":90231,"language":56,"meta":57,"style":57},[59,90620,90621,90631,90639,90651,90661,90665,90669,90673,90683,90691,90703,90713,90717],{"__ignoreMap":57},[62,90622,90623,90625,90627,90629],{"class":64,"line":65},[62,90624,116],{"class":68},[62,90626,119],{"class":68},[62,90628,90242],{"class":122},[62,90630,126],{"class":72},[62,90632,90633,90635,90637],{"class":64,"line":76},[62,90634,137],{"class":68},[62,90636,458],{"class":68},[62,90638,90253],{"class":72},[62,90640,90641,90643,90645,90647,90649],{"class":64,"line":82},[62,90642,194],{"class":68},[62,90644,90242],{"class":122},[62,90646,90262],{"class":72},[62,90648,55998],{"class":889},[62,90650,768],{"class":72},[62,90652,90653,90655,90657,90659],{"class":64,"line":89},[62,90654,2405],{"class":149},[62,90656,90273],{"class":72},[62,90658,146],{"class":68},[62,90660,90278],{"class":72},[62,90662,90663],{"class":64,"line":95},[62,90664,223],{"class":72},[62,90666,90667],{"class":64,"line":101},[62,90668,379],{"class":72},[62,90670,90671],{"class":64,"line":107},[62,90672,79],{"emptyLinePlaceholder":13},[62,90674,90675,90677,90679,90681],{"class":64,"line":113},[62,90676,116],{"class":68},[62,90678,119],{"class":68},[62,90680,90299],{"class":122},[62,90682,126],{"class":72},[62,90684,90685,90687,90689],{"class":64,"line":129},[62,90686,137],{"class":68},[62,90688,458],{"class":68},[62,90690,90253],{"class":72},[62,90692,90693,90695,90697,90699,90701],{"class":64,"line":134},[62,90694,194],{"class":68},[62,90696,90299],{"class":122},[62,90698,90262],{"class":72},[62,90700,55998],{"class":889},[62,90702,768],{"class":72},[62,90704,90705,90707,90709,90711],{"class":64,"line":156},[62,90706,2405],{"class":149},[62,90708,90273],{"class":72},[62,90710,146],{"class":68},[62,90712,90278],{"class":72},[62,90714,90715],{"class":64,"line":161},[62,90716,223],{"class":72},[62,90718,90719],{"class":64,"line":167},[62,90720,379],{"class":72},[22,90722,90723],{},"Lastly, we need to modify our configuration class to reflect this:",[52,90725,90727],{"className":54,"code":90726,"language":56,"meta":57,"style":57},"@Bean\npublic RunService runService(RestTemplate restTemplate) {\n return new RunService(restTemplate);\n}\n\n@Bean\npublic TrackService trackService(RestTemplate restTemplate) {\n return new TrackService(restTemplate);\n}31\n",[59,90728,90729,90735,90746,90757,90761,90765,90771,90781,90791],{"__ignoreMap":57},[62,90730,90731,90733],{"class":64,"line":65},[62,90732,942],{"class":72},[62,90734,2146],{"class":68},[62,90736,90737,90739,90741,90743],{"class":64,"line":76},[62,90738,116],{"class":68},[62,90740,90503],{"class":72},[62,90742,90506],{"class":122},[62,90744,90745],{"class":72},"(RestTemplate restTemplate) {\n",[62,90747,90748,90750,90752,90754],{"class":64,"line":82},[62,90749,2599],{"class":68},[62,90751,466],{"class":68},[62,90753,90242],{"class":122},[62,90755,90756],{"class":72},"(restTemplate);\n",[62,90758,90759],{"class":64,"line":89},[62,90760,379],{"class":72},[62,90762,90763],{"class":64,"line":95},[62,90764,79],{"emptyLinePlaceholder":13},[62,90766,90767,90769],{"class":64,"line":101},[62,90768,942],{"class":72},[62,90770,2146],{"class":68},[62,90772,90773,90775,90777,90779],{"class":64,"line":107},[62,90774,116],{"class":68},[62,90776,90543],{"class":72},[62,90778,90546],{"class":122},[62,90780,90745],{"class":72},[62,90782,90783,90785,90787,90789],{"class":64,"line":113},[62,90784,2599],{"class":68},[62,90786,466],{"class":68},[62,90788,90299],{"class":122},[62,90790,90756],{"class":72},[62,90792,90793,90796],{"class":64,"line":129},[62,90794,90795],{"class":72},"}",[62,90797,90798],{"class":149},"31\n",[22,90800,90801],{},"Now when we run our application, the CGlib proxy class is not used, and we only create one instance of RestTemplate.",[26,90803,1499],{"id":1498},[22,90805,90806],{},"By setting the \"proxy Bean methods\" property to \"false,\" we can eliminate the proxy class. This is something you must do if you’re creating a native executable but I also find it a best practice if it is something you can do. Removing overhead such as the proxy class can help improve your application's performance. I hope this blog post has helped you gain a better understanding of Spring's configuration classes.",[1527,90808,90809],{},"html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .spoOn, html code.shiki .spoOn{--shiki-default:#E36209;--shiki-dark:#FFAB70;--shiki-sepia:#E36209}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}",{"title":57,"searchDepth":76,"depth":76,"links":90811},[90812,90813,90814,90815,90816],{"id":90202,"depth":76,"text":90203},{"id":90224,"depth":76,"text":90225},{"id":90353,"depth":76,"text":90354},{"id":90582,"depth":76,"text":90583},{"id":1498,"depth":76,"text":1499},{"_id":90818,"path":90819,"title":90820,"description":90821,"meta":90822,"body":90827},"content/blog/2022/12/16/spring-response-entity.md","/blog/2022/12/16/spring-response-entity","Spring Response Entity - How to customize the response in Spring Boot","In this tutorial, you will learn what the ResponseEntity class is and how it can be used to customize the response in your Spring Boot application.",{"slug":90823,"date":90824,"published":13,"tags":90825,"author":17,"cover":90826,"excerpt":-1},"spring-response-entity","2022-12-16T12:00:00.000Z",[2925],"./spring-response-entity.png",{"type":19,"value":90828,"toc":91178},[90829,90838,90842,90845,90850,90856,90859,90863,90875,90878,90882,90885,90901,90904,90950,90953,91010,91013,91017,91020,91102,91105,91109,91116,91167,91170,91172,91175],[22,90830,90831,90832,90837],{},"Recently, I sent out a ",[677,90833,90836],{"href":90834,"rel":90835},"https://twitter.com/therealdanvega/status/1599814600463355906",[681],"tweet"," that received a lot of attention. My tweet discussed the use of the ResponseEntity class in Spring framework and when to use it in your projects. In this blog post, I'll go through the ResponseEntity class in detail, and show you an example of how to use it in your next application.",[26,90839,90841],{"id":90840},"the-tweet","The Tweet",[22,90843,90844],{},"Here's the tweet I sent out:",[29685,90846,90847],{},[22,90848,90849],{},"I often see spring developers returning a ResponseEntity and not manipulating the response. If all you're going to do is this and just return the body, just return that as the return type. If you need to customize the response and you need to add headers or change the status code, you can use a ResponseEntity as the return type.",[22,90851,90852],{},[653,90853],{"alt":90854,"src":90855},"Spring Response Entity Tweet","/images/blog/2022/12/16/response-entity-tweet.png",[22,90857,90858],{},"This tweet got more than 600 likes, 57 retweets, and many comments. Let's get into the ResponseEntity class and see what it's all about.",[26,90860,90862],{"id":90861},"what-is-responseentity","What is ResponseEntity?",[22,90864,90865,90866,90871,90872,90874],{},"According to the ",[677,90867,90870],{"href":90868,"rel":90869},"https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-chapter.view-based-rendering",[681],"Spring framework API docs",", ResponseEntity is an extension of the HttpEntity class that adds an HTTP status code. It is used in the RestTemplate as well as in ",[59,90873,55837],{}," methods. You can also use it in Spring MVC as the return value from a controller method. RestController, in fact, is a combination of @Controller and another annotation, so ResponseEntity is useful when you're building a REST API.",[22,90876,90877],{},"Let's understand ResponseEntity better with an example.",[26,90879,90881],{"id":90880},"activity-tracker-example","Activity Tracker Example",[22,90883,90884],{},"I'll use IntelliJ IDEA for this demonstration. Suppose we have a simple project for an activity tracker that records running activities. Each activity has a title, start time, and finish time. I've set up a simple Spring boot project with an ActivityController and an ActivityRepository using Spring Data.",[52,90886,90888],{"className":54,"code":90887,"language":56,"meta":57,"style":57},"public record Activity(Integer id, String title, LocalDateTime started, LocalDateTime completed) { }\n",[59,90889,90890],{"__ignoreMap":57},[62,90891,90892,90894,90896,90898],{"class":64,"line":65},[62,90893,116],{"class":68},[62,90895,2996],{"class":68},[62,90897,36690],{"class":122},[62,90899,90900],{"class":72},"(Integer id, String title, LocalDateTime started, LocalDateTime completed) { }\n",[22,90902,90903],{},"Let's create a method that allows us to get all the activities in the database.",[52,90905,90907],{"className":54,"code":90906,"language":56,"meta":57,"style":57},"@GetMapping\npublic List\u003CActivity> findAll() {\n return activityRepository.findAll(); //returns all activities from the repository\n}\n",[59,90908,90909,90915,90932,90946],{"__ignoreMap":57},[62,90910,90911,90913],{"class":64,"line":65},[62,90912,942],{"class":72},[62,90914,47319],{"class":68},[62,90916,90917,90919,90921,90923,90926,90928,90930],{"class":64,"line":76},[62,90918,116],{"class":68},[62,90920,3835],{"class":72},[62,90922,760],{"class":68},[62,90924,90925],{"class":72},"Activity",[62,90927,2583],{"class":68},[62,90929,44197],{"class":122},[62,90931,206],{"class":72},[62,90933,90934,90936,90939,90941,90943],{"class":64,"line":82},[62,90935,2599],{"class":68},[62,90937,90938],{"class":72}," activityRepository.",[62,90940,10287],{"class":122},[62,90942,472],{"class":72},[62,90944,90945],{"class":85},"//returns all activities from the repository\n",[62,90947,90948],{"class":64,"line":89},[62,90949,379],{"class":72},[22,90951,90952],{},"This method returns a list of activities. A common pattern I see is to change the return type of this method to ResponseEntity without manipulating the response.",[52,90954,90956],{"className":54,"code":90955,"language":56,"meta":57,"style":57},"@GetMapping\npublic ResponseEntity\u003CList\u003CActivity>> findAll() {\n return ResponseEntity.ok(activityRepository.findAll()); // do NOT do this\n}\n",[59,90957,90958,90964,90985,91006],{"__ignoreMap":57},[62,90959,90960,90962],{"class":64,"line":65},[62,90961,942],{"class":72},[62,90963,47319],{"class":68},[62,90965,90966,90968,90971,90973,90975,90977,90979,90981,90983],{"class":64,"line":76},[62,90967,116],{"class":68},[62,90969,90970],{"class":72}," ResponseEntity",[62,90972,760],{"class":68},[62,90974,59063],{"class":72},[62,90976,760],{"class":68},[62,90978,90925],{"class":72},[62,90980,5632],{"class":68},[62,90982,44197],{"class":122},[62,90984,206],{"class":72},[62,90986,90987,90989,90992,90995,90998,91000,91003],{"class":64,"line":82},[62,90988,2599],{"class":68},[62,90990,90991],{"class":72}," ResponseEntity.",[62,90993,90994],{"class":122},"ok",[62,90996,90997],{"class":72},"(activityRepository.",[62,90999,10287],{"class":122},[62,91001,91002],{"class":72},"()); ",[62,91004,91005],{"class":85},"// do NOT do this\n",[62,91007,91008],{"class":64,"line":89},[62,91009,379],{"class":72},[22,91011,91012],{},"Returning either a list of activities or a ResponseEntity list of activities generates the same output, so using ResponseEntity in this case is redundant. I also need to make sure I point out there there is absolutely nothing wrong with the code above.",[636,91014,91016],{"id":91015},"when-to-use-responseentity","When to Use ResponseEntity",[22,91018,91019],{},"A ResponseEntity becomes useful when you want to manipulate the status code or add headers to the response. In this case, it makes sense to use ResponseEntity, as you cannot add new headers if you're just returning the list of activities:",[52,91021,91023],{"className":54,"code":91022,"language":56,"meta":57,"style":57},"@GetMapping\npublic ResponseEntity\u003CList\u003CActivity>> findAll() {\n HttpHeaders responseHeaders = new HttpHeaders();\n responseHeaders.set(\"My-Custom-Header\", \"My-Custom-Value\");\n return new ResponseEntity\u003C>(activityRepository.findAll(), responseHeaders, HttpStatus.OK);\n}\n",[59,91024,91025,91031,91051,91065,91084,91098],{"__ignoreMap":57},[62,91026,91027,91029],{"class":64,"line":65},[62,91028,942],{"class":72},[62,91030,47319],{"class":68},[62,91032,91033,91035,91037,91039,91041,91043,91045,91047,91049],{"class":64,"line":76},[62,91034,116],{"class":68},[62,91036,90970],{"class":72},[62,91038,760],{"class":68},[62,91040,59063],{"class":72},[62,91042,760],{"class":68},[62,91044,90925],{"class":72},[62,91046,5632],{"class":68},[62,91048,44197],{"class":122},[62,91050,206],{"class":72},[62,91052,91053,91056,91058,91060,91063],{"class":64,"line":82},[62,91054,91055],{"class":72}," HttpHeaders responseHeaders ",[62,91057,146],{"class":68},[62,91059,466],{"class":68},[62,91061,91062],{"class":122}," HttpHeaders",[62,91064,822],{"class":72},[62,91066,91067,91070,91072,91074,91077,91079,91082],{"class":64,"line":89},[62,91068,91069],{"class":72}," responseHeaders.",[62,91071,47595],{"class":122},[62,91073,2109],{"class":72},[62,91075,91076],{"class":1675},"\"My-Custom-Header\"",[62,91078,976],{"class":72},[62,91080,91081],{"class":1675},"\"My-Custom-Value\"",[62,91083,1133],{"class":72},[62,91085,91086,91088,91090,91093,91095],{"class":64,"line":95},[62,91087,2599],{"class":68},[62,91089,466],{"class":68},[62,91091,91092],{"class":72}," ResponseEntity\u003C>(activityRepository.",[62,91094,10287],{"class":122},[62,91096,91097],{"class":72},"(), responseHeaders, HttpStatus.OK);\n",[62,91099,91100],{"class":64,"line":101},[62,91101,379],{"class":72},[22,91103,91104],{},"Now, when you make a request to this method, you'll see that the custom header is set in the response.",[636,91106,91108],{"id":91107},"changing-the-status-code","Changing the Status Code",[22,91110,91111,91112,91115],{},"You might want to change the status code when you create a new activity. In this case, you can use the ",[59,91113,91114],{},"@ResponseStatus"," annotation to set a different status code. For example, if you want to return a 201 (Created) status code, you can do this:",[52,91117,91119],{"className":54,"code":91118,"language":56,"meta":57,"style":57},"@PostMapping\n@ResponseStatus(HttpStatus.CREATED)\npublic Activity createActivity(@RequestBody Activity activity) {\n return activityRepository.save(activity);\n}\n",[59,91120,91121,91127,91135,91152,91163],{"__ignoreMap":57},[62,91122,91123,91125],{"class":64,"line":65},[62,91124,942],{"class":72},[62,91126,41533],{"class":68},[62,91128,91129,91131,91133],{"class":64,"line":76},[62,91130,942],{"class":72},[62,91132,41540],{"class":68},[62,91134,41543],{"class":72},[62,91136,91137,91139,91142,91145,91147,91149],{"class":64,"line":82},[62,91138,116],{"class":68},[62,91140,91141],{"class":72}," Activity ",[62,91143,91144],{"class":122},"createActivity",[62,91146,2475],{"class":72},[62,91148,2478],{"class":68},[62,91150,91151],{"class":72}," Activity activity) {\n",[62,91153,91154,91156,91158,91160],{"class":64,"line":89},[62,91155,2599],{"class":68},[62,91157,90938],{"class":72},[62,91159,22562],{"class":122},[62,91161,91162],{"class":72},"(activity);\n",[62,91164,91165],{"class":64,"line":95},[62,91166,379],{"class":72},[22,91168,91169],{},"However, if the status code you want to set is conditional on some logic, then using ResponseEntity is the right choice.",[26,91171,32553],{"id":32552},[22,91173,91174],{},"In summary, ResponseEntity is useful when you need to manipulate the status code or add custom headers to your response. It's not necessary to use ResponseEntity in other cases, such as when you're simply returning the data. A common argument I hear for using ResponseEntity is to be consistent with the return type for all methods. I’m not a big proponent of being consistent for consistent sake. This discussion on when to use ResponseEntity has been a valuable learning experience for me, and I hope it's been helpful for you too.",[1527,91176,91177],{},"html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .s4-Ip, html code.shiki .s4-Ip{--shiki-default:#6A737D;--shiki-dark:#6A737D;--shiki-sepia:#6A737D}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}",{"title":57,"searchDepth":76,"depth":76,"links":91179},[91180,91181,91182,91186],{"id":90840,"depth":76,"text":90841},{"id":90861,"depth":76,"text":90862},{"id":90880,"depth":76,"text":90881,"children":91183},[91184,91185],{"id":91015,"depth":82,"text":91016},{"id":91107,"depth":82,"text":91108},{"id":32552,"depth":76,"text":32553},{"_id":91188,"path":91189,"title":91190,"description":91191,"meta":91192,"body":91197},"content/blog/2022/12/02/aws-lambda-snapstart-spring.md","/blog/2022/12/02/aws-lambda-snapstart-spring","AWS Lambda SnapStart for Spring Developers","AWS Lambda SnapStart is a new performance optimization developed by AWS that can significantly improve the startup time for your applications.",{"slug":91193,"date":91194,"published":13,"tags":91195,"author":17,"cover":91196,"excerpt":-1},"aws-lambda-snapstart-spring","2022-12-02T08:00:00.000Z",[47077,2925],"./aws_lambda_snapstart.png",{"type":19,"value":91198,"toc":91391},[91199,91207,91210,91212,91225,91230,91233,91237,91240,91294,91297,91301,91304,91354,91358,91361,91368,91375,91378,91380,91383,91386,91388],[22,91200,91201,91206],{},[677,91202,91205],{"href":91203,"rel":91204},"https://aws.amazon.com/blogs/compute/starting-up-faster-with-aws-lambda-snapstart/",[681],"AWS Lambda SnapStart"," is a new performance optimization developed by AWS that can significantly improve the startup time for your applications. Currently, it is only available for Java in certain regions, but Java is usually the runtime affected by cold start times. This feature delivers up to 10x faster function startup times for latency-sensitive Java applications at no extra cost and with no or minimal code changes. That's really nice!",[22,91208,91209],{},"In this blog post, we'll create a new project, include Spring Boot, and Spring Cloud Function. We'll talk about writing this function, pushing it to AWS Lambda, and then enabling SnapStart. Finally, we'll compare the init time before and after SnapStart.",[26,91211,34881],{"id":34880},[22,91213,91214,91215,91218,91219,19931,91221,91224],{},"To create a new project, head over to ",[677,91216,2903],{"href":2901,"rel":91217},[681]," and choose Maven as your build tool, Java as your language, and Spring Boot 2.7.6 because AWS doesn't support Java 17 yet. Make sure to add ",[59,91220,27737],{},[59,91222,91223],{},"Spring Cloud Function"," as your project dependencies.",[22,91226,91227],{},[653,91228],{"alt":24606,"src":91229},"/images/blog/2022/12/02/start-spring-io.png",[22,91231,91232],{},"After generating the project, open it up in your favorite IDE.",[26,91234,91236],{"id":91235},"writing-the-function","Writing the Function",[22,91238,91239],{},"In your main application class, create a new Bean with a Function that takes in a string and reverses it. Here's an example implementation:",[52,91241,91243],{"className":54,"code":91242,"language":56,"meta":57,"style":57},"@Bean\npublic Function\u003CString, String> reverse() {\n return string -> new StringBuilder(string).reverse().toString();\n}\n",[59,91244,91245,91251,91267,91290],{"__ignoreMap":57},[62,91246,91247,91249],{"class":64,"line":65},[62,91248,942],{"class":72},[62,91250,2146],{"class":68},[62,91252,91253,91255,91257,91259,91261,91263,91265],{"class":64,"line":76},[62,91254,116],{"class":68},[62,91256,78536],{"class":72},[62,91258,760],{"class":68},[62,91260,78541],{"class":72},[62,91262,2583],{"class":68},[62,91264,78546],{"class":122},[62,91266,206],{"class":72},[62,91268,91269,91271,91273,91275,91277,91279,91282,91284,91286,91288],{"class":64,"line":82},[62,91270,2599],{"class":68},[62,91272,58343],{"class":72},[62,91274,800],{"class":68},[62,91276,466],{"class":68},[62,91278,78562],{"class":122},[62,91280,91281],{"class":72},"(string).",[62,91283,78514],{"class":122},[62,91285,3229],{"class":72},[62,91287,23175],{"class":122},[62,91289,822],{"class":72},[62,91291,91292],{"class":64,"line":89},[62,91293,379],{"class":72},[22,91295,91296],{},"Now you can test your application locally to make sure the function works. Once you know the function works it’s time to deploy it to AWS Lambda.",[26,91298,91300],{"id":91299},"building-and-deploying-to-aws-lambda","Building and Deploying to AWS Lambda",[22,91302,91303],{},"To build and deploy your function to AWS Lambda, follow these steps:",[34,91305,91306,91317,91323,91329,91338,91348],{},[37,91307,91308,91309,91311,91312,2755],{},"Add the Spring Cloud Function Adapter for AWS dependency to your ",[59,91310,1765],{},". Find more details in this article: ",[677,91313,91316],{"href":91314,"rel":91315},"https://tanzu.vmware.com/developer/guides/spring/spring-cloud-function/#installation",[681],"Serverless Spring",[37,91318,91319,91320,91322],{},"Modify your build plugins section in ",[59,91321,1765],{}," as shown in the Serverless Spring article.",[37,91324,91325,91326,91328],{},"Run ",[59,91327,78615],{}," in your terminal to build your project.",[37,91330,91331,91332,91337],{},"Head over to the ",[677,91333,91336],{"href":91334,"rel":91335},"https://aws.amazon.com/console/",[681],"AWS Management Console"," and create a new function, authoring it from scratch. Choose the Coretto 11 runtime while creating the new function.",[37,91339,91340,91341,91344,91345,91347],{},"Upload the built ",[59,91342,91343],{},".jar"," file from the ",[59,91346,3651],{}," directory to your function.",[37,91349,91350,91351,2755],{},"Set the handler for your function to ",[59,91352,91353],{},"org.springframework.cloud.function.adapter.aws.FunctionInvoker::handleRequest",[26,91355,91357],{"id":91356},"testing-your-function-and-enabling-snapstart","Testing Your Function and Enabling SnapStart",[22,91359,91360],{},"Now that your function is deployed to AWS Lambda, test it and observe the init duration. After the first run, the init duration should improve significantly.",[22,91362,91363,91364,91367],{},"To enable SnapStart, go to your function's configuration and edit the ",[59,91365,91366],{},"Runtime Settings",". Set the supported runtime to Java 11 and turn on the SnapStart option. Save your changes and publish a new version of your function.",[22,91369,91370,91371,91374],{},"After enabling SnapStart, test your function again and observe the ",[59,91372,91373],{},"Restore duration",". You should notice a significant decrease compared to the original init duration.",[22,91376,91377],{},"For example, your init duration may have gone from around 5 seconds to just 351 milliseconds. This is a huge improvement, and it's an exciting new feature for serverless Java developers.",[26,91379,1499],{"id":1498},[22,91381,91382],{},"AWS Lambda SnapStart offers a great performance improvement for Java applications with minimal to no code changes required. This new feature will make serverless Java development more accessible and efficient, especially for latency-sensitive applications.",[22,91384,91385],{},"If you found value in this blog post, please consider signing up for my FREE weekly newsletter below. Thank you and as always friends…",[22,91387,80332],{},[1527,91389,91390],{},"html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}",{"title":57,"searchDepth":76,"depth":76,"links":91392},[91393,91394,91395,91396,91397],{"id":34880,"depth":76,"text":34881},{"id":91235,"depth":76,"text":91236},{"id":91299,"depth":76,"text":91300},{"id":91356,"depth":76,"text":91357},{"id":1498,"depth":76,"text":1499},{"_id":91399,"path":91400,"title":91401,"description":91402,"meta":91403,"body":91408},"content/blog/2022/12/01/spring-security-6.md","/blog/2022/12/01/spring-security-6","What's new in Spring Security 6","In this article we will discuss the new features of Spring Security 6 and create a new Spring Boot 3 project together.",{"slug":91404,"date":91405,"published":13,"tags":91406,"author":17,"cover":91407,"excerpt":-1},"spring-security-6","2022-12-01T16:00:00.000Z",[2925,10914],"./spring_security_6.png",{"type":19,"value":91409,"toc":91843},[91410,91413,91415,91426,91431,91435,91445,91518,91522,91535,91662,91668,91672,91691,91698,91820,91826,91828,91836,91841],[22,91411,91412],{},"This article discusses the new features of Spring Security 6. If you haven't heard yet, Spring Boot 3.0 has been released, and if you include Spring Security as a dependency, you will now get Spring Security Version 6. In this blog post, we will delve into three important changes in Spring Security 6 and create a new Spring Boot 3 project together.",[26,91414,2874],{"id":2873},[915,91416,91417,91420],{},[37,91418,91419],{},"Make sure you're using Java 17, as the baseline for Spring Boot 3 and Spring Security 6 is Java 17.",[37,91421,34891,91422,91425],{},[677,91423,2903],{"href":2901,"rel":91424},[681]," to create a new Spring Boot 3 project with the Spring Security dependency.",[22,91427,91428],{},[653,91429],{"alt":78726,"src":91430},"/images/blog/2022/12/01/spring-init.png",[26,91432,91434],{"id":91433},"websecurityconfigureradapter-removed","WebSecurityConfigurerAdapter Removed",[22,91436,91437,91438,91441,91442,91444],{},"In previous versions of Spring Security, you had to extend the ",[59,91439,91440],{},"WebSecurityConfigurerAdapter"," class to configure security settings. This class has been deprecated and removed in Spring Security 6. Instead, you should now take a more component-based approach and create a bean of type ",[59,91443,80940],{},". Here's an example:",[52,91446,91448],{"className":54,"code":91447,"language":56,"meta":57,"style":57},"@EnableWebSecurity\n@Configuration\npublic class SecurityConfig {\n\n @Bean\n public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n return http.build();\n }\n}\n",[59,91449,91450,91456,91462,91472,91476,91482,91500,91510,91514],{"__ignoreMap":57},[62,91451,91452,91454],{"class":64,"line":65},[62,91453,942],{"class":72},[62,91455,11461],{"class":68},[62,91457,91458,91460],{"class":64,"line":76},[62,91459,942],{"class":72},[62,91461,11133],{"class":68},[62,91463,91464,91466,91468,91470],{"class":64,"line":82},[62,91465,116],{"class":68},[62,91467,119],{"class":68},[62,91469,11470],{"class":122},[62,91471,126],{"class":72},[62,91473,91474],{"class":64,"line":89},[62,91475,79],{"emptyLinePlaceholder":13},[62,91477,91478,91480],{"class":64,"line":95},[62,91479,2143],{"class":72},[62,91481,2146],{"class":68},[62,91483,91484,91486,91488,91490,91492,91494,91496,91498],{"class":64,"line":101},[62,91485,194],{"class":68},[62,91487,15452],{"class":72},[62,91489,11490],{"class":122},[62,91491,11493],{"class":72},[62,91493,11496],{"class":889},[62,91495,5024],{"class":72},[62,91497,11501],{"class":68},[62,91499,11504],{"class":72},[62,91501,91502,91504,91506,91508],{"class":64,"line":107},[62,91503,360],{"class":68},[62,91505,84623],{"class":72},[62,91507,2189],{"class":122},[62,91509,822],{"class":72},[62,91511,91512],{"class":64,"line":113},[62,91513,223],{"class":72},[62,91515,91516],{"class":64,"line":129},[62,91517,379],{"class":72},[26,91519,91521],{"id":91520},"authorize-http-requests","Authorize Http Requests",[22,91523,9643,91524,91526,91527,91529,91530,91532,91533,1266],{},[59,91525,84626],{},", which has been deprecated, you should now use ",[59,91528,11533],{},". This method is part of the ",[59,91531,80990],{}," configuration and allows you to configure fine-grained request matching for access control. Here's an example of how to use ",[59,91534,11533],{},[52,91536,91538],{"className":54,"code":91537,"language":56,"meta":57,"style":57},"@EnableWebSecurity\n@Configuration\npublic class SecurityConfig {\n\n @Bean\n public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n return http.authorizeHttpRequests(auth -> auth\n .requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll()\n .anyRequest().authenticated())\n .formLogin();\n .build();\n }\n}\n",[59,91539,91540,91546,91552,91562,91566,91572,91590,91604,91626,91638,91646,91654,91658],{"__ignoreMap":57},[62,91541,91542,91544],{"class":64,"line":65},[62,91543,942],{"class":72},[62,91545,11461],{"class":68},[62,91547,91548,91550],{"class":64,"line":76},[62,91549,942],{"class":72},[62,91551,11133],{"class":68},[62,91553,91554,91556,91558,91560],{"class":64,"line":82},[62,91555,116],{"class":68},[62,91557,119],{"class":68},[62,91559,11470],{"class":122},[62,91561,126],{"class":72},[62,91563,91564],{"class":64,"line":89},[62,91565,79],{"emptyLinePlaceholder":13},[62,91567,91568,91570],{"class":64,"line":95},[62,91569,2143],{"class":72},[62,91571,2146],{"class":68},[62,91573,91574,91576,91578,91580,91582,91584,91586,91588],{"class":64,"line":101},[62,91575,194],{"class":68},[62,91577,15452],{"class":72},[62,91579,11490],{"class":122},[62,91581,11493],{"class":72},[62,91583,11496],{"class":889},[62,91585,5024],{"class":72},[62,91587,11501],{"class":68},[62,91589,11504],{"class":72},[62,91591,91592,91594,91596,91598,91600,91602],{"class":64,"line":107},[62,91593,360],{"class":68},[62,91595,84623],{"class":72},[62,91597,11533],{"class":122},[62,91599,11536],{"class":72},[62,91601,800],{"class":68},[62,91603,15483],{"class":72},[62,91605,91606,91608,91610,91612,91615,91617,91620,91622,91624],{"class":64,"line":113},[62,91607,2610],{"class":72},[62,91609,15490],{"class":122},[62,91611,84637],{"class":72},[62,91613,91614],{"class":122},"toStaticResources",[62,91616,3229],{"class":72},[62,91618,91619],{"class":122},"atCommonLocations",[62,91621,11784],{"class":72},[62,91623,11549],{"class":122},[62,91625,2223],{"class":72},[62,91627,91628,91630,91632,91634,91636],{"class":64,"line":129},[62,91629,2610],{"class":72},[62,91631,11544],{"class":122},[62,91633,3229],{"class":72},[62,91635,15518],{"class":122},[62,91637,4460],{"class":72},[62,91639,91640,91642,91644],{"class":64,"line":134},[62,91641,2610],{"class":72},[62,91643,11518],{"class":122},[62,91645,822],{"class":72},[62,91647,91648,91650,91652],{"class":64,"line":156},[62,91649,2610],{"class":72},[62,91651,2189],{"class":122},[62,91653,822],{"class":72},[62,91655,91656],{"class":64,"line":161},[62,91657,223],{"class":72},[62,91659,91660],{"class":64,"line":167},[62,91661,379],{"class":72},[22,91663,91664,91665,2755],{},"In this example, we allow access to static resources, while requiring authentication for all other requests. If the syntax looks unfamiliar, this is because it is the Lambda DSL configuration. I have also written a tutorial on this, which can be found at ",[677,91666,8441],{"href":82696,"rel":91667},[681],[26,91669,91671],{"id":91670},"requestmatchers-replacing-antmatcher-mvcmatcher-and-regexmatcher","RequestMatchers Replacing AntMatcher, MvcMatcher, and RegexMatcher",[22,91673,91674,91675,976,91678,4201,91681,91684,91685,34867,91687,91690],{},"In Spring Security 6, ",[59,91676,91677],{},"AntMatcher",[59,91679,91680],{},"MvcMatcher",[59,91682,91683],{},"RegexMatcher"," have been deprecated and replaced by ",[59,91686,15490],{},[59,91688,91689],{},"securityMatchers"," for path-based access control. This allows you to match requests based on patterns or other criteria without relying on specific matchers.",[22,91692,91693,91694,91697],{},"Here's an example that permits access to the ",[59,91695,91696],{},"/greet"," endpoint without authentication while requiring authentication for all other requests:",[52,91699,91701],{"className":54,"code":91700,"language":56,"meta":57,"style":57},"@EnableWebSecurity\n@Configuration\npublic class SecurityConfig {\n\n @Bean\n public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n return http.authorizeHttpRequests(auth -> auth\n .requestMatchers(\"/greet\").permitAll()\n .anyRequest().authenticated())\n .formLogin()\n .build();\n }\n}\n",[59,91702,91703,91709,91715,91725,91729,91735,91753,91767,91784,91796,91804,91812,91816],{"__ignoreMap":57},[62,91704,91705,91707],{"class":64,"line":65},[62,91706,942],{"class":72},[62,91708,11461],{"class":68},[62,91710,91711,91713],{"class":64,"line":76},[62,91712,942],{"class":72},[62,91714,11133],{"class":68},[62,91716,91717,91719,91721,91723],{"class":64,"line":82},[62,91718,116],{"class":68},[62,91720,119],{"class":68},[62,91722,11470],{"class":122},[62,91724,126],{"class":72},[62,91726,91727],{"class":64,"line":89},[62,91728,79],{"emptyLinePlaceholder":13},[62,91730,91731,91733],{"class":64,"line":95},[62,91732,2143],{"class":72},[62,91734,2146],{"class":68},[62,91736,91737,91739,91741,91743,91745,91747,91749,91751],{"class":64,"line":101},[62,91738,194],{"class":68},[62,91740,15452],{"class":72},[62,91742,11490],{"class":122},[62,91744,11493],{"class":72},[62,91746,11496],{"class":889},[62,91748,5024],{"class":72},[62,91750,11501],{"class":68},[62,91752,11504],{"class":72},[62,91754,91755,91757,91759,91761,91763,91765],{"class":64,"line":107},[62,91756,360],{"class":68},[62,91758,84623],{"class":72},[62,91760,11533],{"class":122},[62,91762,11536],{"class":72},[62,91764,800],{"class":68},[62,91766,15483],{"class":72},[62,91768,91769,91771,91773,91775,91778,91780,91782],{"class":64,"line":113},[62,91770,2610],{"class":72},[62,91772,15490],{"class":122},[62,91774,2109],{"class":72},[62,91776,91777],{"class":1675},"\"/greet\"",[62,91779,15503],{"class":72},[62,91781,11549],{"class":122},[62,91783,2223],{"class":72},[62,91785,91786,91788,91790,91792,91794],{"class":64,"line":129},[62,91787,2610],{"class":72},[62,91789,11544],{"class":122},[62,91791,3229],{"class":72},[62,91793,15518],{"class":122},[62,91795,4460],{"class":72},[62,91797,91798,91800,91802],{"class":64,"line":134},[62,91799,2610],{"class":72},[62,91801,11518],{"class":122},[62,91803,2223],{"class":72},[62,91805,91806,91808,91810],{"class":64,"line":156},[62,91807,2610],{"class":72},[62,91809,2189],{"class":122},[62,91811,822],{"class":72},[62,91813,91814],{"class":64,"line":161},[62,91815,223],{"class":72},[62,91817,91818],{"class":64,"line":167},[62,91819,379],{"class":72},[22,91821,91822,91823,91825],{},"Now, if you run your application and try to access the ",[59,91824,91696],{}," endpoint, you should see that you can access it without needing to log in. However, if you try to access any other endpoint, you'll be prompted to log in.",[26,91827,32553],{"id":32552},[22,91829,91830,91831,91835],{},"I hope you found this overview of the important changes in Spring Security 6 helpful. Remember to go through the ",[677,91832,34833],{"href":91833,"rel":91834},"https://docs.spring.io/spring-security/site/docs/6.0.x/reference/html5/#new",[681]," for Spring Security 6, as there are many breaking changes and new features that you should be aware of. If you have questions about any of these changes or need help with your application, feel free to reach out.",[22,91837,91838,91839,82545],{},"As always, happy coding, friends!",[36006,91840],{},[1527,91842,10895],{},{"title":57,"searchDepth":76,"depth":76,"links":91844},[91845,91846,91847,91848,91849],{"id":2873,"depth":76,"text":2874},{"id":91433,"depth":76,"text":91434},{"id":91520,"depth":76,"text":91521},{"id":91670,"depth":76,"text":91671},{"id":32552,"depth":76,"text":32553},{"_id":91851,"path":91852,"title":91853,"description":91854,"meta":91855,"body":91860},"content/blog/2022/11/10/aws-lambda-java-core.md","/blog/2022/11/10/aws-lambda-java-core","Building AWS Lambda Functions with Java: An Introduction to the AWS Lambda Core Java Library","In this tutorial you will learn how to build AWS Lambda functions with Java using the AWS Lambda Core Java Library.",{"slug":91856,"date":91857,"published":13,"tags":91858,"author":17,"cover":91859,"excerpt":-1},"aws-lambda-java-core","2022-11-10T08:00:00.000Z",[47077,56],"./aws-lambda-java-core.png",{"type":19,"value":91861,"toc":93040},[91862,91870,91877,91880,91882,91899,91904,92058,92068,92072,92085,92169,92179,92185,92205,92212,92216,92225,92302,92305,92309,92319,92324,92396,92401,92619,92627,92771,92774,92837,92840,92844,92847,93018,93023,93026,93028,93031,93034,93037],[22,91863,91864,91865,2755],{},"In this tutorial, we are continuing our journey to build AWS Lambda functions in Java. Previously, we looked at the quintessential \"Hello World\" example - how to create our first serverless function using Java. If you missed it, you can find the ",[677,91866,91869],{"href":91867,"rel":91868},"https://www.danvega.dev/blog/2022/11/09/hello-aws-lambda-java/",[681],"previous tutorial here",[22,91871,91872,91873,91876],{},"Today, we will make progress by introducing a new library from AWS - the ",[646,91874,91875],{},"AWS Lambda Core Java Library",". This library is one of the few AWS offers to assist in building serverless functions.",[22,91878,91879],{},"Let's build a new project, pull in the dependency, discuss what this new dependency provides, and build and test a new serverless function.",[26,91881,15196],{"id":15195},[22,91883,91884,91885,91888,91889,91891,91892,19931,91895,91898],{},"First, let's create a new Maven project in IntelliJ using the ",[59,91886,91887],{},"maven-archetype-quickstart"," archetype. Once the project is created, update the ",[59,91890,1765],{}," file to include the ",[4534,91893,91894],{},"AWS Lambda Core",[4534,91896,91897],{},"JUnit Jupiter"," dependencies:",[22,91900,91901],{},[653,91902],{"alt":78726,"src":91903},"/images/blog/2022/11/10/start-spring-io.png",[52,91905,91907],{"className":1769,"code":91906,"language":1771,"meta":57,"style":57},"\u003Cdependencies>\n \u003C!-- AWS Lambda Java Core -->\n \u003Cdependency>\n \u003CgroupId>com.amazonaws\u003C/groupId>\n \u003CartifactId>aws-lambda-java-core\u003C/artifactId>\n \u003Cversion>1.2.1\u003C/version>\n \u003C/dependency>\n \u003C!-- JUnit Jupiter -->\n \u003Cdependency>\n \u003CgroupId>org.junit.jupiter\u003C/groupId>\n \u003CartifactId>junit-jupiter\u003C/artifactId>\n \u003Cversion>5.8.2\u003C/version>\n \u003Cscope>test\u003C/scope>\n \u003C/dependency>\n\u003C/dependencies>\n",[59,91908,91909,91917,91922,91930,91943,91956,91969,91977,91982,91990,92003,92016,92029,92042,92050],{"__ignoreMap":57},[62,91910,91911,91913,91915],{"class":64,"line":65},[62,91912,760],{"class":72},[62,91914,1889],{"class":1780},[62,91916,1784],{"class":72},[62,91918,91919],{"class":64,"line":76},[62,91920,91921],{"class":85}," \u003C!-- AWS Lambda Java Core -->\n",[62,91923,91924,91926,91928],{"class":64,"line":82},[62,91925,33056],{"class":72},[62,91927,1781],{"class":1780},[62,91929,1784],{"class":72},[62,91931,91932,91934,91936,91939,91941],{"class":64,"line":89},[62,91933,1789],{"class":72},[62,91935,1792],{"class":1780},[62,91937,91938],{"class":72},">com.amazonaws\u003C/",[62,91940,1792],{"class":1780},[62,91942,1784],{"class":72},[62,91944,91945,91947,91949,91952,91954],{"class":64,"line":95},[62,91946,1789],{"class":72},[62,91948,1806],{"class":1780},[62,91950,91951],{"class":72},">aws-lambda-java-core\u003C/",[62,91953,1806],{"class":1780},[62,91955,1784],{"class":72},[62,91957,91958,91960,91962,91965,91967],{"class":64,"line":101},[62,91959,1789],{"class":72},[62,91961,1933],{"class":1780},[62,91963,91964],{"class":72},">1.2.1\u003C/",[62,91966,1933],{"class":1780},[62,91968,1784],{"class":72},[62,91970,91971,91973,91975],{"class":64,"line":107},[62,91972,33187],{"class":72},[62,91974,1781],{"class":1780},[62,91976,1784],{"class":72},[62,91978,91979],{"class":64,"line":113},[62,91980,91981],{"class":85}," \u003C!-- JUnit Jupiter -->\n",[62,91983,91984,91986,91988],{"class":64,"line":129},[62,91985,33056],{"class":72},[62,91987,1781],{"class":1780},[62,91989,1784],{"class":72},[62,91991,91992,91994,91996,91999,92001],{"class":64,"line":134},[62,91993,1789],{"class":72},[62,91995,1792],{"class":1780},[62,91997,91998],{"class":72},">org.junit.jupiter\u003C/",[62,92000,1792],{"class":1780},[62,92002,1784],{"class":72},[62,92004,92005,92007,92009,92012,92014],{"class":64,"line":156},[62,92006,1789],{"class":72},[62,92008,1806],{"class":1780},[62,92010,92011],{"class":72},">junit-jupiter\u003C/",[62,92013,1806],{"class":1780},[62,92015,1784],{"class":72},[62,92017,92018,92020,92022,92025,92027],{"class":64,"line":161},[62,92019,1789],{"class":72},[62,92021,1933],{"class":1780},[62,92023,92024],{"class":72},">5.8.2\u003C/",[62,92026,1933],{"class":1780},[62,92028,1784],{"class":72},[62,92030,92031,92033,92035,92038,92040],{"class":64,"line":167},[62,92032,1789],{"class":72},[62,92034,1961],{"class":1780},[62,92036,92037],{"class":72},">test\u003C/",[62,92039,1961],{"class":1780},[62,92041,1784],{"class":72},[62,92043,92044,92046,92048],{"class":64,"line":173},[62,92045,33187],{"class":72},[62,92047,1781],{"class":1780},[62,92049,1784],{"class":72},[62,92051,92052,92054,92056],{"class":64,"line":179},[62,92053,1818],{"class":72},[62,92055,1889],{"class":1780},[62,92057,1784],{"class":72},[22,92059,3521,92060,92062,92063,2755],{},[59,92061,91856],{}," library is not required for building serverless functions with Java. This library does add some nice functionality including defining a handler method interface and the context object that the runtime passes to the handler. The library version may change, so make sure to check for the ",[677,92064,92067],{"href":92065,"rel":92066},"https://mvnrepository.com/artifact/com.amazonaws/aws-lambda-java-core",[681],"latest version here",[26,92069,92071],{"id":92070},"creating-the-lambda-function-with-aws-lambda-core-java-library","Creating the Lambda Function with AWS Lambda Core Java Library",[22,92073,47116,92074,92077,92078,92081,92082,48925],{},[59,92075,92076],{},"src/main/java"," folder, create a new class named ",[59,92079,92080],{},"SimpleHandler",". Use the AWS Lambda Core Java Library to implement the ",[59,92083,92084],{},"RequestHandler",[52,92086,92088],{"className":54,"code":92087,"language":56,"meta":57,"style":57},"import com.amazonaws.services.lambda.runtime.Context;\nimport com.amazonaws.services.lambda.runtime.RequestHandler;\n\npublic class SimpleHandler implements RequestHandler\u003CString, String> {\n\n @Override\n public String handleRequest(String input, Context context) {\n // Our code will be added here\n }\n}\n",[59,92089,92090,92096,92102,92106,92128,92132,92138,92156,92161,92165],{"__ignoreMap":57},[62,92091,92092,92094],{"class":64,"line":65},[62,92093,27875],{"class":68},[62,92095,78198],{"class":72},[62,92097,92098,92100],{"class":64,"line":76},[62,92099,27875],{"class":68},[62,92101,78212],{"class":72},[62,92103,92104],{"class":64,"line":82},[62,92105,79],{"emptyLinePlaceholder":13},[62,92107,92108,92110,92112,92114,92116,92118,92120,92122,92124,92126],{"class":64,"line":89},[62,92109,116],{"class":68},[62,92111,119],{"class":68},[62,92113,78225],{"class":122},[62,92115,13520],{"class":68},[62,92117,78230],{"class":122},[62,92119,760],{"class":72},[62,92121,973],{"class":68},[62,92123,976],{"class":72},[62,92125,973],{"class":68},[62,92127,8552],{"class":72},[62,92129,92130],{"class":64,"line":95},[62,92131,79],{"emptyLinePlaceholder":13},[62,92133,92134,92136],{"class":64,"line":101},[62,92135,2143],{"class":72},[62,92137,13555],{"class":68},[62,92139,92140,92142,92144,92146,92148,92150,92152,92154],{"class":64,"line":107},[62,92141,194],{"class":68},[62,92143,2469],{"class":72},[62,92145,48132],{"class":122},[62,92147,1049],{"class":72},[62,92149,8890],{"class":889},[62,92151,78259],{"class":72},[62,92153,78262],{"class":889},[62,92155,768],{"class":72},[62,92157,92158],{"class":64,"line":113},[62,92159,92160],{"class":85}," // Our code will be added here\n",[62,92162,92163],{"class":64,"line":129},[62,92164,223],{"class":72},[62,92166,92167],{"class":64,"line":134},[62,92168,379],{"class":72},[22,92170,47116,92171,92173,92174,19931,92176,92178],{},[59,92172,48132],{}," method, we can now access the ",[59,92175,8890],{},[59,92177,78262],{}," parameters provided by the AWS Lambda Core Java Library.",[22,92180,92181,92184],{},[59,92182,92183],{},"Context"," gives us access to useful information about the request, such as:",[915,92186,92187,92190,92193,92196,92199,92202],{},[37,92188,92189],{},"Request ID",[37,92191,92192],{},"Log group name",[37,92194,92195],{},"Function name",[37,92197,92198],{},"Version",[37,92200,92201],{},"Identity",[37,92203,92204],{},"Other details",[22,92206,92207,92208,92211],{},"Additionally, we can access a ",[59,92209,92210],{},"LambdaLogger"," instance, which is useful for logging within our Lambda function.",[26,92213,92215],{"id":92214},"implementing-the-lambda-function","Implementing the Lambda Function",[22,92217,92218,92219,92222,92223,6175],{},"In this example, our Lambda function will take a string input, convert it to uppercase, and return the result. To do this, we can use the ",[59,92220,92221],{},"toUpperCase()"," method provided by Java's ",[59,92224,973],{},[52,92226,92228],{"className":54,"code":92227,"language":56,"meta":57,"style":57},"@Override\npublic String handleRequest(String input, Context context) {\n LambdaLogger logger = context.getLogger();\n logger.log(\"Function \" + context.getFunctionName() + \" called\");\n return input.toUpperCase();\n}\n",[59,92229,92230,92236,92247,92260,92288,92298],{"__ignoreMap":57},[62,92231,92232,92234],{"class":64,"line":65},[62,92233,942],{"class":72},[62,92235,13555],{"class":68},[62,92237,92238,92240,92242,92244],{"class":64,"line":76},[62,92239,116],{"class":68},[62,92241,2469],{"class":72},[62,92243,48132],{"class":122},[62,92245,92246],{"class":72},"(String input, Context context) {\n",[62,92248,92249,92252,92254,92256,92258],{"class":64,"line":82},[62,92250,92251],{"class":72}," LambdaLogger logger ",[62,92253,146],{"class":68},[62,92255,78274],{"class":72},[62,92257,3069],{"class":122},[62,92259,822],{"class":72},[62,92261,92262,92265,92267,92269,92272,92274,92276,92279,92281,92283,92286],{"class":64,"line":89},[62,92263,92264],{"class":72}," logger.",[62,92266,58271],{"class":122},[62,92268,2109],{"class":72},[62,92270,92271],{"class":1675},"\"Function \"",[62,92273,4507],{"class":68},[62,92275,78274],{"class":72},[62,92277,92278],{"class":122},"getFunctionName",[62,92280,5398],{"class":72},[62,92282,1148],{"class":68},[62,92284,92285],{"class":1675}," \" called\"",[62,92287,1133],{"class":72},[62,92289,92290,92292,92294,92296],{"class":64,"line":95},[62,92291,2599],{"class":68},[62,92293,63117],{"class":72},[62,92295,78311],{"class":122},[62,92297,822],{"class":72},[62,92299,92300],{"class":64,"line":101},[62,92301,379],{"class":72},[22,92303,92304],{},"Now that we have our Lambda function implemented, we need to write tests for it.",[26,92306,92308],{"id":92307},"writing-tests-for-the-lambda-function","Writing Tests for the Lambda Function",[22,92310,92311,92312,92314,92315,19931,92317,55947],{},"Since our Lambda function depends on the ",[59,92313,92183],{}," parameter provided by the AWS Core Java Library, we need to find a way to provide this context during testing. One approach is to use mocking libraries, such as Mockito, to create mock instances of the ",[59,92316,92183],{},[59,92318,92210],{},[22,92320,92321,92322,1266],{},"Let's add Mockito to our project by updating our ",[59,92323,1765],{},[52,92325,92327],{"className":1769,"code":92326,"language":1771,"meta":57,"style":57},"\u003Cdependency>\n \u003CgroupId>org.mockito\u003C/groupId>\n \u003CartifactId>mockito-junit-jupiter\u003C/artifactId>\n \u003Cversion>4.5.1\u003C/version>\n \u003Cscope>test\u003C/scope>\n\u003C/dependency>\n",[59,92328,92329,92337,92350,92363,92376,92388],{"__ignoreMap":57},[62,92330,92331,92333,92335],{"class":64,"line":65},[62,92332,760],{"class":72},[62,92334,1781],{"class":1780},[62,92336,1784],{"class":72},[62,92338,92339,92341,92343,92346,92348],{"class":64,"line":76},[62,92340,1789],{"class":72},[62,92342,1792],{"class":1780},[62,92344,92345],{"class":72},">org.mockito\u003C/",[62,92347,1792],{"class":1780},[62,92349,1784],{"class":72},[62,92351,92352,92354,92356,92359,92361],{"class":64,"line":82},[62,92353,1789],{"class":72},[62,92355,1806],{"class":1780},[62,92357,92358],{"class":72},">mockito-junit-jupiter\u003C/",[62,92360,1806],{"class":1780},[62,92362,1784],{"class":72},[62,92364,92365,92367,92369,92372,92374],{"class":64,"line":89},[62,92366,1789],{"class":72},[62,92368,1933],{"class":1780},[62,92370,92371],{"class":72},">4.5.1\u003C/",[62,92373,1933],{"class":1780},[62,92375,1784],{"class":72},[62,92377,92378,92380,92382,92384,92386],{"class":64,"line":95},[62,92379,1789],{"class":72},[62,92381,1961],{"class":1780},[62,92383,92037],{"class":72},[62,92385,1961],{"class":1780},[62,92387,1784],{"class":72},[62,92389,92390,92392,92394],{"class":64,"line":101},[62,92391,1818],{"class":72},[62,92393,1781],{"class":1780},[62,92395,1784],{"class":72},[22,92397,92398,92399,1266],{},"Reload the Maven changes and create a new test class for our ",[59,92400,92080],{},[52,92402,92404],{"className":54,"code":92403,"language":56,"meta":57,"style":57},"import com.amazonaws.services.lambda.runtime.Context;\nimport com.amazonaws.services.lambda.runtime.LambdaLogger;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.Mockito.doAnswer;\nimport static org.mockito.Mockito.when;\n\n@ExtendWith(MockitoExtension.class)\nclass SimpleHandlerTest {\n\n private SimpleHandler simpleHandler;\n\n @Mock\n private Context context;\n\n @Mock\n private LambdaLogger logger;\n\n @BeforeEach\n void setUp() {\n // Set up your mocks\n }\n\n @Test\n void shouldReturnUppercaseOfInput() {\n // Write your test\n }\n}\n\n",[59,92405,92406,92412,92418,92424,92430,92437,92444,92451,92455,92464,92473,92482,92491,92495,92505,92514,92518,92525,92529,92536,92543,92547,92553,92560,92564,92570,92578,92583,92587,92591,92597,92606,92611,92615],{"__ignoreMap":57},[62,92407,92408,92410],{"class":64,"line":65},[62,92409,27875],{"class":68},[62,92411,78198],{"class":72},[62,92413,92414,92416],{"class":64,"line":76},[62,92415,27875],{"class":68},[62,92417,78205],{"class":72},[62,92419,92420,92422],{"class":64,"line":82},[62,92421,27875],{"class":68},[62,92423,29080],{"class":72},[62,92425,92426,92428],{"class":64,"line":89},[62,92427,27875],{"class":68},[62,92429,29087],{"class":72},[62,92431,92432,92434],{"class":64,"line":95},[62,92433,27875],{"class":68},[62,92435,92436],{"class":72}," org.junit.jupiter.api.extension.ExtendWith;\n",[62,92438,92439,92441],{"class":64,"line":101},[62,92440,27875],{"class":68},[62,92442,92443],{"class":72}," org.mockito.Mock;\n",[62,92445,92446,92448],{"class":64,"line":107},[62,92447,27875],{"class":68},[62,92449,92450],{"class":72}," org.mockito.junit.jupiter.MockitoExtension;\n",[62,92452,92453],{"class":64,"line":113},[62,92454,79],{"emptyLinePlaceholder":13},[62,92456,92457,92459,92461],{"class":64,"line":129},[62,92458,27875],{"class":68},[62,92460,2101],{"class":68},[62,92462,92463],{"class":72}," org.junit.jupiter.api.Assertions.assertEquals;\n",[62,92465,92466,92468,92470],{"class":64,"line":134},[62,92467,27875],{"class":68},[62,92469,2101],{"class":68},[62,92471,92472],{"class":72}," org.mockito.ArgumentMatchers.anyString;\n",[62,92474,92475,92477,92479],{"class":64,"line":156},[62,92476,27875],{"class":68},[62,92478,2101],{"class":68},[62,92480,92481],{"class":72}," org.mockito.Mockito.doAnswer;\n",[62,92483,92484,92486,92488],{"class":64,"line":161},[62,92485,27875],{"class":68},[62,92487,2101],{"class":68},[62,92489,92490],{"class":72}," org.mockito.Mockito.when;\n",[62,92492,92493],{"class":64,"line":167},[62,92494,79],{"emptyLinePlaceholder":13},[62,92496,92497,92499,92502],{"class":64,"line":173},[62,92498,942],{"class":72},[62,92500,92501],{"class":68},"ExtendWith",[62,92503,92504],{"class":72},"(MockitoExtension.class)\n",[62,92506,92507,92509,92512],{"class":64,"line":179},[62,92508,11671],{"class":68},[62,92510,92511],{"class":122}," SimpleHandlerTest",[62,92513,126],{"class":72},[62,92515,92516],{"class":64,"line":185},[62,92517,79],{"emptyLinePlaceholder":13},[62,92519,92520,92522],{"class":64,"line":191},[62,92521,137],{"class":68},[62,92523,92524],{"class":72}," SimpleHandler simpleHandler;\n",[62,92526,92527],{"class":64,"line":209},[62,92528,79],{"emptyLinePlaceholder":13},[62,92530,92531,92533],{"class":64,"line":220},[62,92532,2143],{"class":72},[62,92534,92535],{"class":68},"Mock\n",[62,92537,92538,92540],{"class":64,"line":226},[62,92539,137],{"class":68},[62,92541,92542],{"class":72}," Context context;\n",[62,92544,92545],{"class":64,"line":231},[62,92546,79],{"emptyLinePlaceholder":13},[62,92548,92549,92551],{"class":64,"line":236},[62,92550,2143],{"class":72},[62,92552,92535],{"class":68},[62,92554,92555,92557],{"class":64,"line":242},[62,92556,137],{"class":68},[62,92558,92559],{"class":72}," LambdaLogger logger;\n",[62,92561,92562],{"class":64,"line":247},[62,92563,79],{"emptyLinePlaceholder":13},[62,92565,92566,92568],{"class":64,"line":252},[62,92567,2143],{"class":72},[62,92569,29133],{"class":68},[62,92571,92572,92574,92576],{"class":64,"line":257},[62,92573,11710],{"class":68},[62,92575,29140],{"class":122},[62,92577,206],{"class":72},[62,92579,92580],{"class":64,"line":271},[62,92581,92582],{"class":85}," // Set up your mocks\n",[62,92584,92585],{"class":64,"line":281},[62,92586,223],{"class":72},[62,92588,92589],{"class":64,"line":286},[62,92590,79],{"emptyLinePlaceholder":13},[62,92592,92593,92595],{"class":64,"line":291},[62,92594,2143],{"class":72},[62,92596,11705],{"class":68},[62,92598,92599,92601,92604],{"class":64,"line":296},[62,92600,11710],{"class":68},[62,92602,92603],{"class":122}," shouldReturnUppercaseOfInput",[62,92605,206],{"class":72},[62,92607,92608],{"class":64,"line":302},[62,92609,92610],{"class":85}," // Write your test\n",[62,92612,92613],{"class":64,"line":308},[62,92614,223],{"class":72},[62,92616,92617],{"class":64,"line":314},[62,92618,379],{"class":72},[22,92620,47116,92621,92624,92625,1266],{},[59,92622,92623],{},"setUp()"," method, we need to configure our mocks and create a new instance of ",[59,92626,92080],{},[52,92628,92630],{"className":54,"code":92629,"language":56,"meta":57,"style":57},"@BeforeEach\nvoid setUp() {\n when(context.getLogger()).thenReturn(logger);\n\n doAnswer(invocation -> {\n System.out.println((String) invocation.getArguments()[0]);\n return null;\n }).when(logger).log(anyString());\n\n when(context.getFunctionName()).thenReturn(\"handleRequest\");\n\n simpleHandler = new SimpleHandler();\n}\n",[59,92631,92632,92638,92646,92664,92668,92680,92699,92707,92727,92731,92750,92754,92767],{"__ignoreMap":57},[62,92633,92634,92636],{"class":64,"line":65},[62,92635,942],{"class":72},[62,92637,29133],{"class":68},[62,92639,92640,92642,92644],{"class":64,"line":76},[62,92641,26320],{"class":68},[62,92643,29140],{"class":122},[62,92645,206],{"class":72},[62,92647,92648,92651,92654,92656,92658,92661],{"class":64,"line":82},[62,92649,92650],{"class":122}," when",[62,92652,92653],{"class":72},"(context.",[62,92655,3069],{"class":122},[62,92657,11784],{"class":72},[62,92659,92660],{"class":122},"thenReturn",[62,92662,92663],{"class":72},"(logger);\n",[62,92665,92666],{"class":64,"line":89},[62,92667,79],{"emptyLinePlaceholder":13},[62,92669,92670,92673,92676,92678],{"class":64,"line":95},[62,92671,92672],{"class":122}," doAnswer",[62,92674,92675],{"class":72},"(invocation ",[62,92677,800],{"class":68},[62,92679,126],{"class":72},[62,92681,92682,92684,92686,92689,92692,92695,92697],{"class":64,"line":101},[62,92683,27297],{"class":72},[62,92685,2244],{"class":122},[62,92687,92688],{"class":72},"((String) invocation.",[62,92690,92691],{"class":122},"getArguments",[62,92693,92694],{"class":72},"()[",[62,92696,1130],{"class":149},[62,92698,87836],{"class":72},[62,92700,92701,92703,92705],{"class":64,"line":107},[62,92702,360],{"class":68},[62,92704,13324],{"class":149},[62,92706,153],{"class":72},[62,92708,92709,92712,92715,92718,92720,92722,92725],{"class":64,"line":113},[62,92710,92711],{"class":72}," }).",[62,92713,92714],{"class":122},"when",[62,92716,92717],{"class":72},"(logger).",[62,92719,58271],{"class":122},[62,92721,2109],{"class":72},[62,92723,92724],{"class":122},"anyString",[62,92726,1091],{"class":72},[62,92728,92729],{"class":64,"line":129},[62,92730,79],{"emptyLinePlaceholder":13},[62,92732,92733,92735,92737,92739,92741,92743,92745,92748],{"class":64,"line":134},[62,92734,92650],{"class":122},[62,92736,92653],{"class":72},[62,92738,92278],{"class":122},[62,92740,11784],{"class":72},[62,92742,92660],{"class":122},[62,92744,2109],{"class":72},[62,92746,92747],{"class":1675},"\"handleRequest\"",[62,92749,1133],{"class":72},[62,92751,92752],{"class":64,"line":156},[62,92753,79],{"emptyLinePlaceholder":13},[62,92755,92756,92759,92761,92763,92765],{"class":64,"line":161},[62,92757,92758],{"class":72}," simpleHandler ",[62,92760,146],{"class":68},[62,92762,466],{"class":68},[62,92764,78225],{"class":122},[62,92766,822],{"class":72},[62,92768,92769],{"class":64,"line":167},[62,92770,379],{"class":72},[22,92772,92773],{},"Now we can write our test method:",[52,92775,92777],{"className":54,"code":92776,"language":56,"meta":57,"style":57},"@Test\nvoid shouldReturnUppercaseOfInput() {\n when(context.getFunctionName()).thenReturn(\"handleRequest\");\n assertEquals(\"HELLO, WORLD!\",handler.handleRequest(\"Hello, World!\",context));\n}\n",[59,92778,92779,92785,92793,92811,92833],{"__ignoreMap":57},[62,92780,92781,92783],{"class":64,"line":65},[62,92782,942],{"class":72},[62,92784,11705],{"class":68},[62,92786,92787,92789,92791],{"class":64,"line":76},[62,92788,26320],{"class":68},[62,92790,92603],{"class":122},[62,92792,206],{"class":72},[62,92794,92795,92797,92799,92801,92803,92805,92807,92809],{"class":64,"line":82},[62,92796,92650],{"class":122},[62,92798,92653],{"class":72},[62,92800,92278],{"class":122},[62,92802,11784],{"class":72},[62,92804,92660],{"class":122},[62,92806,2109],{"class":72},[62,92808,92747],{"class":1675},[62,92810,1133],{"class":72},[62,92812,92813,92816,92818,92821,92824,92826,92828,92830],{"class":64,"line":89},[62,92814,92815],{"class":122}," assertEquals",[62,92817,2109],{"class":72},[62,92819,92820],{"class":1675},"\"HELLO, WORLD!\"",[62,92822,92823],{"class":72},",handler.",[62,92825,48132],{"class":122},[62,92827,2109],{"class":72},[62,92829,27469],{"class":1675},[62,92831,92832],{"class":72},",context));\n",[62,92834,92835],{"class":64,"line":95},[62,92836,379],{"class":72},[22,92838,92839],{},"With the test in place, we can run it to ensure our Lambda function works as expected.",[26,92841,92843],{"id":92842},"building-and-deploying-the-lambda-function","Building and Deploying the Lambda Function",[22,92845,92846],{},"Finally, we can use the Maven Shade Plugin to package our Lambda function into an Uber JAR:",[52,92848,92850],{"className":1769,"code":92849,"language":1771,"meta":57,"style":57},"\u003Cbuild>\n \u003Cplugins>\n \u003Cplugin>\n \u003CgroupId>org.apache.maven.plugins\u003C/groupId>\n \u003CartifactId>maven-shade-plugin\u003C/artifactId>\n \u003Cversion>3.2.4\u003C/version>\n \u003Cexecutions>\n \u003Cexecution>\n \u003Cphase>package\u003C/phase>\n \u003Cgoals>\n \u003Cgoal>shade\u003C/goal>\n \u003C/goals>\n \u003C/execution>\n \u003C/executions>\n \u003C/plugin>\n \u003C/plugins>\n\u003C/build>\n",[59,92851,92852,92860,92869,92877,92891,92904,92917,92925,92933,92947,92955,92968,92977,92985,92994,93002,93010],{"__ignoreMap":57},[62,92853,92854,92856,92858],{"class":64,"line":65},[62,92855,760],{"class":72},[62,92857,2189],{"class":1780},[62,92859,1784],{"class":72},[62,92861,92862,92864,92867],{"class":64,"line":76},[62,92863,33056],{"class":72},[62,92865,92866],{"class":1780},"plugins",[62,92868,1784],{"class":72},[62,92870,92871,92873,92875],{"class":64,"line":82},[62,92872,1789],{"class":72},[62,92874,3928],{"class":1780},[62,92876,1784],{"class":72},[62,92878,92879,92882,92884,92887,92889],{"class":64,"line":89},[62,92880,92881],{"class":72}," \u003C",[62,92883,1792],{"class":1780},[62,92885,92886],{"class":72},">org.apache.maven.plugins\u003C/",[62,92888,1792],{"class":1780},[62,92890,1784],{"class":72},[62,92892,92893,92895,92897,92900,92902],{"class":64,"line":95},[62,92894,92881],{"class":72},[62,92896,1806],{"class":1780},[62,92898,92899],{"class":72},">maven-shade-plugin\u003C/",[62,92901,1806],{"class":1780},[62,92903,1784],{"class":72},[62,92905,92906,92908,92910,92913,92915],{"class":64,"line":101},[62,92907,92881],{"class":72},[62,92909,1933],{"class":1780},[62,92911,92912],{"class":72},">3.2.4\u003C/",[62,92914,1933],{"class":1780},[62,92916,1784],{"class":72},[62,92918,92919,92921,92923],{"class":64,"line":107},[62,92920,92881],{"class":72},[62,92922,33441],{"class":1780},[62,92924,1784],{"class":72},[62,92926,92927,92929,92931],{"class":64,"line":113},[62,92928,1896],{"class":72},[62,92930,13587],{"class":1780},[62,92932,1784],{"class":72},[62,92934,92935,92938,92940,92943,92945],{"class":64,"line":129},[62,92936,92937],{"class":72}," \u003C",[62,92939,33506],{"class":1780},[62,92941,92942],{"class":72},">package\u003C/",[62,92944,33506],{"class":1780},[62,92946,1784],{"class":72},[62,92948,92949,92951,92953],{"class":64,"line":134},[62,92950,92937],{"class":72},[62,92952,33475],{"class":1780},[62,92954,1784],{"class":72},[62,92956,92957,92959,92961,92964,92966],{"class":64,"line":156},[62,92958,1905],{"class":72},[62,92960,33484],{"class":1780},[62,92962,92963],{"class":72},">shade\u003C/",[62,92965,33484],{"class":1780},[62,92967,1784],{"class":72},[62,92969,92970,92973,92975],{"class":64,"line":161},[62,92971,92972],{"class":72}," \u003C/",[62,92974,33475],{"class":1780},[62,92976,1784],{"class":72},[62,92978,92979,92981,92983],{"class":64,"line":167},[62,92980,1973],{"class":72},[62,92982,13587],{"class":1780},[62,92984,1784],{"class":72},[62,92986,92987,92990,92992],{"class":64,"line":173},[62,92988,92989],{"class":72}," \u003C/",[62,92991,33441],{"class":1780},[62,92993,1784],{"class":72},[62,92995,92996,92998,93000],{"class":64,"line":179},[62,92997,1982],{"class":72},[62,92999,3928],{"class":1780},[62,93001,1784],{"class":72},[62,93003,93004,93006,93008],{"class":64,"line":185},[62,93005,33187],{"class":72},[62,93007,92866],{"class":1780},[62,93009,1784],{"class":72},[62,93011,93012,93014,93016],{"class":64,"line":191},[62,93013,1818],{"class":72},[62,93015,2189],{"class":1780},[62,93017,1784],{"class":72},[22,93019,91325,93020,93022],{},[59,93021,78615],{}," to build the JAR file, and then upload it to AWS Lambda through the AWS Management Console.",[22,93024,93025],{},"When testing the Lambda function within the console, you should now see the input string being converted to uppercase, and the Lambda function name being logged.",[26,93027,1499],{"id":1498},[22,93029,93030],{},"And that's it! We've successfully built and deployed our serverless Lambda function using the AWS Lambda Core Java Library. With this foundation in place, we can now explore more complex use cases and even incorporate other AWS services and technologies, such as Amazon API Gateway and the AWS Lambda Java Events library.",[22,93032,93033],{},"Stay tuned for more tutorials on building serverless functions with Java, as well as an upcoming video on creating serverless functions using Spring Boot and Spring Cloud Functions. As always...",[22,93035,93036],{},"Happy Coding 😎🧑💻👩💻",[1527,93038,93039],{},"html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .suaqH, html code.shiki .suaqH{--shiki-default:#22863A;--shiki-dark:#85E89D;--shiki-sepia:#22863A}html pre.shiki code .s4-Ip, html code.shiki .s4-Ip{--shiki-default:#6A737D;--shiki-dark:#6A737D;--shiki-sepia:#6A737D}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .spoOn, html code.shiki .spoOn{--shiki-default:#E36209;--shiki-dark:#FFAB70;--shiki-sepia:#E36209}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}",{"title":57,"searchDepth":76,"depth":76,"links":93041},[93042,93043,93044,93045,93046,93047],{"id":15195,"depth":76,"text":15196},{"id":92070,"depth":76,"text":92071},{"id":92214,"depth":76,"text":92215},{"id":92307,"depth":76,"text":92308},{"id":92842,"depth":76,"text":92843},{"id":1498,"depth":76,"text":1499},{"_id":93049,"path":93050,"title":93051,"description":93052,"meta":93053,"body":93058},"content/blog/2022/11/09/hello-aws-lambda-java.md","/blog/2022/11/09/hello-aws-lambda-java","AWS Lambda Java: How to create your first AWS Lambda Function in Java","In this tutorial, I'll show you how to create a simple AWS Lambda function using Java.",{"slug":93054,"date":93055,"published":13,"tags":93056,"author":17,"cover":93057,"excerpt":-1},"hello-aws-lambda-java","2022-11-09T08:00:00.000Z",[47077,56],"./hello-aws-lambda.png",{"type":19,"value":93059,"toc":93554},[93060,93063,93067,93070,93077,93083,93095,93146,93149,93153,93163,93265,93268,93272,93275,93283,93488,93491,93503,93508,93512,93523,93529,93532,93535,93538,93542,93545,93548,93551],[22,93061,93062],{},"In this tutorial, I'll show you how to create a simple AWS Lambda function using Java. We'll start with a plain Java project, upload it to the AWS console, and test it. Along the way, I'll share some of the pain points I encountered while building serverless functions, and in a later tutorial you will see how you can overcome them using tools like Spring Cloud Function.",[26,93064,93066],{"id":93065},"getting-started","Getting Started",[22,93068,93069],{},"First, create a new Java project using your favorite IDE or text editor. I'll be using IntelliJ IDEA Ultimate Edition, but you can also use the Community Edition or another tool of your choice.",[22,93071,93072,93073,93076],{},"Create a new Maven project and choose the ",[59,93074,93075],{},"quickstart"," archetype, which gives us a basic shell of an application. We'll be using Java 11 and JUnit 5.8.2 for our project.",[22,93078,93079],{},[653,93080],{"alt":93081,"src":93082},"Maven QuickStart Archetype","/images/blog/2022/11/09/maven-quickstart.png",[22,93084,93085,93086,93089,93090,93092,93093,2755],{},"To start, let's create a simple Java class that will hold our Lambda function. Name the class ",[59,93087,93088],{},"HelloLambda",", and create a method called ",[59,93091,48132],{}," that returns a ",[59,93094,973],{},[52,93096,93098],{"className":54,"code":93097,"language":56,"meta":57,"style":57},"public class HelloLambda {\n\n public String handleRequest() {\n return \"Hello, AWS Lambda!\";\n }\n\n}\n",[59,93099,93100,93111,93115,93125,93134,93138,93142],{"__ignoreMap":57},[62,93101,93102,93104,93106,93109],{"class":64,"line":65},[62,93103,116],{"class":68},[62,93105,119],{"class":68},[62,93107,93108],{"class":122}," HelloLambda",[62,93110,126],{"class":72},[62,93112,93113],{"class":64,"line":76},[62,93114,79],{"emptyLinePlaceholder":13},[62,93116,93117,93119,93121,93123],{"class":64,"line":82},[62,93118,194],{"class":68},[62,93120,2469],{"class":72},[62,93122,48132],{"class":122},[62,93124,206],{"class":72},[62,93126,93127,93129,93132],{"class":64,"line":89},[62,93128,360],{"class":68},[62,93130,93131],{"class":1675}," \"Hello, AWS Lambda!\"",[62,93133,153],{"class":72},[62,93135,93136],{"class":64,"line":95},[62,93137,223],{"class":72},[62,93139,93140],{"class":64,"line":101},[62,93141,79],{"emptyLinePlaceholder":13},[62,93143,93144],{"class":64,"line":107},[62,93145,379],{"class":72},[22,93147,93148],{},"This is a very simple function that just takes a name as input and returns a greeting message. Now, let's create a test for our function using JUnit.",[26,93150,93152],{"id":93151},"writing-tests","Writing Tests",[22,93154,93155,93156,93159,93160,2755],{},"It's always a good practice to write tests for your functions. Create a new test class called ",[59,93157,93158],{},"HelloLambdaTest"," and add a method named ",[59,93161,93162],{},"shouldReturnHelloMessage",[52,93164,93166],{"className":54,"code":93165,"language":56,"meta":57,"style":57},"import org.junit.jupiter.api.Test;\n\nimport static org.junit.jupiter.api.Assertions.*;\n\nclass HelloLambdaTest {\n\n @Test\n void shouldReturnHelloMessage() {\n var sut = new HelloLambda();\n assertEquals(\"Hello, AWS Lambda!\",sut.handleRequest());\n }\n\n}\n",[59,93167,93168,93174,93178,93190,93194,93203,93207,93213,93222,93237,93253,93257,93261],{"__ignoreMap":57},[62,93169,93170,93172],{"class":64,"line":65},[62,93171,27875],{"class":68},[62,93173,29087],{"class":72},[62,93175,93176],{"class":64,"line":76},[62,93177,79],{"emptyLinePlaceholder":13},[62,93179,93180,93182,93184,93186,93188],{"class":64,"line":82},[62,93181,27875],{"class":68},[62,93183,2101],{"class":68},[62,93185,29096],{"class":72},[62,93187,6973],{"class":149},[62,93189,153],{"class":72},[62,93191,93192],{"class":64,"line":89},[62,93193,79],{"emptyLinePlaceholder":13},[62,93195,93196,93198,93201],{"class":64,"line":95},[62,93197,11671],{"class":68},[62,93199,93200],{"class":122}," HelloLambdaTest",[62,93202,126],{"class":72},[62,93204,93205],{"class":64,"line":101},[62,93206,79],{"emptyLinePlaceholder":13},[62,93208,93209,93211],{"class":64,"line":107},[62,93210,2143],{"class":72},[62,93212,11705],{"class":68},[62,93214,93215,93217,93220],{"class":64,"line":113},[62,93216,11710],{"class":68},[62,93218,93219],{"class":122}," shouldReturnHelloMessage",[62,93221,206],{"class":72},[62,93223,93224,93226,93229,93231,93233,93235],{"class":64,"line":129},[62,93225,13605],{"class":68},[62,93227,93228],{"class":72}," sut ",[62,93230,146],{"class":68},[62,93232,466],{"class":68},[62,93234,93108],{"class":122},[62,93236,822],{"class":72},[62,93238,93239,93241,93243,93246,93249,93251],{"class":64,"line":134},[62,93240,29183],{"class":122},[62,93242,2109],{"class":72},[62,93244,93245],{"class":1675},"\"Hello, AWS Lambda!\"",[62,93247,93248],{"class":72},",sut.",[62,93250,48132],{"class":122},[62,93252,1091],{"class":72},[62,93254,93255],{"class":64,"line":156},[62,93256,223],{"class":72},[62,93258,93259],{"class":64,"line":161},[62,93260,79],{"emptyLinePlaceholder":13},[62,93262,93263],{"class":64,"line":167},[62,93264,379],{"class":72},[22,93266,93267],{},"Run the test to make sure it passes. Now that we have our function and test, we need to package the function using the Maven Shade Plugin.",[26,93269,93271],{"id":93270},"maven-shade-plugin","Maven Shade Plugin",[22,93273,93274],{},"The Maven Shade Plugin is responsible for packaging the project classes together with their dependencies into a single \"Uber JAR\". This is what AWS Lambda expects when we upload our function.",[22,93276,93277,93278,93280,93281,2004],{},"To configure the Maven Shade Plugin, add a ",[59,93279,2189],{}," section containing the plugin configuration to the ",[59,93282,1765],{},[52,93284,93286],{"className":1769,"code":93285,"language":1771,"meta":57,"style":57},"\u003Cbuild>\n \u003Cplugins>\n \u003Cplugin>\n \u003CgroupId>org.apache.maven.plugins\u003C/groupId>\n \u003CartifactId>maven-shade-plugin\u003C/artifactId>\n \u003Cversion>3.2.4\u003C/version>\n \u003Cexecutions>\n \u003Cexecution>\n \u003Cphase>package\u003C/phase>\n \u003Cgoals>\n \u003Cgoal>shade\u003C/goal>\n \u003C/goals>\n \u003Cconfiguration>\n \u003CshadedArtifactAttached>true\u003C/shadedArtifactAttached>\n \u003CshadedClassifierName>aws-lambda\u003C/shadedClassifierName>\n \u003C/configuration>\n \u003C/execution>\n \u003C/executions>\n \u003C/plugin>\n \u003C/plugins>\n\u003C/build>\n",[59,93287,93288,93296,93304,93312,93324,93336,93348,93356,93364,93376,93384,93396,93404,93412,93426,93440,93448,93456,93464,93472,93480],{"__ignoreMap":57},[62,93289,93290,93292,93294],{"class":64,"line":65},[62,93291,760],{"class":72},[62,93293,2189],{"class":1780},[62,93295,1784],{"class":72},[62,93297,93298,93300,93302],{"class":64,"line":76},[62,93299,1789],{"class":72},[62,93301,92866],{"class":1780},[62,93303,1784],{"class":72},[62,93305,93306,93308,93310],{"class":64,"line":82},[62,93307,1896],{"class":72},[62,93309,3928],{"class":1780},[62,93311,1784],{"class":72},[62,93313,93314,93316,93318,93320,93322],{"class":64,"line":89},[62,93315,1905],{"class":72},[62,93317,1792],{"class":1780},[62,93319,92886],{"class":72},[62,93321,1792],{"class":1780},[62,93323,1784],{"class":72},[62,93325,93326,93328,93330,93332,93334],{"class":64,"line":95},[62,93327,1905],{"class":72},[62,93329,1806],{"class":1780},[62,93331,92899],{"class":72},[62,93333,1806],{"class":1780},[62,93335,1784],{"class":72},[62,93337,93338,93340,93342,93344,93346],{"class":64,"line":101},[62,93339,1905],{"class":72},[62,93341,1933],{"class":1780},[62,93343,92912],{"class":72},[62,93345,1933],{"class":1780},[62,93347,1784],{"class":72},[62,93349,93350,93352,93354],{"class":64,"line":107},[62,93351,1905],{"class":72},[62,93353,33441],{"class":1780},[62,93355,1784],{"class":72},[62,93357,93358,93360,93362],{"class":64,"line":113},[62,93359,15981],{"class":72},[62,93361,13587],{"class":1780},[62,93363,1784],{"class":72},[62,93365,93366,93368,93370,93372,93374],{"class":64,"line":129},[62,93367,16094],{"class":72},[62,93369,33506],{"class":1780},[62,93371,92942],{"class":72},[62,93373,33506],{"class":1780},[62,93375,1784],{"class":72},[62,93377,93378,93380,93382],{"class":64,"line":134},[62,93379,16094],{"class":72},[62,93381,33475],{"class":1780},[62,93383,1784],{"class":72},[62,93385,93386,93388,93390,93392,93394],{"class":64,"line":156},[62,93387,16384],{"class":72},[62,93389,33484],{"class":1780},[62,93391,92963],{"class":72},[62,93393,33484],{"class":1780},[62,93395,1784],{"class":72},[62,93397,93398,93400,93402],{"class":64,"line":161},[62,93399,16306],{"class":72},[62,93401,33475],{"class":1780},[62,93403,1784],{"class":72},[62,93405,93406,93408,93410],{"class":64,"line":167},[62,93407,16094],{"class":72},[62,93409,1997],{"class":1780},[62,93411,1784],{"class":72},[62,93413,93414,93416,93419,93422,93424],{"class":64,"line":173},[62,93415,16384],{"class":72},[62,93417,93418],{"class":1780},"shadedArtifactAttached",[62,93420,93421],{"class":72},">true\u003C/",[62,93423,93418],{"class":1780},[62,93425,1784],{"class":72},[62,93427,93428,93430,93433,93436,93438],{"class":64,"line":179},[62,93429,16384],{"class":72},[62,93431,93432],{"class":1780},"shadedClassifierName",[62,93434,93435],{"class":72},">aws-lambda\u003C/",[62,93437,93432],{"class":1780},[62,93439,1784],{"class":72},[62,93441,93442,93444,93446],{"class":64,"line":185},[62,93443,16306],{"class":72},[62,93445,1997],{"class":1780},[62,93447,1784],{"class":72},[62,93449,93450,93452,93454],{"class":64,"line":191},[62,93451,16040],{"class":72},[62,93453,13587],{"class":1780},[62,93455,1784],{"class":72},[62,93457,93458,93460,93462],{"class":64,"line":209},[62,93459,16002],{"class":72},[62,93461,33441],{"class":1780},[62,93463,1784],{"class":72},[62,93465,93466,93468,93470],{"class":64,"line":220},[62,93467,1973],{"class":72},[62,93469,3928],{"class":1780},[62,93471,1784],{"class":72},[62,93473,93474,93476,93478],{"class":64,"line":226},[62,93475,1982],{"class":72},[62,93477,92866],{"class":1780},[62,93479,1784],{"class":72},[62,93481,93482,93484,93486],{"class":64,"line":231},[62,93483,1818],{"class":72},[62,93485,2189],{"class":1780},[62,93487,1784],{"class":72},[22,93489,93490],{},"Now let's build our project using the following command:",[52,93492,93493],{"className":1663,"code":3632,"language":1665,"meta":57,"style":57},[59,93494,93495],{"__ignoreMap":57},[62,93496,93497,93499,93501],{"class":64,"line":65},[62,93498,3639],{"class":122},[62,93500,3642],{"class":1675},[62,93502,3645],{"class":1675},[22,93504,3648,93505,93507],{},[59,93506,3651],{}," directory of our project.",[26,93509,93511],{"id":93510},"deploying-and-testing-the-lambda-function-on-aws","Deploying and Testing the Lambda Function on AWS",[22,93513,93514,93515,93519,93520,2755],{},"Log into the ",[677,93516,91336],{"href":93517,"rel":93518},"https://console.aws.amazon.com/",[681],", navigate to the Lambda service, and create a new function using the \"Author from scratch\" option. Give your function a name, choose the Java 11 (Corretto) runtime, and specify the Lambda handler using the format ",[59,93521,93522],{},"package.class::method",[22,93524,93525],{},[653,93526],{"alt":93527,"src":93528},"Create new Function","/images/blog/2022/11/09/create-function.png",[22,93530,93531],{},"Upload your JAR file built earlier by choosing \"Upload a file from Amazon S3\" or \"Upload a .zip file\", and configure the remaining settings according to your needs.",[22,93533,93534],{},"Once the function is created, you can test it using the AWS Console by creating a test event with an appropriate input. In our case, just pass a string as input. When you run the test, you should see the Lambda function return your greeting message.",[22,93536,93537],{},"You can also view logs and metrics for your Lambda function in the AWS Console.",[26,93539,93541],{"id":93540},"conclusion-and-next-steps","Conclusion and Next Steps",[22,93543,93544],{},"In this tutorial, we've created a simple AWS Lambda function using plain Java and deployed it to AWS. We did not use any AWS libraries or services directly in our Lambda function, making it easy to build and test locally.",[22,93546,93547],{},"As you start building more complex serverless functions, you may want to explore using AWS SDKs, integrating with other AWS services like S3 or DynamoDB, or using frameworks like Spring Cloud Function to simplify the development process.",[22,93549,93550],{},"Stay tuned for more tutorials in this series where we'll dive deeper into various AWS Lambda topics, like handling events, using the AWS CLI, and leveraging the AWS CDK for infrastructure-as-code.",[1527,93552,93553],{},"html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}html pre.shiki code .suaqH, html code.shiki .suaqH{--shiki-default:#22863A;--shiki-dark:#85E89D;--shiki-sepia:#22863A}",{"title":57,"searchDepth":76,"depth":76,"links":93555},[93556,93557,93558,93559,93560],{"id":93065,"depth":76,"text":93066},{"id":93151,"depth":76,"text":93152},{"id":93270,"depth":76,"text":93271},{"id":93510,"depth":76,"text":93511},{"id":93540,"depth":76,"text":93541},{"_id":93562,"path":93563,"title":93564,"description":93565,"meta":93566,"body":93571},"content/blog/2022/09/29/domain-class-converter.md","/blog/2022/09/29/domain-class-converter","Spring Boot REST API Domain Class Converter","In this tutorial, you will learn about a class called the Domain Class Converter in Spring Data. This class will allow you to take arbitrary input like an id from a path variable in a REST API and automatically create a domain object by using Spring Data's CrudRepository.",{"slug":93567,"date":93568,"published":13,"tags":93569,"author":17,"cover":93570,"excerpt":-1},"domain-class-converter","2022-09-29T11:00:00.000Z",[2925,10159],"./domain-class-converter-thumbnail-small.png",{"type":19,"value":93572,"toc":94468},[93573,93576,93585,93587,93593,93623,93630,93634,93662,93846,93874,93933,93957,94128,94132,94138,94186,94193,94197,94203,94217,94313,94317,94320,94331,94353,94400,94409,94411,94421,94429,94452,94454,94457,94465],[22,93574,93575],{},"In this article, we'll discuss a recent tweet I sent out regarding the Domain Class Converter in Spring Data. The Domain Class Converter can convert any arbitrary input into a domain class managed by a Spring Data Repository (specifically a Crud Repository). We'll go through a tutorial that demonstrates how to use this feature.",[22,93577,93578,93579,93584],{},"If you're not already following me on Twitter, be sure to do so! I'm on Twitter as ",[677,93580,93583],{"href":93581,"rel":93582},"http://twitter.com/therealdanvega",[681],"TheRealDanVega",", where I share tips, discussions, and tutorials about Spring and programming in general.",[26,93586,93066],{"id":93065},[22,93588,93589,93590,80364],{},"We'll begin by bootstrapping a simple Spring application that uses Spring Data JPA. Head over to ",[677,93591,2901],{"href":2901,"rel":93592},[681],[915,93594,93595,93600,93607,93613],{},[37,93596,93597,93598],{},"Group ID: ",[59,93599,23065],{},[37,93601,93602,93603,93606],{},"Artifact ID: ",[59,93604,93605],{},"dcc"," (for Domain Class Converter)",[37,93608,93609,93610],{},"Java: ",[59,93611,93612],{},"version 17",[37,93614,93615,93616,976,93618,976,93621],{},"Dependencies: ",[59,93617,27737],{},[59,93619,93620],{},"data-jpa",[59,93622,26],{},[22,93624,93625,93629],{},[653,93626],{"alt":93627,"src":93628},"Create new project using Spring Initializr","/images/blog/2022/09/29/spring-init.png","Once your project is generated, you can open it in your favorite IDE (I'll be using IntelliJ in this tutorial).",[26,93631,93633],{"id":93632},"creating-the-model-repository-and-controller","Creating the Model, Repository, and Controller",[22,93635,93636,93637,93639,93640,976,93642,976,93644,976,93646,4201,93649,93652,93653,93655,93656,93658,93659,93661],{},"Our application will revolve around blog posts. We'll create a simple ",[59,93638,38700],{}," entity with fields such as ",[59,93641,6283],{},[59,93643,3196],{},[59,93645,2230],{},[59,93647,93648],{},"publishedOn",[59,93650,93651],{},"updatedOn",". To get started, create a ",[59,93654,16671],{}," package in your project and add a new class called ",[59,93657,38700],{},". Annotate this class with ",[59,93660,61725],{},", and then create the necessary fields and methods, such as a no-argument constructor, getters, and setters.",[52,93663,93665],{"className":54,"code":93664,"language":56,"meta":57,"style":57},"@Entity\npublic class Post {\n\n @Id\n private Integer id;\n private String title;\n private String content;\n private LocalDateTime publishedOn;\n private LocalDateTime updatedOn;\n\n public Post() {}\n\n public Post(Integer id, String title, String content) {\n this.id = id;\n this.title = title;\n this.content = content;\n this.publishedOn = LocalDateTime.now();\n this.updatedOn = LocalDateTime.now();;\n }\n\n // getters, setters & toString omitted\n\n}\n",[59,93666,93667,93673,93683,93687,93693,93699,93705,93712,93719,93726,93730,93738,93742,93762,93772,93782,93793,93809,93825,93829,93833,93838,93842],{"__ignoreMap":57},[62,93668,93669,93671],{"class":64,"line":65},[62,93670,942],{"class":72},[62,93672,8999],{"class":68},[62,93674,93675,93677,93679,93681],{"class":64,"line":76},[62,93676,116],{"class":68},[62,93678,119],{"class":68},[62,93680,41321],{"class":122},[62,93682,126],{"class":72},[62,93684,93685],{"class":64,"line":82},[62,93686,79],{"emptyLinePlaceholder":13},[62,93688,93689,93691],{"class":64,"line":89},[62,93690,2143],{"class":72},[62,93692,22298],{"class":68},[62,93694,93695,93697],{"class":64,"line":95},[62,93696,137],{"class":68},[62,93698,45372],{"class":72},[62,93700,93701,93703],{"class":64,"line":101},[62,93702,137],{"class":68},[62,93704,9036],{"class":72},[62,93706,93707,93709],{"class":64,"line":107},[62,93708,137],{"class":68},[62,93710,93711],{"class":72}," String content;\n",[62,93713,93714,93716],{"class":64,"line":113},[62,93715,137],{"class":68},[62,93717,93718],{"class":72}," LocalDateTime publishedOn;\n",[62,93720,93721,93723],{"class":64,"line":129},[62,93722,137],{"class":68},[62,93724,93725],{"class":72}," LocalDateTime updatedOn;\n",[62,93727,93728],{"class":64,"line":134},[62,93729,79],{"emptyLinePlaceholder":13},[62,93731,93732,93734,93736],{"class":64,"line":156},[62,93733,194],{"class":68},[62,93735,41321],{"class":122},[62,93737,58809],{"class":72},[62,93739,93740],{"class":64,"line":161},[62,93741,79],{"emptyLinePlaceholder":13},[62,93743,93744,93746,93748,93750,93752,93754,93756,93758,93760],{"class":64,"line":167},[62,93745,194],{"class":68},[62,93747,41321],{"class":122},[62,93749,8601],{"class":72},[62,93751,6283],{"class":889},[62,93753,8624],{"class":72},[62,93755,3196],{"class":889},[62,93757,8624],{"class":72},[62,93759,2230],{"class":889},[62,93761,768],{"class":72},[62,93763,93764,93766,93768,93770],{"class":64,"line":173},[62,93765,2405],{"class":149},[62,93767,9802],{"class":72},[62,93769,146],{"class":68},[62,93771,9954],{"class":72},[62,93773,93774,93776,93778,93780],{"class":64,"line":179},[62,93775,2405],{"class":149},[62,93777,89276],{"class":72},[62,93779,146],{"class":68},[62,93781,89281],{"class":72},[62,93783,93784,93786,93789,93791],{"class":64,"line":185},[62,93785,2405],{"class":149},[62,93787,93788],{"class":72},".content ",[62,93790,146],{"class":68},[62,93792,14207],{"class":72},[62,93794,93795,93797,93800,93802,93805,93807],{"class":64,"line":191},[62,93796,2405],{"class":149},[62,93798,93799],{"class":72},".publishedOn ",[62,93801,146],{"class":68},[62,93803,93804],{"class":72}," LocalDateTime.",[62,93806,43561],{"class":122},[62,93808,822],{"class":72},[62,93810,93811,93813,93816,93818,93820,93822],{"class":64,"line":209},[62,93812,2405],{"class":149},[62,93814,93815],{"class":72},".updatedOn ",[62,93817,146],{"class":68},[62,93819,93804],{"class":72},[62,93821,43561],{"class":122},[62,93823,93824],{"class":72},"();;\n",[62,93826,93827],{"class":64,"line":220},[62,93828,223],{"class":72},[62,93830,93831],{"class":64,"line":226},[62,93832,79],{"emptyLinePlaceholder":13},[62,93834,93835],{"class":64,"line":231},[62,93836,93837],{"class":85}," // getters, setters & toString omitted\n",[62,93839,93840],{"class":64,"line":236},[62,93841,79],{"emptyLinePlaceholder":13},[62,93843,93844],{"class":64,"line":242},[62,93845,379],{"class":72},[22,93847,80452,93848,93850,93851,93853,93854,93856,93857,19931,93859,93861,93862,93864,93865,976,93868,4201,93871,2755],{},[59,93849,23540],{}," package and add a new interface called ",[59,93852,55750],{},". This interface should extend the ",[59,93855,61831],{}," and take ",[59,93858,38700],{},[59,93860,979],{}," as its type arguments. By extending ",[59,93863,61831],{},", we get basic CRUD functionality out of the box, such as ",[59,93866,93867],{},"findById()",[59,93869,93870],{},"findAll()",[59,93872,93873],{},"save()",[52,93875,93877],{"className":54,"code":93876,"language":56,"meta":57,"style":57},"package dev.danvega.dcc.repository;\n\nimport dev.danvega.dcc.model.Post;\nimport org.springframework.data.repository.CrudRepository;\n\npublic interface PostRepository extends CrudRepository\u003CPost,Integer> {\n}\n",[59,93878,93879,93886,93890,93897,93903,93907,93929],{"__ignoreMap":57},[62,93880,93881,93883],{"class":64,"line":65},[62,93882,69],{"class":68},[62,93884,93885],{"class":72}," dev.danvega.dcc.repository;\n",[62,93887,93888],{"class":64,"line":76},[62,93889,79],{"emptyLinePlaceholder":13},[62,93891,93892,93894],{"class":64,"line":82},[62,93893,27875],{"class":68},[62,93895,93896],{"class":72}," dev.danvega.dcc.model.Post;\n",[62,93898,93899,93901],{"class":64,"line":89},[62,93900,27875],{"class":68},[62,93902,52430],{"class":72},[62,93904,93905],{"class":64,"line":95},[62,93906,79],{"emptyLinePlaceholder":13},[62,93908,93909,93911,93913,93915,93917,93919,93921,93923,93925,93927],{"class":64,"line":101},[62,93910,116],{"class":68},[62,93912,8531],{"class":68},[62,93914,38638],{"class":122},[62,93916,8537],{"class":68},[62,93918,22395],{"class":122},[62,93920,760],{"class":72},[62,93922,38700],{"class":68},[62,93924,32225],{"class":72},[62,93926,979],{"class":68},[62,93928,8552],{"class":72},[62,93930,93931],{"class":64,"line":107},[62,93932,379],{"class":72},[22,93934,81327,93935,93937,93938,93658,93940,93942,93943,93945,93946,93948,93949,93951,93952,19931,93954,93956],{},[59,93936,48327],{}," package and add a new class called ",[59,93939,41342],{},[59,93941,40266],{}," and add a ",[59,93944,56483],{}," with the path set to ",[59,93947,80617],{},". Inject an instance of ",[59,93950,55750],{}," using constructor injection, then create ",[59,93953,93870],{},[59,93955,93867],{}," methods that leverage the repository's methods to return posts.",[52,93958,93960],{"className":54,"code":93959,"language":56,"meta":57,"style":57},"@RestController\n@RequestMapping(\"/api/posts\")\npublic class PostController {\n\n private final PostRepository posts;\n\n public PostController(PostRepository posts) {\n this.posts = posts;\n }\n\n @GetMapping\n public Iterable\u003CPost> findAll() {\n return posts.findAll();\n }\n\n @GetMapping(\"/{id}\")\n public Post findById(@PathVariable Integer id) {\n return posts.findById(id);\n }\n\n}\n",[59,93961,93962,93968,93980,93990,93994,94003,94007,94019,94030,94034,94038,94044,94058,94068,94072,94076,94088,94106,94116,94120,94124],{"__ignoreMap":57},[62,93963,93964,93966],{"class":64,"line":65},[62,93965,942],{"class":72},[62,93967,2342],{"class":68},[62,93969,93970,93972,93974,93976,93978],{"class":64,"line":76},[62,93971,942],{"class":72},[62,93973,10592],{"class":68},[62,93975,2109],{"class":72},[62,93977,41368],{"class":1675},[62,93979,2212],{"class":72},[62,93981,93982,93984,93986,93988],{"class":64,"line":82},[62,93983,116],{"class":68},[62,93985,119],{"class":68},[62,93987,41379],{"class":122},[62,93989,126],{"class":72},[62,93991,93992],{"class":64,"line":89},[62,93993,79],{"emptyLinePlaceholder":13},[62,93995,93996,93998,94000],{"class":64,"line":95},[62,93997,137],{"class":68},[62,93999,458],{"class":68},[62,94001,94002],{"class":72}," PostRepository posts;\n",[62,94004,94005],{"class":64,"line":101},[62,94006,79],{"emptyLinePlaceholder":13},[62,94008,94009,94011,94013,94015,94017],{"class":64,"line":107},[62,94010,194],{"class":68},[62,94012,41379],{"class":122},[62,94014,55804],{"class":72},[62,94016,80755],{"class":889},[62,94018,768],{"class":72},[62,94020,94021,94023,94026,94028],{"class":64,"line":113},[62,94022,2405],{"class":149},[62,94024,94025],{"class":72},".posts ",[62,94027,146],{"class":68},[62,94029,47338],{"class":72},[62,94031,94032],{"class":64,"line":129},[62,94033,223],{"class":72},[62,94035,94036],{"class":64,"line":134},[62,94037,79],{"emptyLinePlaceholder":13},[62,94039,94040,94042],{"class":64,"line":156},[62,94041,2143],{"class":72},[62,94043,47319],{"class":68},[62,94045,94046,94048,94050,94052,94054,94056],{"class":64,"line":161},[62,94047,194],{"class":68},[62,94049,57781],{"class":72},[62,94051,38700],{"class":68},[62,94053,3135],{"class":72},[62,94055,10287],{"class":122},[62,94057,206],{"class":72},[62,94059,94060,94062,94064,94066],{"class":64,"line":167},[62,94061,360],{"class":68},[62,94063,4406],{"class":72},[62,94065,10287],{"class":122},[62,94067,822],{"class":72},[62,94069,94070],{"class":64,"line":173},[62,94071,223],{"class":72},[62,94073,94074],{"class":64,"line":179},[62,94075,79],{"emptyLinePlaceholder":13},[62,94077,94078,94080,94082,94084,94086],{"class":64,"line":185},[62,94079,2143],{"class":72},[62,94081,2548],{"class":68},[62,94083,2109],{"class":72},[62,94085,41486],{"class":1675},[62,94087,2212],{"class":72},[62,94089,94090,94092,94094,94096,94098,94100,94102,94104],{"class":64,"line":191},[62,94091,194],{"class":68},[62,94093,41556],{"class":72},[62,94095,38763],{"class":122},[62,94097,2475],{"class":72},[62,94099,23740],{"class":68},[62,94101,39510],{"class":72},[62,94103,6283],{"class":889},[62,94105,768],{"class":72},[62,94107,94108,94110,94112,94114],{"class":64,"line":209},[62,94109,360],{"class":68},[62,94111,4406],{"class":72},[62,94113,38763],{"class":122},[62,94115,23764],{"class":72},[62,94117,94118],{"class":64,"line":220},[62,94119,223],{"class":72},[62,94121,94122],{"class":64,"line":226},[62,94123,79],{"emptyLinePlaceholder":13},[62,94125,94126],{"class":64,"line":231},[62,94127,379],{"class":72},[26,94129,94131],{"id":94130},"configuring-the-application-properties","Configuring the Application Properties",[22,94133,94134,94135,94137],{},"We need to configure our application to work with the H2 in-memory database. Open your ",[59,94136,1265],{}," file and add the following properties:",[52,94139,94141],{"className":1269,"code":94140,"language":1271,"meta":57,"style":57},"spring.h2.console.enabled=true\nspring.h2.console.name=jpablog\nspring.datasource.url=jdbc:h2:mem:japablog\nspring.datasource.username=sa\nspring.datasource.password=\nspring.jpa.show-sql=true\n",[59,94142,94143,94149,94157,94165,94173,94180],{"__ignoreMap":57},[62,94144,94145,94147],{"class":64,"line":65},[62,94146,41993],{"class":68},[62,94148,1281],{"class":72},[62,94150,94151,94154],{"class":64,"line":76},[62,94152,94153],{"class":68},"spring.h2.console.name",[62,94155,94156],{"class":72},"=jpablog\n",[62,94158,94159,94162],{"class":64,"line":82},[62,94160,94161],{"class":68},"spring.datasource.url",[62,94163,94164],{"class":72},"=jdbc:h2:mem:japablog\n",[62,94166,94167,94170],{"class":64,"line":89},[62,94168,94169],{"class":68},"spring.datasource.username",[62,94171,94172],{"class":72},"=sa\n",[62,94174,94175,94178],{"class":64,"line":95},[62,94176,94177],{"class":68},"spring.datasource.password",[62,94179,3596],{"class":72},[62,94181,94182,94184],{"class":64,"line":101},[62,94183,83156],{"class":68},[62,94185,1281],{"class":72},[22,94187,94188,94189,94192],{},"These properties enable the H2 console, set its name to ",[59,94190,94191],{},"jpablog",", configure the data source URL, and enable the display of generated SQL.",[26,94194,94196],{"id":94195},"seeding-the-database","Seeding the Database",[22,94198,94199,94200,94202],{},"To test our application, we need some initial data in our database. We can achieve this by creating a ",[59,94201,2066],{}," bean. This functional interface allows us to execute some code after the application context has been created and before the application runs.",[22,94204,27669,94205,94207,94208,94210,94211,94213,94214,94216],{},[59,94206,2066],{}," bean, we'll inject an instance of ",[59,94209,55750],{}," and use its ",[59,94212,93873],{}," method to create and save some sample ",[59,94215,38700],{}," objects to the database.",[52,94218,94220],{"className":54,"code":94219,"language":56,"meta":57,"style":57},"@Bean\npublic CommandLineRunner seedData(PostRepository posts) {\n return args -> {\n posts.save(new Post(1, \"Hello World\", \"Welcome to my blog\"));\n posts.save(new Post(2, \"Hello JPA\", \"Working with Spring Data JPA\"));\n };\n}\n\n",[59,94221,94222,94228,94240,94250,94277,94305,94309],{"__ignoreMap":57},[62,94223,94224,94226],{"class":64,"line":65},[62,94225,942],{"class":72},[62,94227,2146],{"class":68},[62,94229,94230,94232,94234,94237],{"class":64,"line":76},[62,94231,116],{"class":68},[62,94233,62380],{"class":72},[62,94235,94236],{"class":122},"seedData",[62,94238,94239],{"class":72},"(PostRepository posts) {\n",[62,94241,94242,94244,94246,94248],{"class":64,"line":82},[62,94243,82091],{"class":68},[62,94245,2169],{"class":72},[62,94247,800],{"class":68},[62,94249,126],{"class":72},[62,94251,94252,94254,94256,94258,94260,94262,94264,94266,94268,94270,94272,94275],{"class":64,"line":89},[62,94253,5006],{"class":72},[62,94255,22562],{"class":122},[62,94257,2109],{"class":72},[62,94259,2426],{"class":68},[62,94261,41321],{"class":122},[62,94263,2109],{"class":72},[62,94265,6689],{"class":149},[62,94267,976],{"class":72},[62,94269,43550],{"class":1675},[62,94271,976],{"class":72},[62,94273,94274],{"class":1675},"\"Welcome to my blog\"",[62,94276,6979],{"class":72},[62,94278,94279,94281,94283,94285,94287,94289,94291,94293,94295,94298,94300,94303],{"class":64,"line":95},[62,94280,5006],{"class":72},[62,94282,22562],{"class":122},[62,94284,2109],{"class":72},[62,94286,2426],{"class":68},[62,94288,41321],{"class":122},[62,94290,2109],{"class":72},[62,94292,5219],{"class":149},[62,94294,976],{"class":72},[62,94296,94297],{"class":1675},"\"Hello JPA\"",[62,94299,976],{"class":72},[62,94301,94302],{"class":1675},"\"Working with Spring Data JPA\"",[62,94304,6979],{"class":72},[62,94306,94307],{"class":64,"line":101},[62,94308,82135],{"class":72},[62,94310,94311],{"class":64,"line":107},[62,94312,379],{"class":72},[26,94314,94316],{"id":94315},"using-the-domain-class-converter","Using the Domain Class Converter",[22,94318,94319],{},"With our application setup and initial data in place, we can now explore the main topic of this tutorial: the Domain Class Converter.",[22,94321,27669,94322,94324,94325,94327,94328,94330],{},[59,94323,41342],{},", we have a ",[59,94326,93867],{}," method that takes an ",[59,94329,6283],{}," path variable and returns the corresponding post. We can simplify this method by leveraging the Domain Class Converter.",[22,94332,94333,94334,94336,94337,94339,94340,94342,94343,94346,94347,94349,94350,94352],{},"Instead of using an ",[59,94335,979],{}," parameter for the ",[59,94338,6283],{},", we can use a ",[59,94341,38700],{}," parameter directly, and annotate it with ",[59,94344,94345],{},"@PathVariable(\"id\")",". This tells Spring to use the Domain Class Converter to look up the ",[59,94348,38700],{}," by its ",[59,94351,6283],{}," and assign it to the parameter.",[52,94354,94356],{"className":54,"code":94355,"language":56,"meta":57,"style":57},"@GetMapping(\"/{id}\")\npublic Post findById(@PathVariable(\"id\") Post post) {\n return post;\n}\n",[59,94357,94358,94370,94389,94396],{"__ignoreMap":57},[62,94359,94360,94362,94364,94366,94368],{"class":64,"line":65},[62,94361,942],{"class":72},[62,94363,2548],{"class":68},[62,94365,2109],{"class":72},[62,94367,41486],{"class":1675},[62,94369,2212],{"class":72},[62,94371,94372,94374,94376,94378,94380,94382,94384,94386],{"class":64,"line":76},[62,94373,116],{"class":68},[62,94375,41556],{"class":72},[62,94377,38763],{"class":122},[62,94379,2475],{"class":72},[62,94381,23740],{"class":68},[62,94383,2109],{"class":72},[62,94385,38796],{"class":1675},[62,94387,94388],{"class":72},") Post post) {\n",[62,94390,94391,94393],{"class":64,"line":82},[62,94392,82091],{"class":68},[62,94394,94395],{"class":72}," post;\n",[62,94397,94398],{"class":64,"line":89},[62,94399,379],{"class":72},[22,94401,94402,94403,94405,94406,94408],{},"Now our ",[59,94404,93867],{}," method is simplified, and we no longer need to call the repository's ",[59,94407,93867],{}," method explicitly in our controller.",[26,94410,19432],{"id":19431},[22,94412,94413,94414,94417,94418,94420],{},"Start the application and navigate to the H2 console at ",[677,94415,52672],{"href":52672,"rel":94416},[681],". Connect to the database using the data source name (jpablog) and view the contents of the ",[59,94419,18386],{}," table. You should see the two sample posts we added earlier.",[22,94422,94423,94424,19931,94426,94428],{},"Now, test the ",[59,94425,93870],{},[59,94427,93867],{}," endpoints in your browser:",[915,94430,94431,94438,94445],{},[37,94432,94433,94437],{},[677,94434,94435],{"href":94435,"rel":94436},"http://localhost:8080/api/posts",[681]," should return a list of all posts.",[37,94439,94440,94444],{},[677,94441,94442],{"href":94442,"rel":94443},"http://localhost:8080/api/posts/1",[681]," should return the post with ID 1.",[37,94446,94447,94451],{},[677,94448,94449],{"href":94449,"rel":94450},"http://localhost:8080/api/posts/2",[681]," should return the post with ID 2.",[26,94453,1499],{"id":1498},[22,94455,94456],{},"The Domain Class Converter is a useful feature that can simplify your Spring Data application by converting arbitrary input, like an ID, into a domain class managed by a Crud Repository. Though it may seem like a small change, it can make your code cleaner and more concise.",[22,94458,94459,94460,94464],{},"I hope you found this tutorial helpful! If you did, please give it a thumbs up, and don't forget to follow me on ",[677,94461,86336],{"href":94462,"rel":94463},"https://twitter.com/the_real_DanVega",[681]," for more tips, tutorials, and discussions about Spring Boot, Java, and programming in general. Happy coding!",[1527,94466,94467],{},"html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .spoOn, html code.shiki .spoOn{--shiki-default:#E36209;--shiki-dark:#FFAB70;--shiki-sepia:#E36209}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}html pre.shiki code .s4-Ip, html code.shiki .s4-Ip{--shiki-default:#6A737D;--shiki-dark:#6A737D;--shiki-sepia:#6A737D}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}",{"title":57,"searchDepth":76,"depth":76,"links":94469},[94470,94471,94472,94473,94474,94475,94476],{"id":93065,"depth":76,"text":93066},{"id":93632,"depth":76,"text":93633},{"id":94130,"depth":76,"text":94131},{"id":94195,"depth":76,"text":94196},{"id":94315,"depth":76,"text":94316},{"id":19431,"depth":76,"text":19432},{"id":1498,"depth":76,"text":1499},{"_id":94478,"path":94479,"title":94480,"description":73331,"meta":94481,"body":94486},"content/blog/2022/09/22/spring-security-cors.md","/blog/2022/09/22/spring-security-cors","Spring Security CORS: How to configure CORS in Spring Boot & Spring Security",{"slug":94482,"date":94483,"published":13,"tags":94484,"author":17,"cover":94485,"excerpt":-1},"spring-security-cors","2022-09-22T08:00:00.000Z",[10914],"./ss-cors-thumbnail.jpeg",{"type":19,"value":94487,"toc":95486},[94488,94491,94493,94517,94521,94534,94539,94542,94546,94587,94681,94685,94699,94716,94931,94941,94945,94948,94963,94967,94973,94981,95029,95035,95051,95074,95077,95095,95101,95107,95111,95121,95233,95243,95247,95250,95263,95269,95469,95475,95477,95480,95483],[22,94489,94490],{},"Authentication is a vital aspect of most applications, and Spring Boot provides several methods to control access. One of these methods is the use of CORS - a flexible, HTTP header-based mechanism that allows for the specification of authorized cross-domain requests. This article guides you through configuring CORS in Spring Boot at the controller, method, and global levels, as well as managing CORS when adding Spring Security.",[26,94492,93066],{"id":93065},[22,94494,94495,94496,19931,94500,94505,94506,94510,94511,94516],{},"The tutorial assumes you have ",[677,94497,15],{"href":94498,"rel":94499},"https://www.java.com/en/download/",[681],[677,94501,94504],{"href":94502,"rel":94503},"https://maven.apache.org/",[681],"Maven"," installed on your machine. You would also need an IDE such as ",[677,94507,94509],{"href":57660,"rel":94508},[681],"IntelliJ",". All the resources and code used in this article are available ",[677,94512,94515],{"href":94513,"rel":94514},"https://github.com/danvega/spring-security-cors-demo",[681],"on this GitHub repository",". Let's dive right in!",[26,94518,94520],{"id":94519},"creating-a-new-spring-boot-project","Creating a New Spring Boot Project",[22,94522,94523,94524,94527,94528,94530,94531,94533],{},"Start by creating a new Maven project at ",[677,94525,2903],{"href":2901,"rel":94526},[681],", choosing Java as the language, Java 17 as the version, and fill in the ",[59,94529,34902],{}," as \"dev.danvega\" and ",[59,94532,34908],{}," as \"cors-demo\". The only dependency we would need for now is Spring Web.",[22,94535,94536],{},[653,94537],{"alt":24606,"src":94538},"/images/blog/2022/09/22/cors-spring-init.png",[22,94540,94541],{},"Once done with the setup, generate the project and open the downloaded .zip file in your preferred text editor or IDE.",[26,94543,94545],{"id":94544},"building-our-model","Building Our Model",[22,94547,94548,94549,94552,94553,94555,94556,94559,94560,94562,94563,94565,94566,94568,94569,94571,94572,94575,94576,976,94578,976,94581,4201,94584,2755],{},"For our demo, we will create a ",[59,94550,94551],{},"Coffee"," model. In the ",[59,94554,24813],{}," package within the ",[59,94557,94558],{},"src"," folder, create a new package named ",[59,94561,16671],{},". Within the ",[59,94564,16671],{}," package, create a new class named ",[59,94567,94551],{},". Here, we will create an ",[59,94570,51733],{}," called ",[59,94573,94574],{},"Size"," that contains the values ",[59,94577,76450],{},[59,94579,94580],{},"tall",[59,94582,94583],{},"grande",[59,94585,94586],{},"venti",[52,94588,94590],{"className":54,"code":94589,"language":56,"meta":57,"style":57},"public class Coffee {\n private Integer id;\n private String name;\n private Size size;\n\n // TODO: Add getters and setters\n}\n\npublic enum Size {\n SHORT,\n TALL,\n GRANDE,\n VENTI\n}\n",[59,94591,94592,94603,94609,94615,94622,94626,94631,94635,94639,94651,94658,94665,94672,94677],{"__ignoreMap":57},[62,94593,94594,94596,94598,94601],{"class":64,"line":65},[62,94595,116],{"class":68},[62,94597,119],{"class":68},[62,94599,94600],{"class":122}," Coffee",[62,94602,126],{"class":72},[62,94604,94605,94607],{"class":64,"line":76},[62,94606,137],{"class":68},[62,94608,45372],{"class":72},[62,94610,94611,94613],{"class":64,"line":82},[62,94612,137],{"class":68},[62,94614,9406],{"class":72},[62,94616,94617,94619],{"class":64,"line":89},[62,94618,137],{"class":68},[62,94620,94621],{"class":72}," Size size;\n",[62,94623,94624],{"class":64,"line":95},[62,94625,79],{"emptyLinePlaceholder":13},[62,94627,94628],{"class":64,"line":101},[62,94629,94630],{"class":85}," // TODO: Add getters and setters\n",[62,94632,94633],{"class":64,"line":107},[62,94634,379],{"class":72},[62,94636,94637],{"class":64,"line":113},[62,94638,79],{"emptyLinePlaceholder":13},[62,94640,94641,94643,94646,94649],{"class":64,"line":129},[62,94642,116],{"class":68},[62,94644,94645],{"class":68}," enum",[62,94647,94648],{"class":122}," Size",[62,94650,126],{"class":72},[62,94652,94653,94656],{"class":64,"line":134},[62,94654,94655],{"class":149}," SHORT",[62,94657,3338],{"class":72},[62,94659,94660,94663],{"class":64,"line":156},[62,94661,94662],{"class":149}," TALL",[62,94664,3338],{"class":72},[62,94666,94667,94670],{"class":64,"line":161},[62,94668,94669],{"class":149}," GRANDE",[62,94671,3338],{"class":72},[62,94673,94674],{"class":64,"line":167},[62,94675,94676],{"class":149}," VENTI\n",[62,94678,94679],{"class":64,"line":173},[62,94680,379],{"class":72},[4168,94682,94684],{"id":94683},"creating-our-controller","Creating Our Controller",[22,94686,94687,94688,48324,94690,94692,94693,94695,94696,2755],{},"To begin, create a new package called ",[59,94689,48327],{},[59,94691,24813],{}," package. Inside the ",[59,94694,48327],{}," package, create a new Java class called ",[59,94697,94698],{},"CoffeeController",[22,94700,47116,94701,94703,94704,94706,94707,94709,94710,94712,94713,94715],{},[59,94702,94698],{}," class, we will use the ",[59,94705,40266],{}," annotation to indicate that the class is capable of accepting requests and returning responses. We will also set the ",[59,94708,56483],{}," to \"API/coffee\". Finally, we will create a list of coffees and define two mappings: a ",[59,94711,48908],{}," to return all the coffees, and a ",[59,94714,48918],{}," to delete a specific coffee by ID.",[52,94717,94719],{"className":54,"code":94718,"language":56,"meta":57,"style":57},"@RestController\n@RequestMapping(\"API/coffee\")\npublic class CoffeeController {\n private List\u003CCoffee> coffeeList = new ArrayList\u003C>(Arrays.asList(\n new Coffee(1, \"Americano\", Size.GRANDE),\n new Coffee(2, \"Latte\", Size.VENTI),\n new Coffee(3, \"Macchiato\", Size.TALL)\n ));\n\n @GetMapping\n public List\u003CCoffee> findAll() {\n return coffeeList;\n }\n\n @DeleteMapping(\"/{id}\")\n public void delete(@PathVariable Integer id) {\n coffeeList.removeIf(c -> c.getId().equals(id));\n }\n}\n\n",[59,94720,94721,94727,94740,94751,94773,94791,94809,94827,94832,94836,94842,94856,94863,94867,94871,94883,94901,94923,94927],{"__ignoreMap":57},[62,94722,94723,94725],{"class":64,"line":65},[62,94724,942],{"class":72},[62,94726,2342],{"class":68},[62,94728,94729,94731,94733,94735,94738],{"class":64,"line":76},[62,94730,942],{"class":72},[62,94732,10592],{"class":68},[62,94734,2109],{"class":72},[62,94736,94737],{"class":1675},"\"API/coffee\"",[62,94739,2212],{"class":72},[62,94741,94742,94744,94746,94749],{"class":64,"line":82},[62,94743,116],{"class":68},[62,94745,119],{"class":68},[62,94747,94748],{"class":122}," CoffeeController",[62,94750,126],{"class":72},[62,94752,94753,94755,94757,94759,94762,94764,94766,94769,94771],{"class":64,"line":89},[62,94754,137],{"class":68},[62,94756,3079],{"class":72},[62,94758,94551],{"class":68},[62,94760,94761],{"class":72},"> coffeeList ",[62,94763,146],{"class":68},[62,94765,466],{"class":68},[62,94767,94768],{"class":72}," ArrayList\u003C>(Arrays.",[62,94770,32036],{"class":122},[62,94772,3301],{"class":72},[62,94774,94775,94777,94779,94781,94783,94785,94788],{"class":64,"line":95},[62,94776,3306],{"class":68},[62,94778,94600],{"class":122},[62,94780,2109],{"class":72},[62,94782,6689],{"class":149},[62,94784,976],{"class":72},[62,94786,94787],{"class":1675},"\"Americano\"",[62,94789,94790],{"class":72},", Size.GRANDE),\n",[62,94792,94793,94795,94797,94799,94801,94803,94806],{"class":64,"line":101},[62,94794,3306],{"class":68},[62,94796,94600],{"class":122},[62,94798,2109],{"class":72},[62,94800,5219],{"class":149},[62,94802,976],{"class":72},[62,94804,94805],{"class":1675},"\"Latte\"",[62,94807,94808],{"class":72},", Size.VENTI),\n",[62,94810,94811,94813,94815,94817,94819,94821,94824],{"class":64,"line":107},[62,94812,3306],{"class":68},[62,94814,94600],{"class":122},[62,94816,2109],{"class":72},[62,94818,4472],{"class":149},[62,94820,976],{"class":72},[62,94822,94823],{"class":1675},"\"Macchiato\"",[62,94825,94826],{"class":72},", Size.TALL)\n",[62,94828,94829],{"class":64,"line":113},[62,94830,94831],{"class":72}," ));\n",[62,94833,94834],{"class":64,"line":129},[62,94835,79],{"emptyLinePlaceholder":13},[62,94837,94838,94840],{"class":64,"line":134},[62,94839,2143],{"class":72},[62,94841,47319],{"class":68},[62,94843,94844,94846,94848,94850,94852,94854],{"class":64,"line":156},[62,94845,194],{"class":68},[62,94847,3079],{"class":72},[62,94849,94551],{"class":68},[62,94851,3135],{"class":72},[62,94853,10287],{"class":122},[62,94855,206],{"class":72},[62,94857,94858,94860],{"class":64,"line":161},[62,94859,360],{"class":68},[62,94861,94862],{"class":72}," coffeeList;\n",[62,94864,94865],{"class":64,"line":167},[62,94866,223],{"class":72},[62,94868,94869],{"class":64,"line":173},[62,94870,79],{"emptyLinePlaceholder":13},[62,94872,94873,94875,94877,94879,94881],{"class":64,"line":179},[62,94874,2143],{"class":72},[62,94876,23719],{"class":68},[62,94878,2109],{"class":72},[62,94880,41486],{"class":1675},[62,94882,2212],{"class":72},[62,94884,94885,94887,94889,94891,94893,94895,94897,94899],{"class":64,"line":185},[62,94886,194],{"class":68},[62,94888,200],{"class":68},[62,94890,38982],{"class":122},[62,94892,2475],{"class":72},[62,94894,23740],{"class":68},[62,94896,39510],{"class":72},[62,94898,6283],{"class":889},[62,94900,768],{"class":72},[62,94902,94903,94906,94908,94911,94913,94915,94917,94919,94921],{"class":64,"line":191},[62,94904,94905],{"class":72}," coffeeList.",[62,94907,23352],{"class":122},[62,94909,94910],{"class":72},"(c ",[62,94912,800],{"class":68},[62,94914,65083],{"class":72},[62,94916,23363],{"class":122},[62,94918,3229],{"class":72},[62,94920,3232],{"class":122},[62,94922,23370],{"class":72},[62,94924,94925],{"class":64,"line":209},[62,94926,223],{"class":72},[62,94928,94929],{"class":64,"line":220},[62,94930,379],{"class":72},[22,94932,94933,94934,94937,94938,2755],{},"If you run the application using the command ",[59,94935,94936],{},"mvn spring-boot:run"," (or directly from your IDE), you can view the coffee list at ",[59,94939,94940],{},"localhost:8080/API/coffee",[26,94942,94944],{"id":94943},"understanding-cors-cross-origin-resource-sharing","Understanding CORS: Cross-Origin Resource Sharing",[22,94946,94947],{},"You're able to view the coffee list at the provided localhost URL because the request is coming from the same origin, where the domain and port match. However, issues may arise when cross-origin requests occur, such as when the domain, scheme, or port differ.",[22,94949,94950,94951,94954,94955,94957,94958,2755],{},"For example, suppose another application is running on a different port, say ",[59,94952,94953],{},"localhost:5173",", and it attempts to fetch our coffee list from ",[59,94956,94940],{},". In this case, CORS intervenes and blocks the request because it originates from a different port. If you're interested in learning more about this topic, ",[677,94959,94962],{"href":94960,"rel":94961},"https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS",[681],"Mozilla's network documentation provides an in-depth explanation",[26,94964,94966],{"id":94965},"configuring-cors-in-your-spring-boot-application","Configuring CORS in Your Spring Boot Application",[22,94968,94969,94970,43329],{},"So, how do we allow requests from other origins to access our APIs? Spring Boot provides a handy way to configure CORS using the ",[59,94971,94972],{},"@CrossOrigin",[22,94974,94975,94976,94978,94979,22822],{},"This annotation can be used at both method-level and class-level. For now, let's start by applying it at the class-level by adding ",[59,94977,94972],{}," at the top of our ",[59,94980,94698],{},[52,94982,94984],{"className":54,"code":94983,"language":56,"meta":57,"style":57},"@RestController\n@RequestMapping(\"API/coffee\")\n@CrossOrigin\npublic class CoffeeController {\n // controller content here...\n}\n",[59,94985,94986,94992,95004,95010,95020,95025],{"__ignoreMap":57},[62,94987,94988,94990],{"class":64,"line":65},[62,94989,942],{"class":72},[62,94991,2342],{"class":68},[62,94993,94994,94996,94998,95000,95002],{"class":64,"line":76},[62,94995,942],{"class":72},[62,94997,10592],{"class":68},[62,94999,2109],{"class":72},[62,95001,94737],{"class":1675},[62,95003,2212],{"class":72},[62,95005,95006,95008],{"class":64,"line":82},[62,95007,942],{"class":72},[62,95009,20398],{"class":68},[62,95011,95012,95014,95016,95018],{"class":64,"line":89},[62,95013,116],{"class":68},[62,95015,119],{"class":68},[62,95017,94748],{"class":122},[62,95019,126],{"class":72},[62,95021,95022],{"class":64,"line":95},[62,95023,95024],{"class":85}," // controller content here...\n",[62,95026,95027],{"class":64,"line":101},[62,95028,379],{"class":72},[22,95030,95031,95032,95034],{},"With this configuration, the CORS error we received earlier when we tried to fetch the coffee list from ",[59,95033,94953],{}," should be resolved. However, this configuration allows access to all origins, which may not be suitable for most applications.",[22,95036,95037,95038,95040,95041,95044,95045,95047,95048,95050],{},"Therefore, if you want to allow specific origins, you can do so by passing them to the ",[59,95039,94972],{}," annotation. You can either use the ",[59,95042,95043],{},"origins"," property or pass them directly as the ",[59,95046,2553],{}," property (which is an alias for ",[59,95049,95043],{},"). The syntax would look like this:",[52,95052,95054],{"className":54,"code":95053,"language":56,"meta":57,"style":57},"@CrossOrigin(origins = \"http://localhost:5173\")\n",[59,95055,95056],{"__ignoreMap":57},[62,95057,95058,95060,95063,95065,95067,95069,95072],{"class":64,"line":65},[62,95059,942],{"class":72},[62,95061,95062],{"class":68},"CrossOrigin",[62,95064,2109],{"class":72},[62,95066,95043],{"class":149},[62,95068,2556],{"class":68},[62,95070,95071],{"class":1675}," \"http://localhost:5173\"",[62,95073,2212],{"class":72},[22,95075,95076],{},"or",[52,95078,95080],{"className":54,"code":95079,"language":56,"meta":57,"style":57},"@CrossOrigin(\"http://localhost:5173\")\n",[59,95081,95082],{"__ignoreMap":57},[62,95083,95084,95086,95088,95090,95093],{"class":64,"line":65},[62,95085,942],{"class":72},[62,95087,95062],{"class":68},[62,95089,2109],{"class":72},[62,95091,95092],{"class":1675},"\"http://localhost:5173\"",[62,95094,2212],{"class":72},[22,95096,95097,95098,95100],{},"In either case, only requests from ",[59,95099,94953],{}," will be allowed, and other cross-origin requests will be blocked. This is more suitable for a production-grade application where you want only specific applications to access your APIs.",[22,95102,95103,95104,95106],{},"Moreover, the ",[59,95105,94972],{}," annotation can also be applied at a granular level, specifically on methods, which allows for fine-grained control over which API endpoints should allow cross-origin requests.",[26,95108,95110],{"id":95109},"configuring-cors-globally","Configuring CORS Globally",[22,95112,95113,95114,95117,95118,22831],{},"For larger applications with many controllers, it may be more efficient to configure CORS globally instead of annotating each controller or method separately. This can be done by creating a configuration class that implements the ",[59,95115,95116],{},"WebMvcConfigurer"," interface and overrides its ",[59,95119,95120],{},"addCorsMappings",[52,95122,95124],{"className":54,"code":95123,"language":56,"meta":57,"style":57},"@Configuration\npublic class WebMvcConfig implements WebMvcConfigurer {\n @Override\n public void addCorsMappings(CorsRegistry registry) {\n registry.addMapping(\"/**\")\n .allowedOrigins(\"http://localhost:5173\")\n .allowedMethods(\"GET\", \"POST\", \"PUT\", \"DELETE\");\n }\n}\n\n",[59,95125,95126,95132,95148,95154,95170,95184,95197,95225,95229],{"__ignoreMap":57},[62,95127,95128,95130],{"class":64,"line":65},[62,95129,942],{"class":72},[62,95131,11133],{"class":68},[62,95133,95134,95136,95138,95141,95143,95146],{"class":64,"line":76},[62,95135,116],{"class":68},[62,95137,119],{"class":68},[62,95139,95140],{"class":122}," WebMvcConfig",[62,95142,13520],{"class":68},[62,95144,95145],{"class":122}," WebMvcConfigurer",[62,95147,126],{"class":72},[62,95149,95150,95152],{"class":64,"line":82},[62,95151,2143],{"class":72},[62,95153,13555],{"class":68},[62,95155,95156,95158,95160,95163,95166,95168],{"class":64,"line":89},[62,95157,194],{"class":68},[62,95159,200],{"class":68},[62,95161,95162],{"class":122}," addCorsMappings",[62,95164,95165],{"class":72},"(CorsRegistry ",[62,95167,39791],{"class":889},[62,95169,768],{"class":72},[62,95171,95172,95174,95177,95179,95182],{"class":64,"line":95},[62,95173,68889],{"class":72},[62,95175,95176],{"class":122},"addMapping",[62,95178,2109],{"class":72},[62,95180,95181],{"class":1675},"\"/**\"",[62,95183,2212],{"class":72},[62,95185,95186,95188,95191,95193,95195],{"class":64,"line":101},[62,95187,3862],{"class":72},[62,95189,95190],{"class":122},"allowedOrigins",[62,95192,2109],{"class":72},[62,95194,95092],{"class":1675},[62,95196,2212],{"class":72},[62,95198,95199,95201,95204,95206,95209,95211,95213,95215,95218,95220,95223],{"class":64,"line":107},[62,95200,3862],{"class":72},[62,95202,95203],{"class":122},"allowedMethods",[62,95205,2109],{"class":72},[62,95207,95208],{"class":1675},"\"GET\"",[62,95210,976],{"class":72},[62,95212,16082],{"class":1675},[62,95214,976],{"class":72},[62,95216,95217],{"class":1675},"\"PUT\"",[62,95219,976],{"class":72},[62,95221,95222],{"class":1675},"\"DELETE\"",[62,95224,1133],{"class":72},[62,95226,95227],{"class":64,"line":113},[62,95228,223],{"class":72},[62,95230,95231],{"class":64,"line":129},[62,95232,379],{"class":72},[22,95234,95235,95236,95239,95240,95242],{},"In the code above, we allow all endpoints (indicated by ",[59,95237,95238],{},"/**",") to accept cross-origin requests from ",[59,95241,94953],{}," and permit the HTTP methods GET, POST, PUT, and DELETE.",[26,95244,95246],{"id":95245},"configuring-cors-with-spring-security","Configuring CORS With Spring Security",[22,95248,95249],{},"Adding Spring Security to a Spring Boot application changes things a bit. The issue is that CORS needs to be processed before Spring Security. This is because CORS makes a preflight request that does not contain any cookies. If Spring Security is processed first, it will reject the request as unauthenticated.",[22,95251,95252,95253,95256,95257,95259,95260,2755],{},"To solve this, we need to include a CORS configuration in our Spring Security configuration class. Spring's HTTP security has a ",[59,95254,95255],{},"cors()"," method that can be used to apply CORS configuration. Calling ",[59,95258,95255],{}," without arguments configures Spring Security to use a bean named ",[59,95261,95262],{},"CorsConfigurationSource",[22,95264,95265,95266,95268],{},"We can define this ",[59,95267,95262],{}," bean in our security configuration class itself.",[52,95270,95272],{"className":54,"code":95271,"language":56,"meta":57,"style":57},"@EnableWebSecurity\npublic class SecurityConfig {\n @Bean\n public CorsConfigurationSource corsConfigurationSource() {\n CorsConfiguration configuration = new CorsConfiguration();\n configuration.setAllowedOrigins(Arrays.asList(\"http://localhost:5173\"));\n configuration.setAllowedMethods(Arrays.asList(\"GET\", \"POST\"));\n UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();\n source.registerCorsConfiguration(\"/**\", configuration);\n return source;\n }\n\n @Bean\n public SecurityFilterChain securityFilterChain(HttpSecurity http) {\n http.authorizeRequests().anyRequest().authenticated().and().httpBasic();\n return http.build();\n }\n}\n\n",[59,95273,95274,95280,95290,95296,95308,95322,95341,95362,95376,95391,95398,95402,95406,95412,95426,95451,95461,95465],{"__ignoreMap":57},[62,95275,95276,95278],{"class":64,"line":65},[62,95277,942],{"class":72},[62,95279,11461],{"class":68},[62,95281,95282,95284,95286,95288],{"class":64,"line":76},[62,95283,116],{"class":68},[62,95285,119],{"class":68},[62,95287,11470],{"class":122},[62,95289,126],{"class":72},[62,95291,95292,95294],{"class":64,"line":82},[62,95293,2143],{"class":72},[62,95295,2146],{"class":68},[62,95297,95298,95300,95303,95306],{"class":64,"line":89},[62,95299,194],{"class":68},[62,95301,95302],{"class":72}," CorsConfigurationSource ",[62,95304,95305],{"class":122},"corsConfigurationSource",[62,95307,206],{"class":72},[62,95309,95310,95313,95315,95317,95320],{"class":64,"line":95},[62,95311,95312],{"class":72}," CorsConfiguration configuration ",[62,95314,146],{"class":68},[62,95316,466],{"class":68},[62,95318,95319],{"class":122}," CorsConfiguration",[62,95321,822],{"class":72},[62,95323,95324,95327,95330,95333,95335,95337,95339],{"class":64,"line":101},[62,95325,95326],{"class":72}," configuration.",[62,95328,95329],{"class":122},"setAllowedOrigins",[62,95331,95332],{"class":72},"(Arrays.",[62,95334,32036],{"class":122},[62,95336,2109],{"class":72},[62,95338,95092],{"class":1675},[62,95340,6979],{"class":72},[62,95342,95343,95345,95348,95350,95352,95354,95356,95358,95360],{"class":64,"line":107},[62,95344,95326],{"class":72},[62,95346,95347],{"class":122},"setAllowedMethods",[62,95349,95332],{"class":72},[62,95351,32036],{"class":122},[62,95353,2109],{"class":72},[62,95355,95208],{"class":1675},[62,95357,976],{"class":72},[62,95359,16082],{"class":1675},[62,95361,6979],{"class":72},[62,95363,95364,95367,95369,95371,95374],{"class":64,"line":113},[62,95365,95366],{"class":72}," UrlBasedCorsConfigurationSource source ",[62,95368,146],{"class":68},[62,95370,466],{"class":68},[62,95372,95373],{"class":122}," UrlBasedCorsConfigurationSource",[62,95375,822],{"class":72},[62,95377,95378,95381,95384,95386,95388],{"class":64,"line":129},[62,95379,95380],{"class":72}," source.",[62,95382,95383],{"class":122},"registerCorsConfiguration",[62,95385,2109],{"class":72},[62,95387,95181],{"class":1675},[62,95389,95390],{"class":72},", configuration);\n",[62,95392,95393,95395],{"class":64,"line":134},[62,95394,360],{"class":68},[62,95396,95397],{"class":72}," source;\n",[62,95399,95400],{"class":64,"line":156},[62,95401,223],{"class":72},[62,95403,95404],{"class":64,"line":161},[62,95405,79],{"emptyLinePlaceholder":13},[62,95407,95408,95410],{"class":64,"line":167},[62,95409,2143],{"class":72},[62,95411,2146],{"class":68},[62,95413,95414,95416,95418,95420,95422,95424],{"class":64,"line":173},[62,95415,194],{"class":68},[62,95417,15452],{"class":72},[62,95419,11490],{"class":122},[62,95421,11493],{"class":72},[62,95423,11496],{"class":889},[62,95425,768],{"class":72},[62,95427,95428,95431,95433,95435,95437,95439,95441,95443,95445,95447,95449],{"class":64,"line":179},[62,95429,95430],{"class":72}," http.",[62,95432,84626],{"class":122},[62,95434,3229],{"class":72},[62,95436,11544],{"class":122},[62,95438,3229],{"class":72},[62,95440,15518],{"class":122},[62,95442,3229],{"class":72},[62,95444,84694],{"class":122},[62,95446,3229],{"class":72},[62,95448,81114],{"class":122},[62,95450,822],{"class":72},[62,95452,95453,95455,95457,95459],{"class":64,"line":185},[62,95454,360],{"class":68},[62,95456,84623],{"class":72},[62,95458,2189],{"class":122},[62,95460,822],{"class":72},[62,95462,95463],{"class":64,"line":191},[62,95464,223],{"class":72},[62,95466,95467],{"class":64,"line":209},[62,95468,379],{"class":72},[22,95470,95471,95472,95474],{},"In the code above, we create a ",[59,95473,95262],{}," bean, define allowed origins and methods for CORS, register CORS configuration, and use this CORS configuration bean in our Spring Security configuration.",[26,95476,32553],{"id":32552},[22,95478,95479],{},"In this detailed step-by-step guide, we covered ways to configure CORS for your Spring controllers and methods, create global configurations for ease, and do the same for when you introduce Spring Security into your Spring application. Configuring CORS properly ensures that your APIs are only accessed by trusted domains, providing an extra layer of security for your application.",[22,95481,95482],{},"If you found this article helpful or have any questions or comments, feel free to reach out. Happy coding!",[1527,95484,95485],{},"html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .s4-Ip, html code.shiki .s4-Ip{--shiki-default:#6A737D;--shiki-dark:#6A737D;--shiki-sepia:#6A737D}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}html pre.shiki code .spoOn, html code.shiki .spoOn{--shiki-default:#E36209;--shiki-dark:#FFAB70;--shiki-sepia:#E36209}",{"title":57,"searchDepth":76,"depth":76,"links":95487},[95488,95489,95490,95491,95492,95493,95494,95495],{"id":93065,"depth":76,"text":93066},{"id":94519,"depth":76,"text":94520},{"id":94544,"depth":76,"text":94545},{"id":94943,"depth":76,"text":94944},{"id":94965,"depth":76,"text":94966},{"id":95109,"depth":76,"text":95110},{"id":95245,"depth":76,"text":95246},{"id":32552,"depth":76,"text":32553},{"_id":95497,"path":95498,"title":95499,"description":95500,"meta":95501,"body":95506},"content/blog/2022/09/09/spring-security-jwt.md","/blog/2022/09/09/spring-security-jwt","How to Secure your REST APIs with Spring Security & JSON Web Tokens (JWTs)","In this tutorial, you will learn how to secure REST APIs with Spring Security and JSON Web Tokens (JWTs).",{"slug":95502,"date":95503,"published":13,"tags":95504,"author":17,"cover":95505,"excerpt":-1},"spring-security-jwt","2022-09-09T16:00:00.000Z",[2925,10914],"spring-security-jwt-cover.jpeg",{"type":19,"value":95507,"toc":98256},[95508,95511,95514,95517,95523,95527,95530,95533,95540,95546,95550,95553,95564,95570,95573,95576,95582,95585,95591,95593,95600,95610,95616,95621,95760,95767,95771,95783,95793,95864,95870,95874,95885,95894,96062,96076,96083,96093,96181,96194,96200,96204,96207,96215,96230,96233,96236,96239,96242,96263,96267,96278,96291,96294,96326,96332,96458,96461,96478,96481,96585,96589,96596,96599,96607,96610,96614,96625,96628,96726,96732,96735,96747,96791,96797,96818,96821,96898,96904,96978,96990,97037,97040,97044,97047,97051,97061,97158,97171,97457,97466,97659,97662,97666,97669,97673,97676,97681,97684,97690,97699,97705,97710,97719,97742,97748,97754,97763,97777,97783,97787,97790,97799,97846,97855,98224,98226,98233,98236,98253],[22,95509,95510],{},"If you perform a quick search on how to secure REST APIs in Spring Boot using JSON Web Tokens you will find a lot of the same results. These results contain a method that involves writing a custom filter chain and pulling in a 3rd party library for encoding and decoding JWTs.",[22,95512,95513],{},"After staring at these convoluted and confusing tutorials I said there has to be an easier way to do this. I did what anyone with direct access to the Spring Security team would do, I asked them for help. They informed me that indeed Spring Security has built-in support for JWTs using oAuth2 Resource Server.",[22,95515,95516],{},"In this tutorial, you are going to learn how to secure your APIs using JSON Web Tokens (JWT) with Spring Security. I’m not saying this approach is easy by any stretch but for me, it made a lot more sense than the alternatives.",[22,95518,95519],{},[677,95520,22245],{"href":95521,"rel":95522},"https://github.com/danvega/jwt",[681],[26,95524,95526],{"id":95525},"application-architecture","Application Architecture",[22,95528,95529],{},"Before we get into writing some code I want to make sure we are all on the same page regarding what we are building. In the example below you have a client application which could be a simple command-line application, a full frontend application written in something like Angular or Vue, or some other service in your system.",[22,95531,95532],{},"This client application will make calls to a server application written in Spring Boot that exposes data via REST API. In the following example, it’s a monolith but the same would apply if you had a distributed architecture. There are currently 3 REST controllers that expose the resources products, orders, and customers.",[22,95534,95535,95536,95539],{},"What you will do is secure all of the resources so that when the client makes a call to the REST API the client will get a ",[4534,95537,95538],{},"401 (Unauthorized)"," which means the client request has not been completed because it lacks valid authentication credentials for the requested resource**.**",[22,95541,95542],{},[653,95543],{"alt":95544,"src":95545},"Application Architecture: 401 Unauthorized","/images/blog/2022/09/09/app-arch-401.png",[636,95547,95549],{"id":95548},"json-web-tokens-jwt","JSON Web Tokens (JWT)",[22,95551,95552],{},"A JSON Web Token is an open method for representing claims securely between two parties. A JWT is a set of claims (JSON property–value pairs) that together make up a JSON object. It consists of three parts:",[915,95554,95555,95558,95561],{},[37,95556,95557],{},"Header: Consists of two properties: { \"alg\": \"HS256\", \"typ\": \"JWT\" }. alg is the algorithm that is used to encrypt the JWT.",[37,95559,95560],{},"Payload: This is where the data to be sent is stored; this data is stored as JSON property–value pairs.",[37,95562,95563],{},"Signature: This is created by encrypting, with the algorithm specified in the header: (i) the base64Url-encoded header, (ii) base64Url-encoded payload, and (iii) a secret (or a private key):",[52,95565,95568],{"className":95566,"code":95567,"language":1727},[1725],"HMACSHA256(base64UrlEncode(header) + \".\" + base64UrlEncode(payload), secret|privateKey)\n",[59,95569,95567],{"__ignoreMap":57},[22,95571,95572],{},"The final JWT consists of three parts. Each is base64Url-encoded and separated from the next by a dot. See the openid.net and jwt.io websites for more details.",[22,95574,95575],{},"You will introduce a new authentication controller that a client can make a request to with their authentication credentials (username + password) and when they are successfully authenticated the service will return a JWT.",[22,95577,95578],{},[653,95579],{"alt":95580,"src":95581},"Application Architecture: JSON Web Token (JWT)","/images/blog/2022/09/09/app-arch-jwt.png",[22,95583,95584],{},"The client will then store the JWT and each subsequent request will pass it via the Authorization header. When the Server application receives the request with the JWT it will verify that it is a valid token and if it is will allow the request to continue.",[22,95586,95587],{},[653,95588],{"alt":95589,"src":95590},"Application Architecture: Request with JSON Web Token (JWT)","/images/blog/2022/09/09/app-arch-with-jwt-200.png",[26,95592,93066],{"id":93065},[22,95594,95595,95596,95599],{},"To get started you are going to head over to ",[677,95597,2903],{"href":40207,"rel":95598},[681]," and create a new project. Fill in the metadata for the project and add the following dependencies:",[915,95601,95602,95604,95607],{},[37,95603,1756],{},[37,95605,95606],{},"oAuth2 Resource Server",[37,95608,95609],{},"Spring Configuration Processor",[22,95611,95612],{},[653,95613],{"alt":95614,"src":95615},"Spring Initializer","/images/blog/2022/09/09/start-spring-io.png",[22,95617,95618,95619],{},"This will generate the following dependencies in your ",[59,95620,1765],{},[52,95622,95624],{"className":1769,"code":95623,"language":1771,"meta":57,"style":57},"\u003Cdependency>\n \u003CgroupId>org.springframework.boot\u003C/groupId>\n \u003CartifactId>spring-boot-starter-web\u003C/artifactId>\n\u003C/dependency>\n\u003Cdependency>\n \u003CgroupId>org.springframework.boot\u003C/groupId>\n \u003CartifactId>spring-boot-starter-oauth2-resource-server\u003C/artifactId>\n\u003C/dependency>\n\u003Cdependency>\n \u003CgroupId>org.springframework.boot\u003C/groupId>\n \u003CartifactId>spring-boot-configuration-processor\u003C/artifactId>\n \u003Coptional>true\u003C/optional>\n\u003C/dependency>\n",[59,95625,95626,95634,95646,95658,95666,95674,95686,95699,95707,95715,95727,95740,95752],{"__ignoreMap":57},[62,95627,95628,95630,95632],{"class":64,"line":65},[62,95629,760],{"class":72},[62,95631,1781],{"class":1780},[62,95633,1784],{"class":72},[62,95635,95636,95638,95640,95642,95644],{"class":64,"line":76},[62,95637,1789],{"class":72},[62,95639,1792],{"class":1780},[62,95641,1795],{"class":72},[62,95643,1792],{"class":1780},[62,95645,1784],{"class":72},[62,95647,95648,95650,95652,95654,95656],{"class":64,"line":82},[62,95649,1789],{"class":72},[62,95651,1806],{"class":1780},[62,95653,1809],{"class":72},[62,95655,1806],{"class":1780},[62,95657,1784],{"class":72},[62,95659,95660,95662,95664],{"class":64,"line":89},[62,95661,1818],{"class":72},[62,95663,1781],{"class":1780},[62,95665,1784],{"class":72},[62,95667,95668,95670,95672],{"class":64,"line":95},[62,95669,760],{"class":72},[62,95671,1781],{"class":1780},[62,95673,1784],{"class":72},[62,95675,95676,95678,95680,95682,95684],{"class":64,"line":101},[62,95677,1789],{"class":72},[62,95679,1792],{"class":1780},[62,95681,1795],{"class":72},[62,95683,1792],{"class":1780},[62,95685,1784],{"class":72},[62,95687,95688,95690,95692,95695,95697],{"class":64,"line":107},[62,95689,1789],{"class":72},[62,95691,1806],{"class":1780},[62,95693,95694],{"class":72},">spring-boot-starter-oauth2-resource-server\u003C/",[62,95696,1806],{"class":1780},[62,95698,1784],{"class":72},[62,95700,95701,95703,95705],{"class":64,"line":113},[62,95702,1818],{"class":72},[62,95704,1781],{"class":1780},[62,95706,1784],{"class":72},[62,95708,95709,95711,95713],{"class":64,"line":129},[62,95710,760],{"class":72},[62,95712,1781],{"class":1780},[62,95714,1784],{"class":72},[62,95716,95717,95719,95721,95723,95725],{"class":64,"line":134},[62,95718,1789],{"class":72},[62,95720,1792],{"class":1780},[62,95722,1795],{"class":72},[62,95724,1792],{"class":1780},[62,95726,1784],{"class":72},[62,95728,95729,95731,95733,95736,95738],{"class":64,"line":156},[62,95730,1789],{"class":72},[62,95732,1806],{"class":1780},[62,95734,95735],{"class":72},">spring-boot-configuration-processor\u003C/",[62,95737,1806],{"class":1780},[62,95739,1784],{"class":72},[62,95741,95742,95744,95746,95748,95750],{"class":64,"line":161},[62,95743,1789],{"class":72},[62,95745,38814],{"class":1780},[62,95747,93421],{"class":72},[62,95749,38814],{"class":1780},[62,95751,1784],{"class":72},[62,95753,95754,95756,95758],{"class":64,"line":167},[62,95755,1818],{"class":72},[62,95757,1781],{"class":1780},[62,95759,1784],{"class":72},[22,95761,95762,95763,95766],{},"I know what you’re thinking, what about Spring Security? If you dig into the ",[59,95764,95765],{},"spring-boot-starter-oauth2-resource-server"," you will find that it includes the Spring Security Starter that contains everything you need.",[26,95768,95770],{"id":95769},"rest-api","REST API",[22,95772,95773,95774,48324,95776,95778,95779,95782],{},"The first thing you will need to do is to create a REST API that you want to be secured. For demo purposes and to keep it simple create ",[59,95775,78739],{},[59,95777,48327],{}," package with a single method that returns a String. Request Mapping handler methods can accept a range of arguments, one of which is ",[59,95780,95781],{},"java.security.Principal",". This will allow you to print out the username of the currently authenticated user.",[22,95784,95785,95786,95789,95790,95792],{},"Spring Security takes a secure-by-default approach to security. This means that if you start your application and try to visit ",[677,95787,16942],{"href":16942,"rel":95788},[681]," you will be redirected to a login page. If you want to log in you can enter the username of ",[59,95791,2502],{}," and the password is generated and should be listed in the console output.",[52,95794,95796],{"className":54,"code":95795,"language":56,"meta":57,"style":57},"@RestController\npublic class HomeController {\n\n @GetMapping\n public String home(Principal principal) {\n return \"Hello, \" + principal.getName();\n }\n\n}\n",[59,95797,95798,95804,95814,95818,95824,95838,95852,95856,95860],{"__ignoreMap":57},[62,95799,95800,95802],{"class":64,"line":65},[62,95801,942],{"class":72},[62,95803,2342],{"class":68},[62,95805,95806,95808,95810,95812],{"class":64,"line":76},[62,95807,116],{"class":68},[62,95809,119],{"class":68},[62,95811,25636],{"class":122},[62,95813,126],{"class":72},[62,95815,95816],{"class":64,"line":82},[62,95817,79],{"emptyLinePlaceholder":13},[62,95819,95820,95822],{"class":64,"line":89},[62,95821,2143],{"class":72},[62,95823,47319],{"class":68},[62,95825,95826,95828,95830,95832,95834,95836],{"class":64,"line":95},[62,95827,194],{"class":68},[62,95829,2469],{"class":72},[62,95831,18647],{"class":122},[62,95833,78791],{"class":72},[62,95835,78794],{"class":889},[62,95837,768],{"class":72},[62,95839,95840,95842,95844,95846,95848,95850],{"class":64,"line":101},[62,95841,360],{"class":68},[62,95843,78809],{"class":1675},[62,95845,4507],{"class":68},[62,95847,78814],{"class":72},[62,95849,12678],{"class":122},[62,95851,822],{"class":72},[62,95853,95854],{"class":64,"line":107},[62,95855,223],{"class":72},[62,95857,95858],{"class":64,"line":113},[62,95859,79],{"emptyLinePlaceholder":13},[62,95861,95862],{"class":64,"line":129},[62,95863,379],{"class":72},[22,95865,95866],{},[653,95867],{"alt":95868,"src":95869},"Spring Security Login","/images/blog/2022/09/09/please-sign-in.png",[26,95871,95873],{"id":95872},"spring-security-configuration","Spring Security Configuration",[22,95875,95876,95877,95879,95880,2755],{},"The default security configuration is enough to get you up and running but you will need to provide your own to fit the needs of your application. In the past, you would extend the ",[59,95878,91440],{}," but that has been deprecated in Spring Security 5.7.x. If you’re interested in learning more about this change you can check out ",[677,95881,95884],{"href":95882,"rel":95883},"https://youtu.be/s4X4SJv2RrU",[681],"this tutorial",[22,95886,95887,95888,95890,95891,95893],{},"To get started create a new class in the ",[59,95889,48865],{}," package called ",[59,95892,15407],{},". This class will have the following configuration:",[52,95895,95897],{"className":54,"code":95896,"language":56,"meta":57,"style":57},"@Configuration\n@EnableWebSecurity\npublic class SecurityConfig {\n\n @Bean\n public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n return http\n .csrf(csrf -> csrf.disable()) // (1)\n .authorizeRequests( auth -> auth\n .anyRequest().authenticated() // (2)\n )\n .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) // (3)\n .httpBasic(Customizer.withDefaults()) // (4)\n .build();\n }\n\n}\n",[59,95898,95899,95905,95911,95921,95925,95931,95949,95955,95974,95986,96001,96006,96026,96042,96050,96054,96058],{"__ignoreMap":57},[62,95900,95901,95903],{"class":64,"line":65},[62,95902,942],{"class":72},[62,95904,11133],{"class":68},[62,95906,95907,95909],{"class":64,"line":76},[62,95908,942],{"class":72},[62,95910,11461],{"class":68},[62,95912,95913,95915,95917,95919],{"class":64,"line":82},[62,95914,116],{"class":68},[62,95916,119],{"class":68},[62,95918,11470],{"class":122},[62,95920,126],{"class":72},[62,95922,95923],{"class":64,"line":89},[62,95924,79],{"emptyLinePlaceholder":13},[62,95926,95927,95929],{"class":64,"line":95},[62,95928,2143],{"class":72},[62,95930,2146],{"class":68},[62,95932,95933,95935,95937,95939,95941,95943,95945,95947],{"class":64,"line":101},[62,95934,194],{"class":68},[62,95936,15452],{"class":72},[62,95938,11490],{"class":122},[62,95940,11493],{"class":72},[62,95942,11496],{"class":889},[62,95944,5024],{"class":72},[62,95946,11501],{"class":68},[62,95948,11504],{"class":72},[62,95950,95951,95953],{"class":64,"line":107},[62,95952,360],{"class":68},[62,95954,11511],{"class":72},[62,95956,95957,95959,95961,95963,95965,95967,95969,95971],{"class":64,"line":113},[62,95958,2418],{"class":72},[62,95960,11558],{"class":122},[62,95962,81264],{"class":72},[62,95964,800],{"class":68},[62,95966,81269],{"class":72},[62,95968,81306],{"class":122},[62,95970,64747],{"class":72},[62,95972,95973],{"class":85},"// (1)\n",[62,95975,95976,95978,95980,95982,95984],{"class":64,"line":129},[62,95977,2418],{"class":72},[62,95979,84626],{"class":122},[62,95981,81226],{"class":72},[62,95983,800],{"class":68},[62,95985,15483],{"class":72},[62,95987,95988,95990,95992,95994,95996,95998],{"class":64,"line":134},[62,95989,6410],{"class":72},[62,95991,11544],{"class":122},[62,95993,3229],{"class":72},[62,95995,15518],{"class":122},[62,95997,5398],{"class":72},[62,95999,96000],{"class":85},"// (2)\n",[62,96002,96003],{"class":64,"line":156},[62,96004,96005],{"class":72}," )\n",[62,96007,96008,96010,96012,96014,96016,96018,96020,96023],{"class":64,"line":161},[62,96009,2418],{"class":72},[62,96011,81094],{"class":122},[62,96013,81097],{"class":72},[62,96015,800],{"class":68},[62,96017,79130],{"class":72},[62,96019,81104],{"class":122},[62,96021,96022],{"class":72},"(SessionCreationPolicy.STATELESS)) ",[62,96024,96025],{"class":85},"// (3)\n",[62,96027,96028,96030,96032,96035,96037,96039],{"class":64,"line":167},[62,96029,2418],{"class":72},[62,96031,81114],{"class":122},[62,96033,96034],{"class":72},"(Customizer.",[62,96036,79586],{"class":122},[62,96038,64747],{"class":72},[62,96040,96041],{"class":85},"// (4)\n",[62,96043,96044,96046,96048],{"class":64,"line":173},[62,96045,2418],{"class":72},[62,96047,2189],{"class":122},[62,96049,822],{"class":72},[62,96051,96052],{"class":64,"line":179},[62,96053,223],{"class":72},[62,96055,96056],{"class":64,"line":185},[62,96057,79],{"emptyLinePlaceholder":13},[62,96059,96060],{"class":64,"line":191},[62,96061,379],{"class":72},[34,96063,96064,96067,96070,96073],{},[37,96065,96066],{},"Disable Cross-Site Request Forgery (CSRF)",[37,96068,96069],{},"The user should be authenticated for any request in the application.",[37,96071,96072],{},"Spring Security will never create an HttpSession and it will never use it to obtain the Security Context.",[37,96074,96075],{},"Spring Security’s HTTP Basic Authentication support is enabled by default. However, as soon as any servlet-based configuration is provided, HTTP Basic must be explicitly provided.",[96077,96078,96080],"warning",{"title":96079},"WARNING",[22,96081,96082],{},"Never disable CSRF protection while leaving session management enabled! Doing so will open you up to a Cross-Site Request Forgery attack.",[22,96084,96085,96086,96089,96090,96092],{},"Now that you have a custom security configuration in place you need a user that isn’t the default one provided by Spring Boot. The following configuration will create an in-memory user using the ",[59,96087,96088],{},"NoOpPasswordEncoder"," This is a password encoder that does nothing and is useful for testing but should ",[646,96091,69066],{}," be used in production.",[52,96094,96096],{"className":54,"code":96095,"language":56,"meta":57,"style":57},"@Bean\npublic InMemoryUserDetailsManager users() {\n return new InMemoryUserDetailsManager(\n User.withUsername(\"dvega\")\n .password(\"{noop}password\")\n .authorities(\"read\")\n .build()\n );\n}\n",[59,96097,96098,96104,96115,96125,96138,96151,96165,96173,96177],{"__ignoreMap":57},[62,96099,96100,96102],{"class":64,"line":65},[62,96101,942],{"class":72},[62,96103,2146],{"class":68},[62,96105,96106,96108,96110,96113],{"class":64,"line":76},[62,96107,116],{"class":68},[62,96109,15666],{"class":72},[62,96111,96112],{"class":122},"users",[62,96114,206],{"class":72},[62,96116,96117,96119,96121,96123],{"class":64,"line":82},[62,96118,2599],{"class":68},[62,96120,466],{"class":68},[62,96122,15736],{"class":122},[62,96124,3301],{"class":72},[62,96126,96127,96130,96132,96134,96136],{"class":64,"line":89},[62,96128,96129],{"class":72}," User.",[62,96131,15684],{"class":122},[62,96133,2109],{"class":72},[62,96135,46121],{"class":1675},[62,96137,2212],{"class":72},[62,96139,96140,96142,96144,96146,96149],{"class":64,"line":95},[62,96141,2217],{"class":72},[62,96143,15698],{"class":122},[62,96145,2109],{"class":72},[62,96147,96148],{"class":1675},"\"{noop}password\"",[62,96150,2212],{"class":72},[62,96152,96153,96155,96158,96160,96163],{"class":64,"line":101},[62,96154,2217],{"class":72},[62,96156,96157],{"class":122},"authorities",[62,96159,2109],{"class":72},[62,96161,96162],{"class":1675},"\"read\"",[62,96164,2212],{"class":72},[62,96166,96167,96169,96171],{"class":64,"line":107},[62,96168,2217],{"class":72},[62,96170,2189],{"class":122},[62,96172,2223],{"class":72},[62,96174,96175],{"class":64,"line":113},[62,96176,5969],{"class":72},[62,96178,96179],{"class":64,"line":129},[62,96180,379],{"class":72},[22,96182,96183,96184,96187,96188,96191,96192,2755],{},"With the new user configured you should be able to restart the application and visit ",[677,96185,16942],{"href":16942,"rel":96186},[681],". You will be presented with a dialog asking for a username and password and if everything works you should be able to log in with ",[59,96189,96190],{},"dvega"," + ",[59,96193,15698],{},[22,96195,96196],{},[653,96197],{"alt":96198,"src":96199},"Spring Security HTTP Basic","/images/blog/2022/09/09/http-basic-auth.png",[26,96201,96203],{"id":96202},"oauth-20-resource-server","OAuth 2.0 Resource Server",[22,96205,96206],{},"If you watched my previous tutorial everything you have done so far should be familiar but I know that’s not what you’re here for. Spring Security supports protecting endpoints using two forms of OAuth 2.0 Bearer Tokens:",[915,96208,96209,96212],{},[37,96210,96211],{},"JWT",[37,96213,96214],{},"Opaque Tokens",[22,96216,96217,96218,96223,96224,96229],{},"This is handy in circumstances where an application has delegated its authority management to an ",[677,96219,96222],{"href":96220,"rel":96221},"https://tools.ietf.org/html/rfc6749",[681],"authorization server"," (for example, Okta or ",[677,96225,96228],{"href":96226,"rel":96227},"https://spring.io/projects/spring-authorization-server",[681],"Spring Authorization Server","). This authorization server can be consulted by resource servers to authorize requests.",[22,96231,96232],{},"In this tutorial, you will use self-signed JWTs which will eliminate the need to introduce an authorization server. While this works for this example, your application requirements might be different so when is it no longer acceptable to use self-signed JWTs? This is a question I also posed to the Spring Security team and got some really great answers.",[29685,96234,96235],{},"\nWhen you reach the point where the trade-offs for self-signed JWTs are not acceptable. An example might be the moment you want to introduce refresh tokens.\n",[29685,96237,96238],{},"\nI'd add that a distinct authorization server makes more sense when you have more than one service or you want to be able to harden security (isolating something as critical as authentication provides value because the attack surface is reduced)\n",[22,96240,96241],{},"We could spend a lot of time talking about Authorization and Resource servers. To keep this tutorial on the topic I will leave you some really great resources that I would recommend you go through them when you have some time.",[915,96243,96244,96251,96258],{},[37,96245,96246],{},[677,96247,96250],{"href":96248,"rel":96249},"https://docs.spring.io/spring-security/reference/servlet/oauth2/resource-server/index.html",[681],"OAuth2 Resource Server",[37,96252,96253],{},[677,96254,96257],{"href":96255,"rel":96256},"https://docs.spring.io/spring-security/reference/servlet/oauth2/resource-server/jwt.html",[681],"OAuth2 Resource Server JWT",[37,96259,96260],{},[677,96261,96228],{"href":96226,"rel":96262},[681],[636,96264,96266],{"id":96265},"oauth-2-resource-server-configuration","OAuth 2 Resource Server Configuration",[22,96268,96269,96270,96273,96274,96277],{},"Now that you know what a resource server is and what it is used for you need to configure one. You can do so in your security config by setting ",[59,96271,96272],{},".oauth2ResourceServer()",". This could be a custom resource server configurer or you can use the ",[59,96275,96276],{},"OAuth2ResourceServerConfigurer"," class provided by Spring.",[22,96279,3521,96280,96282,96283,96286,96287,96290],{},[59,96281,96276],{}," is an ",[59,96284,96285],{},"AbstractHttpConfigurer"," for OAuth 2.0 Resource Server Support. By default, this wires a ",[59,96288,96289],{},"BearerTokenAuthenticationFilter",", which can be used to parse the request for bearer tokens and make an authentication attempt.",[22,96292,96293],{},"This configuration class has the following options available:",[915,96295,96296,96302,96308,96314,96320],{},[37,96297,96298,96301],{},[59,96299,96300],{},"accessDeniedHandler"," - Customizes how access denied errors are handled.",[37,96303,96304,96307],{},[59,96305,96306],{},"authenticationEntryPoint"," - Customizes how authentication failures are handled.",[37,96309,96310,96313],{},[59,96311,96312],{},"bearerTokenResolver"," - Customizes how to resolve a bearer token from the request.",[37,96315,96316,96319],{},[59,96317,96318],{},"jwt","(Customizer) - Enables Jwt-encoded bearer token support.",[37,96321,96322,96325],{},[59,96323,96324],{},"opaqueToken","(Customizer) - Enables opaque bearer token support.",[22,96327,96328,96329],{},"You are going to use JWT so the configuration option can use a method reference and will look like ",[59,96330,96331],{},"OAuth2ResourceServerConfigurer::jwt",[52,96333,96335],{"className":54,"code":96334,"language":56,"meta":57,"style":57},"@Bean\npublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {\n return http\n .csrf(csrf -> csrf.disable())\n .authorizeRequests( auth -> auth\n .anyRequest().authenticated()\n )\n .oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt)\n .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))\n .httpBasic(withDefaults())\n .build();\n}\n",[59,96336,96337,96343,96353,96359,96375,96387,96399,96403,96418,96434,96446,96454],{"__ignoreMap":57},[62,96338,96339,96341],{"class":64,"line":65},[62,96340,942],{"class":72},[62,96342,2146],{"class":68},[62,96344,96345,96347,96349,96351],{"class":64,"line":76},[62,96346,116],{"class":68},[62,96348,15452],{"class":72},[62,96350,11490],{"class":122},[62,96352,81038],{"class":72},[62,96354,96355,96357],{"class":64,"line":82},[62,96356,2599],{"class":68},[62,96358,11511],{"class":72},[62,96360,96361,96363,96365,96367,96369,96371,96373],{"class":64,"line":89},[62,96362,2610],{"class":72},[62,96364,11558],{"class":122},[62,96366,81264],{"class":72},[62,96368,800],{"class":68},[62,96370,81269],{"class":72},[62,96372,81306],{"class":122},[62,96374,4460],{"class":72},[62,96376,96377,96379,96381,96383,96385],{"class":64,"line":95},[62,96378,2610],{"class":72},[62,96380,84626],{"class":122},[62,96382,81226],{"class":72},[62,96384,800],{"class":68},[62,96386,15483],{"class":72},[62,96388,96389,96391,96393,96395,96397],{"class":64,"line":101},[62,96390,2217],{"class":72},[62,96392,11544],{"class":122},[62,96394,3229],{"class":72},[62,96396,15518],{"class":122},[62,96398,2223],{"class":72},[62,96400,96401],{"class":64,"line":107},[62,96402,14430],{"class":72},[62,96404,96405,96407,96410,96413,96415],{"class":64,"line":113},[62,96406,2610],{"class":72},[62,96408,96409],{"class":122},"oauth2ResourceServer",[62,96411,96412],{"class":72},"(OAuth2ResourceServerConfigurer",[62,96414,4451],{"class":68},[62,96416,96417],{"class":72},"jwt)\n",[62,96419,96420,96422,96424,96426,96428,96430,96432],{"class":64,"line":129},[62,96421,2610],{"class":72},[62,96423,81094],{"class":122},[62,96425,81097],{"class":72},[62,96427,800],{"class":68},[62,96429,79130],{"class":72},[62,96431,81104],{"class":122},[62,96433,81107],{"class":72},[62,96435,96436,96438,96440,96442,96444],{"class":64,"line":134},[62,96437,2610],{"class":72},[62,96439,81114],{"class":122},[62,96441,2109],{"class":72},[62,96443,79586],{"class":122},[62,96445,4460],{"class":72},[62,96447,96448,96450,96452],{"class":64,"line":156},[62,96449,2610],{"class":72},[62,96451,2189],{"class":122},[62,96453,822],{"class":72},[62,96455,96456],{"class":64,"line":161},[62,96457,379],{"class":72},[22,96459,96460],{},"When you use the JWT customizer you need to provide one of the following:",[915,96462,96463,96469,96475],{},[37,96464,96465,96466],{},"Supply a Jwk Set Uri via ",[59,96467,96468],{},"OAuth2ResourceServerConfigurer.JwtConfigurer.jwkSetUri",[37,96470,96471,96472],{},"Supply a JwtDecoder instance via ",[59,96473,96474],{},"OAuth2ResourceServerConfigurer.JwtConfigurer.decoder",[37,96476,96477],{},"Expose a JwtDecoder bean.",[22,96479,96480],{},"If you try and run the app without providing one of the options above you will receive the following error:",[52,96482,96484],{"className":1663,"code":96483,"language":1665,"meta":57,"style":57},"Description:\n\nParameter 0 of method setFilterChains in\norg.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration\nrequired a bean of type 'org.springframework.security.oauth2.jwt.JwtDecoder' that could not be found.\n\nAction:\n\nConsider defining a bean of type 'org.springframework.security.oauth2.jwt.JwtDecoder'\nin your configuration.\n",[59,96485,96486,96491,96495,96512,96517,96548,96552,96557,96561,96580],{"__ignoreMap":57},[62,96487,96488],{"class":64,"line":65},[62,96489,96490],{"class":122},"Description:\n",[62,96492,96493],{"class":64,"line":76},[62,96494,79],{"emptyLinePlaceholder":13},[62,96496,96497,96500,96502,96504,96506,96509],{"class":64,"line":82},[62,96498,96499],{"class":122},"Parameter",[62,96501,150],{"class":149},[62,96503,59066],{"class":1675},[62,96505,16077],{"class":1675},[62,96507,96508],{"class":1675}," setFilterChains",[62,96510,96511],{"class":1675}," in\n",[62,96513,96514],{"class":64,"line":89},[62,96515,96516],{"class":122},"org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration\n",[62,96518,96519,96522,96524,96527,96529,96531,96534,96536,96539,96542,96545],{"class":64,"line":95},[62,96520,96521],{"class":122},"required",[62,96523,28870],{"class":1675},[62,96525,96526],{"class":1675}," bean",[62,96528,59066],{"class":1675},[62,96530,16099],{"class":1675},[62,96532,96533],{"class":1675}," 'org.springframework.security.oauth2.jwt.JwtDecoder'",[62,96535,50417],{"class":1675},[62,96537,96538],{"class":1675}," could",[62,96540,96541],{"class":1675}," not",[62,96543,96544],{"class":1675}," be",[62,96546,96547],{"class":1675}," found.\n",[62,96549,96550],{"class":64,"line":101},[62,96551,79],{"emptyLinePlaceholder":13},[62,96553,96554],{"class":64,"line":107},[62,96555,96556],{"class":122},"Action:\n",[62,96558,96559],{"class":64,"line":113},[62,96560,79],{"emptyLinePlaceholder":13},[62,96562,96563,96566,96569,96571,96573,96575,96577],{"class":64,"line":129},[62,96564,96565],{"class":122},"Consider",[62,96567,96568],{"class":1675}," defining",[62,96570,28870],{"class":1675},[62,96572,96526],{"class":1675},[62,96574,59066],{"class":1675},[62,96576,16099],{"class":1675},[62,96578,96579],{"class":1675}," 'org.springframework.security.oauth2.jwt.JwtDecoder'\n",[62,96581,96582],{"class":64,"line":134},[62,96583,96584],{"class":72},"in your configuration.\n",[636,96586,96588],{"id":96587},"signing-json-web-tokens","Signing JSON Web Tokens",[22,96590,96591,96592,96595],{},"The next step is to create a new ",[59,96593,96594],{},"JwtDecoder"," bean but I think we need to talk about what we are going to do here. As you learned earlier there are 3 parts to the JWT, the header, payload, and signature. The signature is created using by encrypting the header + payload and a secret (or private key).",[22,96597,96598],{},"A JWT can be encrypted using either a symmetric key (shared secret) or asymmetric keys (the private key of a private-public pair).",[915,96600,96601,96604],{},[37,96602,96603],{},"Symmetric key: The same key is used for both encryption (when the JWT is created) and decryption (MobileTogether Server uses the key to verify the JWT). The symmetric key—also known as the shared secret—is stored as a setting in MobileTogether Server. See Symmetric Key: Shared Secret for details of working with symmetric keys.",[37,96605,96606],{},"Asymmetric keys: Different keys are used for encryption (private key) and decryption (public key). The public key is stored as a setting in MobileTogether Server so that the JWT can be verified. For information about using asymmetric encryption for JWTs, see Asymmetric Keys: Public Key.",[22,96608,96609],{},"There are pros/cons to each but it is generally recommended that you use Asymmetric keys so that is the approach you will take here.",[636,96611,96613],{"id":96612},"rsa-public-private-keys","RSA Public & Private Keys",[22,96615,96616,96617,96620,96621,96624],{},"You are going to create a public / private key pair. This is something that you can do via code but I think it might make more sense if you do it manually here. I would create these in a new folder under ",[59,96618,96619],{},"/src/main/rescurces/certs",". I am going to use ",[646,96622,96623],{},"OpenSSL"," which is installed by default on macOS but you should be able to install it on whatever OS you’re using.",[22,96626,96627],{},"Normally you could get away with running the first 2 commands. The reason for the 3rd command is that the private key needs to be in PEM-encoded PKCS#8 format. Switch to that certs directory and run each of the following commands separately.",[52,96629,96631],{"className":1663,"code":96630,"language":1665,"meta":57,"style":57},"# create rsa key pair\nopenssl genrsa -out keypair.pem 2048\n\n# extract public key\nopenssl rsa -in keypair.pem -pubout -out public.pem\n\n# create private key in PKCS#8 format\nopenssl pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in keypair.pem -out private.pem\n",[59,96632,96633,96638,96655,96659,96664,96684,96688,96693],{"__ignoreMap":57},[62,96634,96635],{"class":64,"line":65},[62,96636,96637],{"class":85},"# create rsa key pair\n",[62,96639,96640,96643,96646,96649,96652],{"class":64,"line":76},[62,96641,96642],{"class":122},"openssl",[62,96644,96645],{"class":1675}," genrsa",[62,96647,96648],{"class":149}," -out",[62,96650,96651],{"class":1675}," keypair.pem",[62,96653,96654],{"class":149}," 2048\n",[62,96656,96657],{"class":64,"line":82},[62,96658,79],{"emptyLinePlaceholder":13},[62,96660,96661],{"class":64,"line":89},[62,96662,96663],{"class":85},"# extract public key\n",[62,96665,96666,96668,96671,96674,96676,96679,96681],{"class":64,"line":95},[62,96667,96642],{"class":122},[62,96669,96670],{"class":1675}," rsa",[62,96672,96673],{"class":149}," -in",[62,96675,96651],{"class":1675},[62,96677,96678],{"class":149}," -pubout",[62,96680,96648],{"class":149},[62,96682,96683],{"class":1675}," public.pem\n",[62,96685,96686],{"class":64,"line":101},[62,96687,79],{"emptyLinePlaceholder":13},[62,96689,96690],{"class":64,"line":107},[62,96691,96692],{"class":85},"# create private key in PKCS#8 format\n",[62,96694,96695,96697,96700,96703,96706,96709,96712,96714,96717,96719,96721,96723],{"class":64,"line":113},[62,96696,96642],{"class":122},[62,96698,96699],{"class":1675}," pkcs8",[62,96701,96702],{"class":149}," -topk8",[62,96704,96705],{"class":149}," -inform",[62,96707,96708],{"class":1675}," PEM",[62,96710,96711],{"class":149}," -outform",[62,96713,96708],{"class":1675},[62,96715,96716],{"class":149}," -nocrypt",[62,96718,96673],{"class":149},[62,96720,96651],{"class":1675},[62,96722,96648],{"class":149},[62,96724,96725],{"class":1675}," private.pem\n",[22,96727,96728,96729],{},"If everything runs without error and you have both a public and private key you can delete ",[59,96730,96731],{},"keypair.pem",[636,96733,96594],{"id":96734},"jwtdecoder",[22,96736,96737,96738,96740,96741,95890,96743,96746],{},"With the public and private keys in place, you can return your focus to defining a ",[59,96739,96594],{}," bean. First, create a new record class in the ",[59,96742,48865],{},[59,96744,96745],{},"RsaKeyProperties"," This will be used to externalize both the public and private key.",[52,96748,96750],{"className":54,"code":96749,"language":56,"meta":57,"style":57},"@ConfigurationProperties(prefix = \"rsa\")\npublic record RsaKeyProperties(RSAPublicKey publicKey, RSAPrivateKey privateKey) {\n\n}\n",[59,96751,96752,96771,96783,96787],{"__ignoreMap":57},[62,96753,96754,96756,96759,96761,96764,96766,96769],{"class":64,"line":65},[62,96755,942],{"class":72},[62,96757,96758],{"class":68},"ConfigurationProperties",[62,96760,2109],{"class":72},[62,96762,96763],{"class":149},"prefix",[62,96765,2556],{"class":68},[62,96767,96768],{"class":1675}," \"rsa\"",[62,96770,2212],{"class":72},[62,96772,96773,96775,96777,96780],{"class":64,"line":76},[62,96774,116],{"class":68},[62,96776,2996],{"class":68},[62,96778,96779],{"class":122}," RsaKeyProperties",[62,96781,96782],{"class":72},"(RSAPublicKey publicKey, RSAPrivateKey privateKey) {\n",[62,96784,96785],{"class":64,"line":82},[62,96786,79],{"emptyLinePlaceholder":13},[62,96788,96789],{"class":64,"line":89},[62,96790,379],{"class":72},[22,96792,96793,96794,96796],{},"If you run a build and open up ",[59,96795,1265],{}," you should get IntelliSense for the private and public key configuration. Add the following configuration so your application can find your keys.",[52,96798,96800],{"className":1269,"code":96799,"language":1271,"meta":57,"style":57},"rsa.private-key=classpath:certs/private.pem\nrsa.public-key=classpath:certs/public.pem\n",[59,96801,96802,96810],{"__ignoreMap":57},[62,96803,96804,96807],{"class":64,"line":65},[62,96805,96806],{"class":68},"rsa.private-key",[62,96808,96809],{"class":72},"=classpath:certs/private.pem\n",[62,96811,96812,96815],{"class":64,"line":76},[62,96813,96814],{"class":68},"rsa.public-key",[62,96816,96817],{"class":72},"=classpath:certs/public.pem\n",[22,96819,96820],{},"Next you need to enable configuration properties on your main class:",[52,96822,96824],{"className":54,"code":96823,"language":56,"meta":57,"style":57},"@SpringBootApplication\n@EnableConfigurationProperties(RsaKeyProperties.class)\npublic class JwtDemoApplication {\n\n public static void main(String[] args) {\n SpringApplication.run(JwtDemoApplication.class, args);\n }\n\n}\n",[59,96825,96826,96832,96842,96853,96857,96877,96886,96890,96894],{"__ignoreMap":57},[62,96827,96828,96830],{"class":64,"line":65},[62,96829,942],{"class":72},[62,96831,2079],{"class":68},[62,96833,96834,96836,96839],{"class":64,"line":76},[62,96835,942],{"class":72},[62,96837,96838],{"class":68},"EnableConfigurationProperties",[62,96840,96841],{"class":72},"(RsaKeyProperties.class)\n",[62,96843,96844,96846,96848,96851],{"class":64,"line":82},[62,96845,116],{"class":68},[62,96847,119],{"class":68},[62,96849,96850],{"class":122}," JwtDemoApplication",[62,96852,126],{"class":72},[62,96854,96855],{"class":64,"line":89},[62,96856,79],{"emptyLinePlaceholder":13},[62,96858,96859,96861,96863,96865,96867,96869,96871,96873,96875],{"class":64,"line":95},[62,96860,194],{"class":68},[62,96862,2101],{"class":68},[62,96864,200],{"class":68},[62,96866,2106],{"class":122},[62,96868,2109],{"class":72},[62,96870,973],{"class":68},[62,96872,2114],{"class":72},[62,96874,2117],{"class":889},[62,96876,768],{"class":72},[62,96878,96879,96881,96883],{"class":64,"line":101},[62,96880,2124],{"class":72},[62,96882,2127],{"class":122},[62,96884,96885],{"class":72},"(JwtDemoApplication.class, args);\n",[62,96887,96888],{"class":64,"line":107},[62,96889,223],{"class":72},[62,96891,96892],{"class":64,"line":113},[62,96893,79],{"emptyLinePlaceholder":13},[62,96895,96896],{"class":64,"line":129},[62,96897,379],{"class":72},[22,96899,96900,96901,96903],{},"Back in ",[59,96902,15407],{},", you can get an instance of that autowired in for you:",[52,96905,96907],{"className":54,"code":96906,"language":56,"meta":57,"style":57},"@Configuration\n@EnableWebSecurity\npublic class SecurityConfig {\n\n private final RsaKeyProperties rsaKeys;\n\n public SecurityConfig(RsaKeyProperties rsaKeys) {\n this.rsaKeys = rsaKeys;\n }\n",[59,96908,96909,96915,96921,96931,96935,96944,96948,96962,96974],{"__ignoreMap":57},[62,96910,96911,96913],{"class":64,"line":65},[62,96912,942],{"class":72},[62,96914,11133],{"class":68},[62,96916,96917,96919],{"class":64,"line":76},[62,96918,942],{"class":72},[62,96920,11461],{"class":68},[62,96922,96923,96925,96927,96929],{"class":64,"line":82},[62,96924,116],{"class":68},[62,96926,119],{"class":68},[62,96928,11470],{"class":122},[62,96930,126],{"class":72},[62,96932,96933],{"class":64,"line":89},[62,96934,79],{"emptyLinePlaceholder":13},[62,96936,96937,96939,96941],{"class":64,"line":95},[62,96938,137],{"class":68},[62,96940,458],{"class":68},[62,96942,96943],{"class":72}," RsaKeyProperties rsaKeys;\n",[62,96945,96946],{"class":64,"line":101},[62,96947,79],{"emptyLinePlaceholder":13},[62,96949,96950,96952,96954,96957,96960],{"class":64,"line":107},[62,96951,194],{"class":68},[62,96953,11470],{"class":122},[62,96955,96956],{"class":72},"(RsaKeyProperties ",[62,96958,96959],{"class":889},"rsaKeys",[62,96961,768],{"class":72},[62,96963,96964,96966,96969,96971],{"class":64,"line":113},[62,96965,2405],{"class":149},[62,96967,96968],{"class":72},".rsaKeys ",[62,96970,146],{"class":68},[62,96972,96973],{"class":72}," rsaKeys;\n",[62,96975,96976],{"class":64,"line":129},[62,96977,223],{"class":72},[22,96979,96980,96981,96983,96984,96989],{},"Now you can create a ",[59,96982,96594],{}," using the public key. This is something you would normally need to bring in a third-party library for but you won’t need to. One of the dependencies that the resource server brings in for you is ``spring-security-oauth2-jose` which contains a library called ",[677,96985,96988],{"href":96986,"rel":96987},"https://connect2id.com/products/nimbus-jose-jwt",[681],"Nimbus Jose JWT",". You can return a Nimbus JWT Decoder using the public key you just created.",[52,96991,96993],{"className":54,"code":96992,"language":56,"meta":57,"style":57},"@Bean\nJwtDecoder jwtDecoder() {\n return NimbusJwtDecoder.withPublicKey(rsaKeys.publicKey()).build();\n}\n",[59,96994,96995,97001,97011,97033],{"__ignoreMap":57},[62,96996,96997,96999],{"class":64,"line":65},[62,96998,942],{"class":72},[62,97000,2146],{"class":68},[62,97002,97003,97006,97009],{"class":64,"line":76},[62,97004,97005],{"class":72},"JwtDecoder ",[62,97007,97008],{"class":122},"jwtDecoder",[62,97010,206],{"class":72},[62,97012,97013,97015,97018,97021,97024,97027,97029,97031],{"class":64,"line":82},[62,97014,2599],{"class":68},[62,97016,97017],{"class":72}," NimbusJwtDecoder.",[62,97019,97020],{"class":122},"withPublicKey",[62,97022,97023],{"class":72},"(rsaKeys.",[62,97025,97026],{"class":122},"publicKey",[62,97028,11784],{"class":72},[62,97030,2189],{"class":122},[62,97032,822],{"class":72},[62,97034,97035],{"class":64,"line":89},[62,97036,379],{"class":72},[22,97038,97039],{},"At this point, you should be able to run the application without any errors.",[26,97041,97043],{"id":97042},"auth-controller-token-service","Auth Controller & Token Service",[22,97045,97046],{},"You have the keys in place and you have defined a decoder which is a way to decipher the JWT. If you remember back to our architecture diagrams earlier the user will need to log in with their username and password. If they pass authentication you will generate a new JSON Web Token and send it back in the response.",[22,97048,97049],{},[653,97050],{"alt":95580,"src":95581},[22,97052,97053,97054,97057,97058,97060],{},"To do this you first need to create a bean of type ",[59,97055,97056],{},"JwtEncoder"," and you can do this in the ",[59,97059,15407],{},". The encoder will be used to encode the signature we learned about earlier into a token and sign it using our private key.",[52,97062,97064],{"className":54,"code":97063,"language":56,"meta":57,"style":57},"@Bean\nJwtEncoder jwtEncoder() {\n JWK jwk = new RSAKey.Builder(rsaKeys.publicKey()).privateKey(rsaKeys.privateKey()).build();\n JWKSource\u003CSecurityContext> jwks = new ImmutableJWKSet\u003C>(new JWKSet(jwk));\n return new NimbusJwtEncoder(jwks);\n}\n",[59,97065,97066,97072,97082,97116,97142,97154],{"__ignoreMap":57},[62,97067,97068,97070],{"class":64,"line":65},[62,97069,942],{"class":72},[62,97071,2146],{"class":68},[62,97073,97074,97077,97080],{"class":64,"line":76},[62,97075,97076],{"class":72},"JwtEncoder ",[62,97078,97079],{"class":122},"jwtEncoder",[62,97081,206],{"class":72},[62,97083,97084,97087,97089,97091,97094,97097,97099,97101,97103,97106,97108,97110,97112,97114],{"class":64,"line":82},[62,97085,97086],{"class":72}," JWK jwk ",[62,97088,146],{"class":68},[62,97090,466],{"class":68},[62,97092,97093],{"class":72}," RSAKey.",[62,97095,97096],{"class":122},"Builder",[62,97098,97023],{"class":72},[62,97100,97026],{"class":122},[62,97102,11784],{"class":72},[62,97104,97105],{"class":122},"privateKey",[62,97107,97023],{"class":72},[62,97109,97105],{"class":122},[62,97111,11784],{"class":72},[62,97113,2189],{"class":122},[62,97115,822],{"class":72},[62,97117,97118,97121,97124,97127,97129,97131,97134,97136,97139],{"class":64,"line":89},[62,97119,97120],{"class":72}," JWKSource\u003C",[62,97122,97123],{"class":68},"SecurityContext",[62,97125,97126],{"class":72},"> jwks ",[62,97128,146],{"class":68},[62,97130,466],{"class":68},[62,97132,97133],{"class":72}," ImmutableJWKSet\u003C>(",[62,97135,2426],{"class":68},[62,97137,97138],{"class":122}," JWKSet",[62,97140,97141],{"class":72},"(jwk));\n",[62,97143,97144,97146,97148,97151],{"class":64,"line":95},[62,97145,2599],{"class":68},[62,97147,466],{"class":68},[62,97149,97150],{"class":122}," NimbusJwtEncoder",[62,97152,97153],{"class":72},"(jwks);\n",[62,97155,97156],{"class":64,"line":101},[62,97157,379],{"class":72},[22,97159,97160,97161,97164,97165,97167,97168,97170],{},"You could just use the encoder directly in the authentication controller but I feel like is something you should extract out to a service layer. Create a new class called ",[59,97162,97163],{},"TokenService"," in a new package called ",[59,97166,48855],{}," which will use the new ",[59,97169,97056],{}," to generate a token. In the following example, the token will expire after 1 hour but you can adjust it to fit your needs.",[52,97172,97174],{"className":54,"code":97173,"language":56,"meta":57,"style":57},"@Service\npublic class TokenService {\n\n private final JwtEncoder encoder;\n\n public TokenService(JwtEncoder encoder) {\n this.encoder = encoder;\n }\n\n public String generateToken(Authentication authentication) {\n Instant now = Instant.now();\n String scope = authentication.getAuthorities().stream()\n .map(GrantedAuthority::getAuthority)\n .collect(Collectors.joining(\" \"));\n JwtClaimsSet claims = JwtClaimsSet.builder()\n .issuer(\"self\")\n .issuedAt(now)\n .expiresAt(now.plus(1, ChronoUnit.HOURS))\n .subject(authentication.getName())\n .claim(\"scope\", scope)\n .build();\n return this.encoder.encode(JwtEncoderParameters.from(claims)).getTokenValue();\n }\n\n}\n",[59,97175,97176,97182,97193,97197,97206,97210,97224,97236,97240,97244,97261,97275,97294,97308,97326,97340,97354,97363,97383,97397,97412,97420,97445,97449,97453],{"__ignoreMap":57},[62,97177,97178,97180],{"class":64,"line":65},[62,97179,942],{"class":72},[62,97181,945],{"class":68},[62,97183,97184,97186,97188,97191],{"class":64,"line":76},[62,97185,116],{"class":68},[62,97187,119],{"class":68},[62,97189,97190],{"class":122}," TokenService",[62,97192,126],{"class":72},[62,97194,97195],{"class":64,"line":82},[62,97196,79],{"emptyLinePlaceholder":13},[62,97198,97199,97201,97203],{"class":64,"line":89},[62,97200,137],{"class":68},[62,97202,458],{"class":68},[62,97204,97205],{"class":72}," JwtEncoder encoder;\n",[62,97207,97208],{"class":64,"line":95},[62,97209,79],{"emptyLinePlaceholder":13},[62,97211,97212,97214,97216,97219,97222],{"class":64,"line":101},[62,97213,194],{"class":68},[62,97215,97190],{"class":122},[62,97217,97218],{"class":72},"(JwtEncoder ",[62,97220,97221],{"class":889},"encoder",[62,97223,768],{"class":72},[62,97225,97226,97228,97231,97233],{"class":64,"line":107},[62,97227,2405],{"class":149},[62,97229,97230],{"class":72},".encoder ",[62,97232,146],{"class":68},[62,97234,97235],{"class":72}," encoder;\n",[62,97237,97238],{"class":64,"line":113},[62,97239,223],{"class":72},[62,97241,97242],{"class":64,"line":129},[62,97243,79],{"emptyLinePlaceholder":13},[62,97245,97246,97248,97250,97253,97256,97259],{"class":64,"line":134},[62,97247,194],{"class":68},[62,97249,2469],{"class":72},[62,97251,97252],{"class":122},"generateToken",[62,97254,97255],{"class":72},"(Authentication ",[62,97257,97258],{"class":889},"authentication",[62,97260,768],{"class":72},[62,97262,97263,97266,97268,97271,97273],{"class":64,"line":156},[62,97264,97265],{"class":72}," Instant now ",[62,97267,146],{"class":68},[62,97269,97270],{"class":72}," Instant.",[62,97272,43561],{"class":122},[62,97274,822],{"class":72},[62,97276,97277,97280,97282,97285,97288,97290,97292],{"class":64,"line":161},[62,97278,97279],{"class":72}," String scope ",[62,97281,146],{"class":68},[62,97283,97284],{"class":72}," authentication.",[62,97286,97287],{"class":122},"getAuthorities",[62,97289,3229],{"class":72},[62,97291,2621],{"class":122},[62,97293,2223],{"class":72},[62,97295,97296,97298,97300,97303,97305],{"class":64,"line":167},[62,97297,2418],{"class":72},[62,97299,4200],{"class":122},[62,97301,97302],{"class":72},"(GrantedAuthority",[62,97304,4451],{"class":68},[62,97306,97307],{"class":72},"getAuthority)\n",[62,97309,97310,97312,97314,97316,97319,97321,97324],{"class":64,"line":173},[62,97311,2418],{"class":72},[62,97313,3897],{"class":122},[62,97315,3900],{"class":72},[62,97317,97318],{"class":122},"joining",[62,97320,2109],{"class":72},[62,97322,97323],{"class":1675},"\" \"",[62,97325,6979],{"class":72},[62,97327,97328,97331,97333,97336,97338],{"class":64,"line":179},[62,97329,97330],{"class":72}," JwtClaimsSet claims ",[62,97332,146],{"class":68},[62,97334,97335],{"class":72}," JwtClaimsSet.",[62,97337,2160],{"class":122},[62,97339,2223],{"class":72},[62,97341,97342,97344,97347,97349,97352],{"class":64,"line":185},[62,97343,2418],{"class":72},[62,97345,97346],{"class":122},"issuer",[62,97348,2109],{"class":72},[62,97350,97351],{"class":1675},"\"self\"",[62,97353,2212],{"class":72},[62,97355,97356,97358,97361],{"class":64,"line":191},[62,97357,2418],{"class":72},[62,97359,97360],{"class":122},"issuedAt",[62,97362,61570],{"class":72},[62,97364,97365,97367,97370,97373,97376,97378,97380],{"class":64,"line":209},[62,97366,2418],{"class":72},[62,97368,97369],{"class":122},"expiresAt",[62,97371,97372],{"class":72},"(now.",[62,97374,97375],{"class":122},"plus",[62,97377,2109],{"class":72},[62,97379,6689],{"class":149},[62,97381,97382],{"class":72},", ChronoUnit.HOURS))\n",[62,97384,97385,97387,97390,97393,97395],{"class":64,"line":220},[62,97386,2418],{"class":72},[62,97388,97389],{"class":122},"subject",[62,97391,97392],{"class":72},"(authentication.",[62,97394,12678],{"class":122},[62,97396,4460],{"class":72},[62,97398,97399,97401,97404,97406,97409],{"class":64,"line":226},[62,97400,2418],{"class":72},[62,97402,97403],{"class":122},"claim",[62,97405,2109],{"class":72},[62,97407,97408],{"class":1675},"\"scope\"",[62,97410,97411],{"class":72},", scope)\n",[62,97413,97414,97416,97418],{"class":64,"line":231},[62,97415,2418],{"class":72},[62,97417,2189],{"class":122},[62,97419,822],{"class":72},[62,97421,97422,97424,97426,97429,97432,97435,97437,97440,97443],{"class":64,"line":236},[62,97423,360],{"class":68},[62,97425,9961],{"class":149},[62,97427,97428],{"class":72},".encoder.",[62,97430,97431],{"class":122},"encode",[62,97433,97434],{"class":72},"(JwtEncoderParameters.",[62,97436,3507],{"class":122},[62,97438,97439],{"class":72},"(claims)).",[62,97441,97442],{"class":122},"getTokenValue",[62,97444,822],{"class":72},[62,97446,97447],{"class":64,"line":242},[62,97448,223],{"class":72},[62,97450,97451],{"class":64,"line":247},[62,97452,79],{"emptyLinePlaceholder":13},[62,97454,97455],{"class":64,"line":252},[62,97456,379],{"class":72},[22,97458,97459,97460,95890,97462,97465],{},"Next create a new controller in the ",[59,97461,48327],{},[59,97463,97464],{},"AuthController",". This is going to contain a single POST method that will use your new token service to generate a token for the authenticated user. As you can see there is some logging for debugging purposes so that in development you will see the user requesting a JWT and the token that was created.",[52,97467,97469],{"className":54,"code":97468,"language":56,"meta":57,"style":57},"@RestController\npublic class AuthController {\n\n private static final Logger LOG = LoggerFactory.getLogger(AuthController.class);\n\n private final TokenService tokenService;\n\n public AuthController(TokenService tokenService) {\n this.tokenService = tokenService;\n }\n\n @PostMapping(\"/token\")\n public String token(Authentication authentication) {\n LOG.debug(\"Token requested for user: '{}'\", authentication.getName());\n String token = tokenService.generateToken(authentication);\n LOG.debug(\"Token granted: {}\", token);\n return token;\n }\n\n}\n",[59,97470,97471,97477,97488,97492,97512,97516,97525,97529,97543,97555,97559,97563,97576,97591,97611,97626,97640,97647,97651,97655],{"__ignoreMap":57},[62,97472,97473,97475],{"class":64,"line":65},[62,97474,942],{"class":72},[62,97476,2342],{"class":68},[62,97478,97479,97481,97483,97486],{"class":64,"line":76},[62,97480,116],{"class":68},[62,97482,119],{"class":68},[62,97484,97485],{"class":122}," AuthController",[62,97487,126],{"class":72},[62,97489,97490],{"class":64,"line":82},[62,97491,79],{"emptyLinePlaceholder":13},[62,97493,97494,97496,97498,97500,97503,97505,97507,97509],{"class":64,"line":89},[62,97495,137],{"class":68},[62,97497,2101],{"class":68},[62,97499,458],{"class":68},[62,97501,97502],{"class":72}," Logger LOG ",[62,97504,146],{"class":68},[62,97506,3066],{"class":72},[62,97508,3069],{"class":122},[62,97510,97511],{"class":72},"(AuthController.class);\n",[62,97513,97514],{"class":64,"line":95},[62,97515,79],{"emptyLinePlaceholder":13},[62,97517,97518,97520,97522],{"class":64,"line":101},[62,97519,137],{"class":68},[62,97521,458],{"class":68},[62,97523,97524],{"class":72}," TokenService tokenService;\n",[62,97526,97527],{"class":64,"line":107},[62,97528,79],{"emptyLinePlaceholder":13},[62,97530,97531,97533,97535,97538,97541],{"class":64,"line":113},[62,97532,194],{"class":68},[62,97534,97485],{"class":122},[62,97536,97537],{"class":72},"(TokenService ",[62,97539,97540],{"class":889},"tokenService",[62,97542,768],{"class":72},[62,97544,97545,97547,97550,97552],{"class":64,"line":129},[62,97546,2405],{"class":149},[62,97548,97549],{"class":72},".tokenService ",[62,97551,146],{"class":68},[62,97553,97554],{"class":72}," tokenService;\n",[62,97556,97557],{"class":64,"line":134},[62,97558,223],{"class":72},[62,97560,97561],{"class":64,"line":156},[62,97562,79],{"emptyLinePlaceholder":13},[62,97564,97565,97567,97569,97571,97574],{"class":64,"line":161},[62,97566,2143],{"class":72},[62,97568,2455],{"class":68},[62,97570,2109],{"class":72},[62,97572,97573],{"class":1675},"\"/token\"",[62,97575,2212],{"class":72},[62,97577,97578,97580,97582,97585,97587,97589],{"class":64,"line":167},[62,97579,194],{"class":68},[62,97581,2469],{"class":72},[62,97583,97584],{"class":122},"token",[62,97586,97255],{"class":72},[62,97588,97258],{"class":889},[62,97590,768],{"class":72},[62,97592,97593,97596,97599,97601,97604,97607,97609],{"class":64,"line":173},[62,97594,97595],{"class":72}," LOG.",[62,97597,97598],{"class":122},"debug",[62,97600,2109],{"class":72},[62,97602,97603],{"class":1675},"\"Token requested for user: '{}'\"",[62,97605,97606],{"class":72},", authentication.",[62,97608,12678],{"class":122},[62,97610,1091],{"class":72},[62,97612,97613,97616,97618,97621,97623],{"class":64,"line":179},[62,97614,97615],{"class":72}," String token ",[62,97617,146],{"class":68},[62,97619,97620],{"class":72}," tokenService.",[62,97622,97252],{"class":122},[62,97624,97625],{"class":72},"(authentication);\n",[62,97627,97628,97630,97632,97634,97637],{"class":64,"line":185},[62,97629,97595],{"class":72},[62,97631,97598],{"class":122},[62,97633,2109],{"class":72},[62,97635,97636],{"class":1675},"\"Token granted: {}\"",[62,97638,97639],{"class":72},", token);\n",[62,97641,97642,97644],{"class":64,"line":191},[62,97643,360],{"class":68},[62,97645,97646],{"class":72}," token;\n",[62,97648,97649],{"class":64,"line":209},[62,97650,223],{"class":72},[62,97652,97653],{"class":64,"line":220},[62,97654,79],{"emptyLinePlaceholder":13},[62,97656,97657],{"class":64,"line":226},[62,97658,379],{"class":72},[22,97660,97661],{},"If everything was done correctly you should be able to start your application without error.",[26,97663,97665],{"id":97664},"spring-security-jwt-testing","Spring Security JWT Testing",[22,97667,97668],{},"With that, you should have your root path secured using JWT. Now you just need to test it.",[636,97670,97672],{"id":97671},"manual-testing","Manual Testing.",[22,97674,97675],{},"There are many ways that you can manually test this but In this tutorial, I will show you 2.",[22,97677,97678],{},[646,97679,97680],{},"Postman",[22,97682,97683],{},"An easy way to test this is by using a tool like Postman. If you create a new POST request to the token endpoint you can select Basic Auth from the Authorization tab and enter your credentials. If everything works you will get back the generated JWT in the response.",[22,97685,97686],{},[653,97687],{"alt":97688,"src":97689},"Postman Basic Auth","/images/blog/2022/09/09/postman-basic-auth.png",[22,97691,97692,97693,97696,97697,2755],{},"Copy the JWT and create a new GET request for ",[677,97694,16942],{"href":16942,"rel":97695},[681],". Go to the Authorization tab and select Bearer Token and paste in the generated token. If you send the request you should get back the string returned from the home method in the ",[59,97698,78739],{},[22,97700,97701],{},[653,97702],{"alt":97703,"src":97704},"Postman with JWT Response","/images/blog/2022/09/09/postman-with-jwt-response.png",[22,97706,97707],{},[646,97708,97709],{},"CommandLine",[22,97711,97712,97713,97718],{},"I’m a big fan of the command line and a tool called ",[677,97714,97717],{"href":97715,"rel":97716},"https://httpie.io/",[681],"httpie",". It simplifies writing commands for testing our APIs in the terminal. You can send a request to the token endpoint with your credentials using the following command:",[52,97720,97722],{"className":1663,"code":97721,"language":1665,"meta":57,"style":57},"http POST :8080/token --auth dvega:password -v\n",[59,97723,97724],{"__ignoreMap":57},[62,97725,97726,97728,97731,97734,97737,97740],{"class":64,"line":65},[62,97727,11496],{"class":122},[62,97729,97730],{"class":1675}," POST",[62,97732,97733],{"class":1675}," :8080/token",[62,97735,97736],{"class":149}," --auth",[62,97738,97739],{"class":1675}," dvega:password",[62,97741,51577],{"class":149},[22,97743,3521,97744,97747],{},[59,97745,97746],{},"-v"," argument will print the request and the response",[22,97749,97750],{},[653,97751],{"alt":97752,"src":97753},"Httpie with Authorization","/images/blog/2022/09/09/httpie-auth.png",[22,97755,97756,97757,97760,97761,2755],{},"The response will contain the generated JWT token. If you make a request to the root path without the authorization header or without the correct token you will receive a ",[4534,97758,97759],{},"401 (Denied)"," response. If however, you include the Authorization header in the correct format you will get the string returned from the home method in the ",[59,97762,78739],{},[52,97764,97766],{"className":1663,"code":97765,"language":1665,"meta":57,"style":57},"http :8080 'Authorization: Bearer JWT_TOKEN_HERE'\n",[59,97767,97768],{"__ignoreMap":57},[62,97769,97770,97772,97774],{"class":64,"line":65},[62,97771,11496],{"class":122},[62,97773,19954],{"class":1675},[62,97775,97776],{"class":1675}," 'Authorization: Bearer JWT_TOKEN_HERE'\n",[22,97778,97779],{},[653,97780],{"alt":97781,"src":97782},"Httpie Response Success","/images/blog/2022/09/09/httpie-success.png",[636,97784,97786],{"id":97785},"automated-testing","Automated Testing",[22,97788,97789],{},"Manually testing is great because you can see that everything is working as intended. However, you will need some automated tests in place so that as you make changes you can be confident that nothing has broken the existing functionality. I am not going to go to much into this but I wanted to provide you with a simple example of how to write such a test.",[22,97791,97792,97793,97796,97797,2755],{},"When you brought in the resource server there was one dependency that didn’t get brought in and that is ",[59,97794,97795],{},"spring-security-test",". Before writing any security-related tests you will need to add this to your ",[59,97798,1765],{},[52,97800,97802],{"className":1769,"code":97801,"language":1771,"meta":57,"style":57},"\u003Cdependency>\n \u003CgroupId>org.springframework.security\u003C/groupId>\n \u003CartifactId>spring-security-test\u003C/artifactId>\n\u003C/dependency>\n",[59,97803,97804,97812,97825,97838],{"__ignoreMap":57},[62,97805,97806,97808,97810],{"class":64,"line":65},[62,97807,760],{"class":72},[62,97809,1781],{"class":1780},[62,97811,1784],{"class":72},[62,97813,97814,97816,97818,97821,97823],{"class":64,"line":76},[62,97815,1789],{"class":72},[62,97817,1792],{"class":1780},[62,97819,97820],{"class":72},">org.springframework.security\u003C/",[62,97822,1792],{"class":1780},[62,97824,1784],{"class":72},[62,97826,97827,97829,97831,97834,97836],{"class":64,"line":82},[62,97828,1789],{"class":72},[62,97830,1806],{"class":1780},[62,97832,97833],{"class":72},">spring-security-test\u003C/",[62,97835,1806],{"class":1780},[62,97837,1784],{"class":72},[62,97839,97840,97842,97844],{"class":64,"line":89},[62,97841,1818],{"class":72},[62,97843,1781],{"class":1780},[62,97845,1784],{"class":72},[22,97847,97848,97849,19931,97852,97854],{},"When you write a slice test that focuses on just the web layer configuration and service classes will not be added to the application context. To make everything work you will need to manually import the ",[59,97850,97851],{},"SercurityConfig",[59,97853,97163],{}," classes. These tests should be pretty self-explanatory but if you would like me to go though them please reach out and let me know.",[52,97856,97858],{"className":54,"code":97857,"language":56,"meta":57,"style":57},"@WebMvcTest({HomeController.class, AuthController.class})\n@Import({SecurityConfig.class, TokenService.class})\nclass HomeControllerTest {\n\n @Autowired\n MockMvc mvc;\n\n @Test\n void rootWhenUnauthenticatedThen401() throws Exception {\n this.mvc.perform(get(\"/\"))\n .andExpect(status().isUnauthorized());\n }\n\n @Test\n void rootWhenAuthenticatedThenSaysHelloUser() throws Exception {\n MvcResult result = this.mvc.perform(post(\"/token\")\n .with(httpBasic(\"dvega\", \"password\")))\n .andExpect(status().isOk())\n .andReturn();\n\n String token = result.getResponse().getContentAsString();\n\n this.mvc.perform(get(\"/\")\n .header(\"Authorization\", \"Bearer \" + token))\n .andExpect(content().string(\"Hello, dvega\"));\n }\n\n @Test\n @WithMockUser\n public void rootWithMockUserStatusIsOK() throws Exception {\n this.mvc.perform(get(\"/\")).andExpect(status().isOk());\n }\n\n}\n",[59,97859,97860,97870,97880,97889,97893,97899,97904,97908,97914,97927,97947,97965,97969,97973,97979,97992,98015,98035,98052,98061,98065,98084,98088,98106,98125,98146,98150,98154,98160,98167,98182,98212,98216,98220],{"__ignoreMap":57},[62,97861,97862,97864,97867],{"class":64,"line":65},[62,97863,942],{"class":72},[62,97865,97866],{"class":68},"WebMvcTest",[62,97868,97869],{"class":72},"({HomeController.class, AuthController.class})\n",[62,97871,97872,97874,97877],{"class":64,"line":76},[62,97873,942],{"class":72},[62,97875,97876],{"class":68},"Import",[62,97878,97879],{"class":72},"({SecurityConfig.class, TokenService.class})\n",[62,97881,97882,97884,97887],{"class":64,"line":82},[62,97883,11671],{"class":68},[62,97885,97886],{"class":122}," HomeControllerTest",[62,97888,126],{"class":72},[62,97890,97891],{"class":64,"line":89},[62,97892,79],{"emptyLinePlaceholder":13},[62,97894,97895,97897],{"class":64,"line":95},[62,97896,2143],{"class":72},[62,97898,11687],{"class":68},[62,97900,97901],{"class":64,"line":101},[62,97902,97903],{"class":72}," MockMvc mvc;\n",[62,97905,97906],{"class":64,"line":107},[62,97907,79],{"emptyLinePlaceholder":13},[62,97909,97910,97912],{"class":64,"line":113},[62,97911,2143],{"class":72},[62,97913,11705],{"class":68},[62,97915,97916,97918,97921,97923,97925],{"class":64,"line":129},[62,97917,11710],{"class":68},[62,97919,97920],{"class":122}," rootWhenUnauthenticatedThen401",[62,97922,5398],{"class":72},[62,97924,11501],{"class":68},[62,97926,11504],{"class":72},[62,97928,97929,97931,97934,97937,97939,97941,97943,97945],{"class":64,"line":134},[62,97930,2405],{"class":149},[62,97932,97933],{"class":72},".mvc.",[62,97935,97936],{"class":122},"perform",[62,97938,2109],{"class":72},[62,97940,11363],{"class":122},[62,97942,2109],{"class":72},[62,97944,15635],{"class":1675},[62,97946,5047],{"class":72},[62,97948,97949,97951,97954,97956,97958,97960,97963],{"class":64,"line":156},[62,97950,2418],{"class":72},[62,97952,97953],{"class":122},"andExpect",[62,97955,2109],{"class":72},[62,97957,55012],{"class":122},[62,97959,3229],{"class":72},[62,97961,97962],{"class":122},"isUnauthorized",[62,97964,1091],{"class":72},[62,97966,97967],{"class":64,"line":161},[62,97968,223],{"class":72},[62,97970,97971],{"class":64,"line":167},[62,97972,79],{"emptyLinePlaceholder":13},[62,97974,97975,97977],{"class":64,"line":173},[62,97976,2143],{"class":72},[62,97978,11705],{"class":68},[62,97980,97981,97983,97986,97988,97990],{"class":64,"line":179},[62,97982,11710],{"class":68},[62,97984,97985],{"class":122}," rootWhenAuthenticatedThenSaysHelloUser",[62,97987,5398],{"class":72},[62,97989,11501],{"class":68},[62,97991,11504],{"class":72},[62,97993,97994,97997,97999,98001,98003,98005,98007,98009,98011,98013],{"class":64,"line":185},[62,97995,97996],{"class":72}," MvcResult result ",[62,97998,146],{"class":68},[62,98000,9961],{"class":149},[62,98002,97933],{"class":72},[62,98004,97936],{"class":122},[62,98006,2109],{"class":72},[62,98008,38838],{"class":122},[62,98010,2109],{"class":72},[62,98012,97573],{"class":1675},[62,98014,2212],{"class":72},[62,98016,98017,98019,98021,98023,98025,98027,98029,98031,98033],{"class":64,"line":191},[62,98018,6410],{"class":72},[62,98020,54126],{"class":122},[62,98022,2109],{"class":72},[62,98024,81114],{"class":122},[62,98026,2109],{"class":72},[62,98028,46121],{"class":1675},[62,98030,976],{"class":72},[62,98032,16219],{"class":1675},[62,98034,81283],{"class":72},[62,98036,98037,98039,98041,98043,98045,98047,98050],{"class":64,"line":209},[62,98038,2418],{"class":72},[62,98040,97953],{"class":122},[62,98042,2109],{"class":72},[62,98044,55012],{"class":122},[62,98046,3229],{"class":72},[62,98048,98049],{"class":122},"isOk",[62,98051,4460],{"class":72},[62,98053,98054,98056,98059],{"class":64,"line":220},[62,98055,2418],{"class":72},[62,98057,98058],{"class":122},"andReturn",[62,98060,822],{"class":72},[62,98062,98063],{"class":64,"line":226},[62,98064,79],{"emptyLinePlaceholder":13},[62,98066,98067,98069,98071,98074,98077,98079,98082],{"class":64,"line":231},[62,98068,97615],{"class":72},[62,98070,146],{"class":68},[62,98072,98073],{"class":72}," result.",[62,98075,98076],{"class":122},"getResponse",[62,98078,3229],{"class":72},[62,98080,98081],{"class":122},"getContentAsString",[62,98083,822],{"class":72},[62,98085,98086],{"class":64,"line":236},[62,98087,79],{"emptyLinePlaceholder":13},[62,98089,98090,98092,98094,98096,98098,98100,98102,98104],{"class":64,"line":242},[62,98091,2405],{"class":149},[62,98093,97933],{"class":72},[62,98095,97936],{"class":122},[62,98097,2109],{"class":72},[62,98099,11363],{"class":122},[62,98101,2109],{"class":72},[62,98103,15635],{"class":1675},[62,98105,2212],{"class":72},[62,98107,98108,98110,98112,98114,98116,98118,98120,98122],{"class":64,"line":247},[62,98109,6410],{"class":72},[62,98111,18346],{"class":122},[62,98113,2109],{"class":72},[62,98115,18369],{"class":1675},[62,98117,976],{"class":72},[62,98119,18374],{"class":1675},[62,98121,4507],{"class":68},[62,98123,98124],{"class":72}," token))\n",[62,98126,98127,98129,98131,98133,98135,98137,98139,98141,98144],{"class":64,"line":252},[62,98128,2418],{"class":72},[62,98130,97953],{"class":122},[62,98132,2109],{"class":72},[62,98134,2230],{"class":122},[62,98136,3229],{"class":72},[62,98138,88059],{"class":122},[62,98140,2109],{"class":72},[62,98142,98143],{"class":1675},"\"Hello, dvega\"",[62,98145,6979],{"class":72},[62,98147,98148],{"class":64,"line":257},[62,98149,223],{"class":72},[62,98151,98152],{"class":64,"line":271},[62,98153,79],{"emptyLinePlaceholder":13},[62,98155,98156,98158],{"class":64,"line":281},[62,98157,2143],{"class":72},[62,98159,11705],{"class":68},[62,98161,98162,98164],{"class":64,"line":286},[62,98163,2143],{"class":72},[62,98165,98166],{"class":68},"WithMockUser\n",[62,98168,98169,98171,98173,98176,98178,98180],{"class":64,"line":291},[62,98170,194],{"class":68},[62,98172,200],{"class":68},[62,98174,98175],{"class":122}," rootWithMockUserStatusIsOK",[62,98177,5398],{"class":72},[62,98179,11501],{"class":68},[62,98181,11504],{"class":72},[62,98183,98184,98186,98188,98190,98192,98194,98196,98198,98200,98202,98204,98206,98208,98210],{"class":64,"line":296},[62,98185,2405],{"class":149},[62,98187,97933],{"class":72},[62,98189,97936],{"class":122},[62,98191,2109],{"class":72},[62,98193,11363],{"class":122},[62,98195,2109],{"class":72},[62,98197,15635],{"class":1675},[62,98199,81247],{"class":72},[62,98201,97953],{"class":122},[62,98203,2109],{"class":72},[62,98205,55012],{"class":122},[62,98207,3229],{"class":72},[62,98209,98049],{"class":122},[62,98211,1091],{"class":72},[62,98213,98214],{"class":64,"line":302},[62,98215,223],{"class":72},[62,98217,98218],{"class":64,"line":308},[62,98219,79],{"emptyLinePlaceholder":13},[62,98221,98222],{"class":64,"line":314},[62,98223,379],{"class":72},[26,98225,1499],{"id":1498},[22,98227,98228,98229,2755],{},"When I started going down the route of creating this tutorial my whole goal was to let you know there was an easier way to secure your APIs using JWTs. My hope is that now that you know Spring Security has built-in support for JSON Web Tokens using the oAuth2 Resource Server you can reach for it in your next project. This was just the starting line of how to use JWTs in your Spring Boot applications and by no means the finish line. If you have questions about your specific configuration please ",[677,98230,98232],{"href":86334,"rel":98231},[681],"reach out to me",[22,98234,98235],{},"I feel very fortunate that I get to work for a company like VMware and that I have access to some really smart people. That access means, even more, when you work with such a great group of people who are always willing to share their knowledge and help out. I would like to give a special shoutout to the following individuals for helping me put this together:",[915,98237,98238,98241,98244,98247,98250],{},[37,98239,98240],{},"Daniel Garnier-Moiroux",[37,98242,98243],{},"Steve Riesenberg",[37,98245,98246],{},"Rob Winch",[37,98248,98249],{},"Josh Cummings",[37,98251,98252],{},"Toshiaki Maki",[1527,98254,98255],{},"html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .suaqH, html code.shiki .suaqH{--shiki-default:#22863A;--shiki-dark:#85E89D;--shiki-sepia:#22863A}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .spoOn, html code.shiki .spoOn{--shiki-default:#E36209;--shiki-dark:#FFAB70;--shiki-sepia:#E36209}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}html pre.shiki code .s4-Ip, html code.shiki .s4-Ip{--shiki-default:#6A737D;--shiki-dark:#6A737D;--shiki-sepia:#6A737D}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}",{"title":57,"searchDepth":76,"depth":76,"links":98257},[98258,98261,98262,98263,98264,98270,98271,98275],{"id":95525,"depth":76,"text":95526,"children":98259},[98260],{"id":95548,"depth":82,"text":95549},{"id":93065,"depth":76,"text":93066},{"id":95769,"depth":76,"text":95770},{"id":95872,"depth":76,"text":95873},{"id":96202,"depth":76,"text":96203,"children":98265},[98266,98267,98268,98269],{"id":96265,"depth":82,"text":96266},{"id":96587,"depth":82,"text":96588},{"id":96612,"depth":82,"text":96613},{"id":96734,"depth":82,"text":96594},{"id":97042,"depth":76,"text":97043},{"id":97664,"depth":76,"text":97665,"children":98272},[98273,98274],{"id":97671,"depth":82,"text":97672},{"id":97785,"depth":82,"text":97786},{"id":1498,"depth":76,"text":1499},{"_id":98277,"path":98278,"title":98279,"description":98280,"meta":98281,"body":98286},"content/blog/2022/05/17/spring-for-graphql.md","/blog/2022/05/17/spring-for-graphql","GraphQL Spring Boot - Up and Running with Spring for GraphQL","In this tutorial you are going to learn how to get up and running with Spring for GraphQL.",{"slug":98282,"date":98283,"published":13,"tags":98284,"author":17,"cover":98285,"excerpt":-1},"spring-for-graphql","2022-05-17T16:00:00.000Z",[11002,8824],"./spring-for-graphql-thumbnail.png",{"type":19,"value":98287,"toc":101179},[98288,98302,98305,98309,98312,98315,98318,98321,98324,98327,98333,98337,98340,98347,98350,98353,98368,98374,98378,98381,98384,98454,98457,98461,98464,98467,98472,98475,98480,98483,98548,98569,98572,98577,98580,98614,98617,98622,98625,98631,98649,98655,98701,98706,98715,98788,98791,98793,98799,98803,98806,98813,98819,98822,98825,98831,98842,98845,98856,98861,98877,98884,98892,98895,98899,98906,98912,98918,98922,98925,98928,98952,99013,99168,99177,99180,99561,99567,99919,99923,99932,99941,100088,100095,100122,100126,100129,100145,100212,100218,100317,100326,100352,100359,100486,100489,100493,100499,100511,100518,100524,100530,100537,100557,100563,100566,100630,100636,100642,100687,100695,100854,100865,100873,100932,100955,100994,100997,101070,101073,101076,101087,101098,101110,101112,101115,101139,101141,101167,101169,101176],[22,98289,98290,98291,98295,98296,98301],{},"In this tutorial you are going to learn how to get up and running with ",[677,98292,88091],{"href":98293,"rel":98294},"https://spring.io/projects/spring-graphql",[681],". At the time of this article being published Spring for GraphQL 1.0 is close to being released. This is a huge milestone and the culmination of a really great collaboration between the Spring team and the ",[677,98297,98300],{"href":98298,"rel":98299},"https://www.graphql-java.com/",[681],"GraphQL Java"," team.",[22,98303,98304],{},"Before you start writing code you should take some time to understand what GraphQL is and why you might reach for it in your next application. By the end of this tutorial you will have what you need to build your first GraphQL API using Spring for GraphQL.",[26,98306,98308],{"id":98307},"why-graphql","Why GraphQL",[22,98310,98311],{},"There are so many things to learn but your time is valuable and you need to spend it wisely. That said, don’t dive into something just because everyone else is talking about. Let’s discuss why you should spend some of that valuable time learning GraphQL.",[22,98313,98314],{},"If you’re currently building REST APIs you have probably come across this scenario. The API might have started out for a single purpose like feeding data to your frontend client for the web. Even if you stay right there this application might of evolved over time and may have even split into micro frontends. As this application grows you are constantly getting requests for new endpoints that return the data in a specific shape.",[22,98316,98317],{},"The next evolution of this API is when more and more clients have requirements for data. You might have 3rd party services, internal microservices, mobile applications and IoT applications all with their own specific data requirements.",[22,98319,98320],{},"Each of these clients have specific limitations and data requirements. The web application for this application has a ton of real estate on the screen so it can request a lot of data and display it to the user. The web client also doesn’t have to think about constraints like battery life or connection speeds.",[22,98322,98323],{},"When you move to a mobile application you do have to think about things like screen size, battery life and connection speeds. Because of those constraints you might have setup new endpoints for your mobile applications to return smaller data sets.",[22,98325,98326],{},"This is where GraphQL really stands out and provides a solution to a real problem. GraphQL has a single endpoint and lets each individual client request exactly what data they need. Gone are the days of standup up new endpoints each time a new requirement is raised.",[22,98328,98329],{},[653,98330],{"alt":98331,"src":98332},"wes-hicks-4-EeTnaC1S4-unsplash.jpg","/images/blog/2022/05/17/wes-hicks-4-EeTnaC1S4-unsplash.jpg",[26,98334,98336],{"id":98335},"what-is-graphql","What is GraphQL",[22,98338,98339],{},"Now that you understand why you would reach for GraphQL let’s talk about some of the fundamentals of it. First off GraphQL is a query language for your API, and a server-side runtime for executing queries using a type system you define for your data. It is an alternative to REST & SOAP and in most cases will replace those, not sit alongside them.",[22,98341,98342,98343,98346],{},"GraphQL ",[646,98344,98345],{},"isn't tied to any specific database or storage engine"," and is instead backed by your existing code and data. This is an important concept to understand so we should start there.",[22,98348,98349],{},"In your existing applications your REST API is also not tied to any specific database. In your Rest Controller endpoints you accept the arguments you need and in turn delegate to a service or repository for the data. GraphQL is no different so if you’re asking questions like “How does GraphQL connect to my database” you’re asking the wrong ones.",[22,98351,98352],{},"GraphQL was created back in 2012 by Facebook. They were facing a lot of the same problems we discussed in the previous section. They had these massive amounts of data sets and mobile was becoming the dominant way users were accessing their application.",[22,98354,98355,98356,98361,98362,98367],{},"GraphQL was open sourced back in 2015 and is now governed by a ",[677,98357,98360],{"href":98358,"rel":98359},"https://graphql.org/foundation/",[681],"neutral foundation"," made up of some really great companies. You will find implementations of the ",[677,98363,98366],{"href":98364,"rel":98365},"https://spec.graphql.org/",[681],"GraphQL Spec"," in almost every language. This means that after you learn it on the Java side you should be able to move over to JavaScript and hit the ground running.",[22,98369,98370],{},[653,98371],{"alt":98372,"src":98373},"alina-grubnyak-ZiQkhI7417A-unsplash.jpg","/images/blog/2022/05/17/alina-grubnyak-ZiQkhI7417A-unsplash.jpg",[636,98375,98377],{"id":98376},"graphql-client-server","GraphQL Client + Server 🤝",[22,98379,98380],{},"GraphQL is technology for client / server data exchange. The client + server part means there are two sides to this story.",[22,98382,98383],{},"On the client side you’re working with the Query Language. This isn’t SQL, kind of looks like JSON and is something we refer to as a Domain Specific Language (DSL). Like anything there is a little bit of an overhead in learning how to write these queries but once you get the hang of it they are quite intuitive and expressive. This is a query you will write later on to retrieve all of the books along with its author.",[52,98385,98387],{"className":8822,"code":98386,"language":8824,"meta":57,"style":57},"query {\n allBooks {\n title\n pages\n rating {\n star\n }\n author {\n firstName\n lastName\n }\n }\n}\n",[59,98388,98389,98395,98402,98406,98410,98417,98422,98426,98432,98437,98442,98446,98450],{"__ignoreMap":57},[62,98390,98391,98393],{"class":64,"line":65},[62,98392,9155],{"class":68},[62,98394,126],{"class":72},[62,98396,98397,98400],{"class":64,"line":76},[62,98398,98399],{"class":889}," allBooks",[62,98401,126],{"class":72},[62,98403,98404],{"class":64,"line":82},[62,98405,83608],{"class":889},[62,98407,98408],{"class":64,"line":89},[62,98409,83613],{"class":889},[62,98411,98412,98415],{"class":64,"line":95},[62,98413,98414],{"class":889}," rating",[62,98416,126],{"class":72},[62,98418,98419],{"class":64,"line":101},[62,98420,98421],{"class":889}," star\n",[62,98423,98424],{"class":64,"line":107},[62,98425,223],{"class":72},[62,98427,98428,98430],{"class":64,"line":113},[62,98429,8863],{"class":889},[62,98431,126],{"class":72},[62,98433,98434],{"class":64,"line":129},[62,98435,98436],{"class":889}," firstName\n",[62,98438,98439],{"class":64,"line":134},[62,98440,98441],{"class":889}," lastName\n",[62,98443,98444],{"class":64,"line":156},[62,98445,223],{"class":72},[62,98447,98448],{"class":64,"line":161},[62,98449,3731],{"class":72},[62,98451,98452],{"class":64,"line":167},[62,98453,379],{"class":72},[22,98455,98456],{},"On the server side we have an execution engine that will execute these queries mapping each of the fields to resolvers, also known as data fetchers.",[636,98458,98460],{"id":98459},"graphql-type-system","GraphQL Type System",[22,98462,98463],{},"If you have never seen GraphQL before you can take a good guess at what you’re doing with the query above. You are selecting fields on objects where the root object is a book and inside of that book there is a rating and author type.",[22,98465,98466],{},"Just looking at this query though you aren’t exactly sure what each type is and what fields might be available on the sub-types like rating and author. This is where a schema enters the conversation and is able to describe and validate what fields you can query for.",[22,98468,98469],{},[646,98470,98471],{},"Type Language",[22,98473,98474],{},"The GraphQL Schema Language allows us to create a consistent schema across languages. This means that if you’re using Java, JavaScript or Go you will understand a GraphQL schema because it is language agnostic. Let’s take a look at some of the types that make up a GraphQL schema.",[22,98476,98477],{},[646,98478,98479],{},"Object Types and Fields",[22,98481,98482],{},"Object types and fields are a common type that you will work with in any GraphQL Schema. Once you have your models in place in your Spring application you will need to define what objects and fields you want to make available in your API. The following is a schema definition for a Book object:",[52,98484,98486],{"className":8822,"code":98485,"language":8824,"meta":57,"style":57},"type Book {\n id: ID!\n title: String\n pages: Int\n rating: Rating\n author: Author\n}\n",[59,98487,98488,98496,98507,98515,98524,98534,98544],{"__ignoreMap":57},[62,98489,98490,98492,98494],{"class":64,"line":65},[62,98491,1947],{"class":68},[62,98493,8833],{"class":149},[62,98495,126],{"class":72},[62,98497,98498,98501,98503,98505],{"class":64,"line":76},[62,98499,98500],{"class":889}," id",[62,98502,3696],{"class":72},[62,98504,8845],{"class":149},[62,98506,8848],{"class":68},[62,98508,98509,98511,98513],{"class":64,"line":82},[62,98510,50719],{"class":889},[62,98512,3696],{"class":72},[62,98514,8858],{"class":149},[62,98516,98517,98520,98522],{"class":64,"line":89},[62,98518,98519],{"class":889}," pages",[62,98521,3696],{"class":72},[62,98523,8877],{"class":149},[62,98525,98526,98529,98531],{"class":64,"line":95},[62,98527,98528],{"class":889}," rating",[62,98530,3696],{"class":72},[62,98532,98533],{"class":149},"Rating\n",[62,98535,98536,98539,98541],{"class":64,"line":101},[62,98537,98538],{"class":889}," author",[62,98540,3696],{"class":72},[62,98542,98543],{"class":149},"Author\n",[62,98545,98546],{"class":64,"line":107},[62,98547,379],{"class":72},[915,98549,98550,98553,98564],{},[37,98551,98552],{},"The values on the left are the field names",[37,98554,98555,98556],{},"The values on the right are the types\n",[915,98557,98558,98561],{},[37,98559,98560],{},"ID, String & Int are built-in Scalar types",[37,98562,98563],{},"Rating & Author are Object Types that you define",[37,98565,3521,98566,98568],{},[59,98567,6277],{}," simply tells us that you can always expect a value back and will never need to check for null.",[22,98570,98571],{},"You will learn more about building schemas when you build your first Spring for GraphQL application later in this tutorial.",[22,98573,98574],{},[646,98575,98576],{},"Scalar Types",[22,98578,98579],{},"As you learned in the previous section there are built-in Scalar types. GraphQL comes with the following types out of the box:",[915,98581,98582,98587,98592,98597,98606],{},[37,98583,98584,98586],{},[59,98585,36791],{},": A signed 32-bit integer.",[37,98588,98589,98591],{},[59,98590,76483],{},": A signed double-precision floating-point value.",[37,98593,98594,98596],{},[59,98595,973],{},": A UTF-8 character sequence.",[37,98598,98599,49672,98601,98603,98604,2755],{},[59,98600,76514],{},[59,98602,21775],{}," or ",[59,98605,50950],{},[37,98607,98608,98610,98611,98613],{},[59,98609,8845],{},": The ID scalar type represents a unique identifier, often used to refetch an object or as the key for a cache. The ID type is serialized in the same way as a String; however, defining it as an ",[59,98612,8845],{}," signifies that it is not intended to be human-readable.",[22,98615,98616],{},"If you need to define a custom type like a date you can do so but this needs to be configured on the server side.",[22,98618,98619],{},[646,98620,98621],{},"Root Operation Types",[22,98623,98624],{},"After you have setup your object types you will need a way to read and write data from your API. There are 3 root operation types:",[22,98626,98627],{},[653,98628],{"alt":98629,"src":98630},"root-operation-types.png","/images/blog/2022/05/17/root-operation-types.png",[915,98632,98633,98638,98643],{},[37,98634,98635,98637],{},[646,98636,83344],{},": Used to read data",[37,98639,98640,98642],{},[646,98641,83758],{},": Used to create, update and delete data",[37,98644,98645,98648],{},[646,98646,98647],{},"Subscription",": Similar to a query allowing you to fetch data from the server. Subscriptions offer a long-lasting operation that can change their result over time.",[22,98650,98651,98652,98654],{},"The following defines the Query operation in our schema. The values on the left are the field names (query names) and the values on the right are the return type. In the first example the brackets around ",[59,98653,8545],{}," simply means that it could return more than 1.",[52,98656,98658],{"className":8822,"code":98657,"language":8824,"meta":57,"style":57},"type Query {\n allBooks: [Book]\n findOne(id: ID!): Book\n}\n",[59,98659,98660,98668,98678,98697],{"__ignoreMap":57},[62,98661,98662,98664,98666],{"class":64,"line":65},[62,98663,1947],{"class":68},[62,98665,8934],{"class":149},[62,98667,126],{"class":72},[62,98669,98670,98672,98674,98676],{"class":64,"line":76},[62,98671,98399],{"class":889},[62,98673,36721],{"class":72},[62,98675,8545],{"class":149},[62,98677,8959],{"class":72},[62,98679,98680,98683,98685,98687,98689,98691,98693,98695],{"class":64,"line":82},[62,98681,98682],{"class":889}," findOne",[62,98684,2109],{"class":72},[62,98686,6283],{"class":889},[62,98688,3696],{"class":72},[62,98690,8845],{"class":149},[62,98692,6277],{"class":68},[62,98694,8977],{"class":72},[62,98696,8980],{"class":149},[62,98698,98699],{"class":64,"line":89},[62,98700,379],{"class":72},[22,98702,98703],{},[646,98704,98705],{},"Arguments",[22,98707,98708,98709,98711,98712,98714],{},"Each field on a GraphQL object type can have one or more arguments. The ",[59,98710,39503],{}," query above defines a single argument of type ID. The ",[59,98713,6277],{}," states that the ID can’t be null and must be supplied. When you write a query against that you can pass in the id like so:",[52,98716,98718],{"className":8822,"code":98717,"language":8824,"meta":57,"style":57},"query {\n findOne(id: 2) {\n title\n pages\n rating {\n star\n }\n author {\n firstName\n lastName\n }\n }\n}\n",[59,98719,98720,98726,98740,98744,98748,98754,98758,98762,98768,98772,98776,98780,98784],{"__ignoreMap":57},[62,98721,98722,98724],{"class":64,"line":65},[62,98723,9155],{"class":68},[62,98725,126],{"class":72},[62,98727,98728,98730,98732,98734,98736,98738],{"class":64,"line":76},[62,98729,98682],{"class":889},[62,98731,2109],{"class":72},[62,98733,6283],{"class":889},[62,98735,3696],{"class":72},[62,98737,5219],{"class":149},[62,98739,768],{"class":72},[62,98741,98742],{"class":64,"line":82},[62,98743,83608],{"class":889},[62,98745,98746],{"class":64,"line":89},[62,98747,83613],{"class":889},[62,98749,98750,98752],{"class":64,"line":95},[62,98751,98414],{"class":889},[62,98753,126],{"class":72},[62,98755,98756],{"class":64,"line":101},[62,98757,98421],{"class":889},[62,98759,98760],{"class":64,"line":107},[62,98761,223],{"class":72},[62,98763,98764,98766],{"class":64,"line":113},[62,98765,8863],{"class":889},[62,98767,126],{"class":72},[62,98769,98770],{"class":64,"line":129},[62,98771,98436],{"class":889},[62,98773,98774],{"class":64,"line":134},[62,98775,98441],{"class":889},[62,98777,98778],{"class":64,"line":156},[62,98779,223],{"class":72},[62,98781,98782],{"class":64,"line":161},[62,98783,3731],{"class":72},[62,98785,98786],{"class":64,"line":167},[62,98787,379],{"class":72},[22,98789,98790],{},"There are more types to learn about but they basic types will get you up and running.",[26,98792,88091],{"id":98282},[22,98794,98795,98796,98301],{},"Now that you have some background on the what and why of GraphQL it’s time to learn about Spring for GraphQL. As I mentioned earlier this project is result of a really great collaboration between the Spring team and the ",[677,98797,98300],{"href":98298,"rel":98798},[681],[636,98800,98802],{"id":98801},"graphql-for-java","GraphQL for Java",[22,98804,98805],{},"GraphQL Java was released in 2015 shortly after Facebook open sourced GraphQL. GraphQL Java is the server side execution engine and it does this one thing really well. This means that it doesn’t try and focus on the whole story and it’s limited on purpose.",[22,98807,98808,98809,98812],{},"GraphQL for Java has been tried and tested by some of the biggest companies in the world, including Twitter. Here is a tweet noting that Twitter is running GraphQL for Java in production and serving 500k requests per second. That is insane and tells you the library is ready for the greatest place on the internet... ",[646,98810,98811],{},"PRODUCTION"," 👏🏻",[22,98814,98815],{},[677,98816,98817],{"href":98817,"rel":98818},"https://twitter.com/graphql_java/status/1450195999373205506?lang=en",[681],[636,98820,88091],{"id":98821},"spring-for-graphql-1",[22,98823,98824],{},"So GraphQL for Java is the execution engine, why do we need Spring for GraphQL? GraphQL Java Engine itself is only concerned with executing queries. It doesn't deal with any HTTP or JSON related topics. The first thing Spring does is provide server transports in the form of:",[22,98826,98827],{},[653,98828],{"alt":98829,"src":98830},"graphql-server-transports.png","/images/blog/2022/05/17/graphql-server-transports.png",[915,98832,98833,98836,98839],{},[37,98834,98835],{},"Http (Spring MVC or Spring Webflux)",[37,98837,98838],{},"WebSocket",[37,98840,98841],{},"RSocket",[22,98843,98844],{},"The next feature Spring brings to the table is what it does best. Spring makes the process of integrating with a library seamless and takes away the pain of dependency management, configuration and lets you focus on building out the features of your application.",[22,98846,98847,98848,98851,98852,98855],{},"When you select ",[646,98849,98850],{},"Spring GraphQL"," as a dependency in the Spring Initializer you will get the ",[59,98853,98854],{},"spring-boot-starter-gaphql"," dependency added to your build. ****",[22,98857,98858],{},[646,98859,98860],{},"Spring Boot Starter GraphQL",[915,98862,98863,98866,98869,98871,98874],{},[37,98864,98865],{},"Dependency Management",[37,98867,98868],{},"Auto Configuration",[37,98870,55480],{},[37,98872,98873],{},"Metrics",[37,98875,98876],{},"GraphiQL UI and Schema Pages",[22,98878,98879,98880,98883],{},"You will also get testing support out of the box in the form of the ",[59,98881,98882],{},"spring-graphql-test"," starter which provides:",[915,98885,98886,98889],{},[37,98887,98888],{},"GraphQL Slice Tests",[37,98890,98891],{},"GraphQL Tester",[22,98893,98894],{},"Now that you have a good understand of what Spring for GraphQL is its time to write your first application 🤩",[26,98896,98898],{"id":98897},"build-your-first-graphql-api-with-spring-for-graphql","Build your first GraphQL API with Spring for GraphQL",[22,98900,98901,98902,98905],{},"To build your first GraphQL API head over to ",[677,98903,2903],{"href":40207,"rel":98904},[681]," and while you’re there you might as well bookmark it 😉 Create a new project using Java 17+, Spring Boot 2.7+ and select the following dependencies:",[915,98907,98908,98910],{},[37,98909,1756],{},[37,98911,98850],{},[22,98913,98914],{},[653,98915],{"alt":98916,"src":98917},"graphql-spring-init.png","/images/blog/2022/05/17/graphql-spring-init.png",[636,98919,98921],{"id":98920},"the-data-layer","The Data Layer",[22,98923,98924],{},"As I mentioned earlier the GraphQL layer isn’t tied to any specific database and is backed by your existing code + data. To demonstrate this you are going to create an application that isn’t tied to any specific database and hold its records in memory.",[22,98926,98927],{},"This application is going to manage your book reading list. To get started you will need to model a Book, Author and Rating object. You could write a normal class with constructors, getters & setters, equals & hash code and a toString or you can save yourself some keystrokes and use a record type.",[52,98929,98931],{"className":54,"code":98930,"language":56,"meta":57,"style":57},"public record Book(Integer id, String title, Integer pages, Rating rating, Author author) {\n\n}\n",[59,98932,98933,98944,98948],{"__ignoreMap":57},[62,98934,98935,98937,98939,98941],{"class":64,"line":65},[62,98936,116],{"class":68},[62,98938,2996],{"class":68},[62,98940,8833],{"class":122},[62,98942,98943],{"class":72},"(Integer id, String title, Integer pages, Rating rating, Author author) {\n",[62,98945,98946],{"class":64,"line":76},[62,98947,79],{"emptyLinePlaceholder":13},[62,98949,98950],{"class":64,"line":82},[62,98951,379],{"class":72},[52,98953,98955],{"className":54,"code":98954,"language":56,"meta":57,"style":57},"public record Author(Integer id, String firstName, String lastName) {\n\n public String fullName() {\n return firstName + \" \" + lastName;\n }\n\n}\n",[59,98956,98957,98969,98973,98984,99001,99005,99009],{"__ignoreMap":57},[62,98958,98959,98961,98963,98966],{"class":64,"line":65},[62,98960,116],{"class":68},[62,98962,2996],{"class":68},[62,98964,98965],{"class":122}," Author",[62,98967,98968],{"class":72},"(Integer id, String firstName, String lastName) {\n",[62,98970,98971],{"class":64,"line":76},[62,98972,79],{"emptyLinePlaceholder":13},[62,98974,98975,98977,98979,98982],{"class":64,"line":82},[62,98976,194],{"class":68},[62,98978,2469],{"class":72},[62,98980,98981],{"class":122},"fullName",[62,98983,206],{"class":72},[62,98985,98986,98988,98991,98993,98996,98998],{"class":64,"line":89},[62,98987,360],{"class":68},[62,98989,98990],{"class":72}," firstName ",[62,98992,1148],{"class":68},[62,98994,98995],{"class":1675}," \" \"",[62,98997,4507],{"class":68},[62,98999,99000],{"class":72}," lastName;\n",[62,99002,99003],{"class":64,"line":95},[62,99004,223],{"class":72},[62,99006,99007],{"class":64,"line":101},[62,99008,79],{"emptyLinePlaceholder":13},[62,99010,99011],{"class":64,"line":107},[62,99012,379],{"class":72},[52,99014,99016],{"className":54,"code":99015,"language":56,"meta":57,"style":57},"public enum Rating {\n FIVE_STARS(\"⭐️⭐️⭐️⭐️⭐️️️️\"),\n FOUR_STARS(\"⭐️⭐️⭐️⭐️\"),\n THREE_STARS(\"⭐️⭐️⭐️\"),\n TWO_STARS(\"⭐️⭐️\"),\n ONE_STAR(\"⭐️\");\n\n private String star;\n\n Rating(String star) {\n this.star = star;\n }\n\n @JsonValue\n public String getStar() {\n return star;\n }\n}\n",[59,99017,99018,99029,99041,99053,99065,99077,99089,99093,99100,99104,99116,99128,99132,99136,99143,99154,99160,99164],{"__ignoreMap":57},[62,99019,99020,99022,99024,99027],{"class":64,"line":65},[62,99021,116],{"class":68},[62,99023,94645],{"class":68},[62,99025,99026],{"class":122}," Rating",[62,99028,126],{"class":72},[62,99030,99031,99034,99036,99039],{"class":64,"line":76},[62,99032,99033],{"class":149}," FIVE_STARS",[62,99035,2109],{"class":72},[62,99037,99038],{"class":1675},"\"⭐️⭐️⭐️⭐️⭐️️️️\"",[62,99040,3324],{"class":72},[62,99042,99043,99046,99048,99051],{"class":64,"line":82},[62,99044,99045],{"class":149}," FOUR_STARS",[62,99047,2109],{"class":72},[62,99049,99050],{"class":1675},"\"⭐️⭐️⭐️⭐️\"",[62,99052,3324],{"class":72},[62,99054,99055,99058,99060,99063],{"class":64,"line":89},[62,99056,99057],{"class":149}," THREE_STARS",[62,99059,2109],{"class":72},[62,99061,99062],{"class":1675},"\"⭐️⭐️⭐️\"",[62,99064,3324],{"class":72},[62,99066,99067,99070,99072,99075],{"class":64,"line":95},[62,99068,99069],{"class":149}," TWO_STARS",[62,99071,2109],{"class":72},[62,99073,99074],{"class":1675},"\"⭐️⭐️\"",[62,99076,3324],{"class":72},[62,99078,99079,99082,99084,99087],{"class":64,"line":101},[62,99080,99081],{"class":149}," ONE_STAR",[62,99083,2109],{"class":72},[62,99085,99086],{"class":1675},"\"⭐️\"",[62,99088,1133],{"class":72},[62,99090,99091],{"class":64,"line":107},[62,99092,79],{"emptyLinePlaceholder":13},[62,99094,99095,99097],{"class":64,"line":113},[62,99096,137],{"class":68},[62,99098,99099],{"class":72}," String star;\n",[62,99101,99102],{"class":64,"line":129},[62,99103,79],{"emptyLinePlaceholder":13},[62,99105,99106,99109,99111,99114],{"class":64,"line":134},[62,99107,99108],{"class":122}," Rating",[62,99110,1049],{"class":72},[62,99112,99113],{"class":889},"star",[62,99115,768],{"class":72},[62,99117,99118,99120,99123,99125],{"class":64,"line":156},[62,99119,2405],{"class":149},[62,99121,99122],{"class":72},".star ",[62,99124,146],{"class":68},[62,99126,99127],{"class":72}," star;\n",[62,99129,99130],{"class":64,"line":161},[62,99131,223],{"class":72},[62,99133,99134],{"class":64,"line":167},[62,99135,79],{"emptyLinePlaceholder":13},[62,99137,99138,99140],{"class":64,"line":173},[62,99139,2143],{"class":72},[62,99141,99142],{"class":68},"JsonValue\n",[62,99144,99145,99147,99149,99152],{"class":64,"line":179},[62,99146,194],{"class":68},[62,99148,2469],{"class":72},[62,99150,99151],{"class":122},"getStar",[62,99153,206],{"class":72},[62,99155,99156,99158],{"class":64,"line":185},[62,99157,360],{"class":68},[62,99159,99127],{"class":72},[62,99161,99162],{"class":64,"line":191},[62,99163,223],{"class":72},[62,99165,99166],{"class":64,"line":209},[62,99167,379],{"class":72},[22,99169,99170,99171,99176],{},"Now that you have your data models in place you need a way to store data and retrieve it. You could go the route of Spring Data JPA and I just created ",[677,99172,99175],{"href":99173,"rel":99174},"https://www.danvega.dev/blog/2022/05/12/spring-data-jpa-pagination/",[681],"a tutorial on that here",". To keep this simple and less focused on the data layer you can just store the data in a collection.",[22,99178,99179],{},"Create a book and author repository class using the following code:",[52,99181,99183],{"className":54,"code":99182,"language":56,"meta":57,"style":57},"@Repository\npublic class BookRepository {\n\n private final AuthorRepository authorRepository;\n\n private List\u003CBook> books = new ArrayList\u003C>();\n\n public BookRepository(AuthorRepository authorRepository) {\n this.authorRepository = authorRepository;\n }\n\n public List\u003CBook> findAll() {\n return books;\n }\n\n public Book findOne(Integer id) {\n return books.stream()\n .filter(book -> book.id() == id)\n .findFirst().orElseThrow(() -> new RuntimeException(\"Book not found\"));\n }\n\n @PostConstruct\n private void init() {\n books.add(new Book(1,\n \"Reactive Spring\",\n 484,\n Rating.FIVE_STARS,\n authorRepository.findByName(\"Josh Long\")));\n books.add(new Book(2,\n \"Spring Boot Up & Running\",\n 328,\n Rating.FIVE_STARS,\n authorRepository.findByName(\"Mark Heckler\")));\n books.add(new Book(3,\n \"Hacking with Spring Boot 2.3\",\n 392,\n Rating.FIVE_STARS,\n authorRepository.findByName(\"Greg Turnquist\")));\n }\n\n}\n",[59,99184,99185,99191,99201,99205,99214,99218,99235,99239,99253,99265,99269,99273,99287,99294,99298,99302,99316,99326,99349,99375,99379,99383,99389,99399,99418,99425,99432,99437,99451,99469,99476,99483,99487,99500,99518,99525,99532,99536,99549,99553,99557],{"__ignoreMap":57},[62,99186,99187,99189],{"class":64,"line":65},[62,99188,942],{"class":72},[62,99190,23229],{"class":68},[62,99192,99193,99195,99197,99199],{"class":64,"line":76},[62,99194,116],{"class":68},[62,99196,119],{"class":68},[62,99198,8534],{"class":122},[62,99200,126],{"class":72},[62,99202,99203],{"class":64,"line":82},[62,99204,79],{"emptyLinePlaceholder":13},[62,99206,99207,99209,99211],{"class":64,"line":89},[62,99208,137],{"class":68},[62,99210,458],{"class":68},[62,99212,99213],{"class":72}," AuthorRepository authorRepository;\n",[62,99215,99216],{"class":64,"line":95},[62,99217,79],{"emptyLinePlaceholder":13},[62,99219,99220,99222,99224,99226,99229,99231,99233],{"class":64,"line":101},[62,99221,137],{"class":68},[62,99223,3079],{"class":72},[62,99225,8545],{"class":68},[62,99227,99228],{"class":72},"> books ",[62,99230,146],{"class":68},[62,99232,466],{"class":68},[62,99234,3091],{"class":72},[62,99236,99237],{"class":64,"line":107},[62,99238,79],{"emptyLinePlaceholder":13},[62,99240,99241,99243,99245,99248,99251],{"class":64,"line":113},[62,99242,9932],{"class":68},[62,99244,8534],{"class":122},[62,99246,99247],{"class":72},"(AuthorRepository ",[62,99249,99250],{"class":889},"authorRepository",[62,99252,768],{"class":72},[62,99254,99255,99257,99260,99262],{"class":64,"line":129},[62,99256,2405],{"class":149},[62,99258,99259],{"class":72},".authorRepository ",[62,99261,146],{"class":68},[62,99263,99264],{"class":72}," authorRepository;\n",[62,99266,99267],{"class":64,"line":134},[62,99268,223],{"class":72},[62,99270,99271],{"class":64,"line":156},[62,99272,79],{"emptyLinePlaceholder":13},[62,99274,99275,99277,99279,99281,99283,99285],{"class":64,"line":161},[62,99276,194],{"class":68},[62,99278,3079],{"class":72},[62,99280,8545],{"class":68},[62,99282,3135],{"class":72},[62,99284,10287],{"class":122},[62,99286,206],{"class":72},[62,99288,99289,99291],{"class":64,"line":167},[62,99290,360],{"class":68},[62,99292,99293],{"class":72}," books;\n",[62,99295,99296],{"class":64,"line":173},[62,99297,223],{"class":72},[62,99299,99300],{"class":64,"line":179},[62,99301,79],{"emptyLinePlaceholder":13},[62,99303,99304,99306,99308,99310,99312,99314],{"class":64,"line":185},[62,99305,194],{"class":68},[62,99307,83704],{"class":72},[62,99309,39503],{"class":122},[62,99311,8601],{"class":72},[62,99313,6283],{"class":889},[62,99315,768],{"class":72},[62,99317,99318,99320,99322,99324],{"class":64,"line":191},[62,99319,360],{"class":68},[62,99321,84120],{"class":72},[62,99323,2621],{"class":122},[62,99325,2223],{"class":72},[62,99327,99328,99330,99332,99335,99337,99340,99342,99344,99346],{"class":64,"line":209},[62,99329,2418],{"class":72},[62,99331,3216],{"class":122},[62,99333,99334],{"class":72},"(book ",[62,99336,800],{"class":68},[62,99338,99339],{"class":72}," book.",[62,99341,6283],{"class":122},[62,99343,5398],{"class":72},[62,99345,28328],{"class":68},[62,99347,99348],{"class":72}," id)\n",[62,99350,99351,99353,99355,99357,99359,99361,99363,99365,99368,99370,99373],{"class":64,"line":220},[62,99352,2418],{"class":72},[62,99354,3242],{"class":122},[62,99356,3229],{"class":72},[62,99358,47433],{"class":122},[62,99360,797],{"class":72},[62,99362,800],{"class":68},[62,99364,466],{"class":68},[62,99366,99367],{"class":122}," RuntimeException",[62,99369,2109],{"class":72},[62,99371,99372],{"class":1675},"\"Book not found\"",[62,99374,6979],{"class":72},[62,99376,99377],{"class":64,"line":226},[62,99378,223],{"class":72},[62,99380,99381],{"class":64,"line":231},[62,99382,79],{"emptyLinePlaceholder":13},[62,99384,99385,99387],{"class":64,"line":236},[62,99386,2143],{"class":72},[62,99388,3273],{"class":68},[62,99390,99391,99393,99395,99397],{"class":64,"line":242},[62,99392,137],{"class":68},[62,99394,200],{"class":68},[62,99396,3282],{"class":122},[62,99398,206],{"class":72},[62,99400,99401,99404,99406,99408,99410,99412,99414,99416],{"class":64,"line":247},[62,99402,99403],{"class":72}," books.",[62,99405,5806],{"class":122},[62,99407,2109],{"class":72},[62,99409,2426],{"class":68},[62,99411,8833],{"class":122},[62,99413,2109],{"class":72},[62,99415,6689],{"class":149},[62,99417,3338],{"class":72},[62,99419,99420,99423],{"class":64,"line":252},[62,99421,99422],{"class":1675}," \"Reactive Spring\"",[62,99424,3338],{"class":72},[62,99426,99427,99430],{"class":64,"line":257},[62,99428,99429],{"class":149}," 484",[62,99431,3338],{"class":72},[62,99433,99434],{"class":64,"line":271},[62,99435,99436],{"class":72}," Rating.FIVE_STARS,\n",[62,99438,99439,99442,99445,99447,99449],{"class":64,"line":281},[62,99440,99441],{"class":72}," authorRepository.",[62,99443,99444],{"class":122},"findByName",[62,99446,2109],{"class":72},[62,99448,83242],{"class":1675},[62,99450,47460],{"class":72},[62,99452,99453,99455,99457,99459,99461,99463,99465,99467],{"class":64,"line":286},[62,99454,99403],{"class":72},[62,99456,5806],{"class":122},[62,99458,2109],{"class":72},[62,99460,2426],{"class":68},[62,99462,8833],{"class":122},[62,99464,2109],{"class":72},[62,99466,5219],{"class":149},[62,99468,3338],{"class":72},[62,99470,99471,99474],{"class":64,"line":291},[62,99472,99473],{"class":1675}," \"Spring Boot Up & Running\"",[62,99475,3338],{"class":72},[62,99477,99478,99481],{"class":64,"line":296},[62,99479,99480],{"class":149}," 328",[62,99482,3338],{"class":72},[62,99484,99485],{"class":64,"line":302},[62,99486,99436],{"class":72},[62,99488,99489,99491,99493,99495,99498],{"class":64,"line":308},[62,99490,99441],{"class":72},[62,99492,99444],{"class":122},[62,99494,2109],{"class":72},[62,99496,99497],{"class":1675},"\"Mark Heckler\"",[62,99499,47460],{"class":72},[62,99501,99502,99504,99506,99508,99510,99512,99514,99516],{"class":64,"line":314},[62,99503,99403],{"class":72},[62,99505,5806],{"class":122},[62,99507,2109],{"class":72},[62,99509,2426],{"class":68},[62,99511,8833],{"class":122},[62,99513,2109],{"class":72},[62,99515,4472],{"class":149},[62,99517,3338],{"class":72},[62,99519,99520,99523],{"class":64,"line":320},[62,99521,99522],{"class":1675}," \"Hacking with Spring Boot 2.3\"",[62,99524,3338],{"class":72},[62,99526,99527,99530],{"class":64,"line":326},[62,99528,99529],{"class":149}," 392",[62,99531,3338],{"class":72},[62,99533,99534],{"class":64,"line":338},[62,99535,99436],{"class":72},[62,99537,99538,99540,99542,99544,99547],{"class":64,"line":343},[62,99539,99441],{"class":72},[62,99541,99444],{"class":122},[62,99543,2109],{"class":72},[62,99545,99546],{"class":1675},"\"Greg Turnquist\"",[62,99548,47460],{"class":72},[62,99550,99551],{"class":64,"line":357},[62,99552,223],{"class":72},[62,99554,99555],{"class":64,"line":366},[62,99556,79],{"emptyLinePlaceholder":13},[62,99558,99559],{"class":64,"line":371},[62,99560,379],{"class":72},[22,99562,99563,99564,99566],{},"If you’re not aware the ",[59,99565,3393],{}," annotation is used on a method that needs to be executed after dependency injection is done to perform any initialization.",[52,99568,99570],{"className":54,"code":99569,"language":56,"meta":57,"style":57},"@Repository\npublic class AuthorRepository {\n\n private List\u003CAuthor> authors = new ArrayList\u003C>();\n\n public List\u003CAuthor> findAll() {\n return authors;\n }\n\n public Author findById(int id) {\n return authors.stream()\n .filter(author -> author.id() == id)\n .findFirst()\n .orElseThrow(() -> new RuntimeException(\"Author not found\"));\n }\n\n public Author findByName(String name) {\n return authors.stream()\n .filter(author -> author.fullName().equals(name))\n .findFirst()\n .orElseThrow(() -> new RuntimeException(\"Author not found\"));\n }\n\n @PostConstruct\n private void init() {\n authors.add(new Author(1,\"Josh\",\"Long\"));\n authors.add(new Author(2,\"Mark\",\"Heckler\"));\n authors.add(new Author(3,\"Greg\",\"Turnquist\"));\n }\n}\n",[59,99571,99572,99578,99589,99593,99611,99615,99629,99636,99640,99644,99661,99672,99694,99702,99723,99727,99731,99745,99755,99775,99783,99803,99807,99811,99817,99827,99855,99883,99911,99915],{"__ignoreMap":57},[62,99573,99574,99576],{"class":64,"line":65},[62,99575,942],{"class":72},[62,99577,23229],{"class":68},[62,99579,99580,99582,99584,99587],{"class":64,"line":76},[62,99581,116],{"class":68},[62,99583,119],{"class":68},[62,99585,99586],{"class":122}," AuthorRepository",[62,99588,126],{"class":72},[62,99590,99591],{"class":64,"line":82},[62,99592,79],{"emptyLinePlaceholder":13},[62,99594,99595,99597,99599,99602,99605,99607,99609],{"class":64,"line":89},[62,99596,137],{"class":68},[62,99598,3079],{"class":72},[62,99600,99601],{"class":68},"Author",[62,99603,99604],{"class":72},"> authors ",[62,99606,146],{"class":68},[62,99608,466],{"class":68},[62,99610,3091],{"class":72},[62,99612,99613],{"class":64,"line":95},[62,99614,79],{"emptyLinePlaceholder":13},[62,99616,99617,99619,99621,99623,99625,99627],{"class":64,"line":101},[62,99618,194],{"class":68},[62,99620,3079],{"class":72},[62,99622,99601],{"class":68},[62,99624,3135],{"class":72},[62,99626,10287],{"class":122},[62,99628,206],{"class":72},[62,99630,99631,99633],{"class":64,"line":107},[62,99632,360],{"class":68},[62,99634,99635],{"class":72}," authors;\n",[62,99637,99638],{"class":64,"line":113},[62,99639,223],{"class":72},[62,99641,99642],{"class":64,"line":129},[62,99643,79],{"emptyLinePlaceholder":13},[62,99645,99646,99648,99651,99653,99655,99657,99659],{"class":64,"line":134},[62,99647,194],{"class":68},[62,99649,99650],{"class":72}," Author ",[62,99652,38763],{"class":122},[62,99654,2109],{"class":72},[62,99656,747],{"class":68},[62,99658,20831],{"class":889},[62,99660,768],{"class":72},[62,99662,99663,99665,99668,99670],{"class":64,"line":156},[62,99664,360],{"class":68},[62,99666,99667],{"class":72}," authors.",[62,99669,2621],{"class":122},[62,99671,2223],{"class":72},[62,99673,99674,99676,99678,99681,99683,99686,99688,99690,99692],{"class":64,"line":161},[62,99675,2418],{"class":72},[62,99677,3216],{"class":122},[62,99679,99680],{"class":72},"(author ",[62,99682,800],{"class":68},[62,99684,99685],{"class":72}," author.",[62,99687,6283],{"class":122},[62,99689,5398],{"class":72},[62,99691,28328],{"class":68},[62,99693,99348],{"class":72},[62,99695,99696,99698,99700],{"class":64,"line":167},[62,99697,2418],{"class":72},[62,99699,3242],{"class":122},[62,99701,2223],{"class":72},[62,99703,99704,99706,99708,99710,99712,99714,99716,99718,99721],{"class":64,"line":173},[62,99705,2418],{"class":72},[62,99707,47433],{"class":122},[62,99709,797],{"class":72},[62,99711,800],{"class":68},[62,99713,466],{"class":68},[62,99715,99367],{"class":122},[62,99717,2109],{"class":72},[62,99719,99720],{"class":1675},"\"Author not found\"",[62,99722,6979],{"class":72},[62,99724,99725],{"class":64,"line":179},[62,99726,223],{"class":72},[62,99728,99729],{"class":64,"line":185},[62,99730,79],{"emptyLinePlaceholder":13},[62,99732,99733,99735,99737,99739,99741,99743],{"class":64,"line":191},[62,99734,194],{"class":68},[62,99736,99650],{"class":72},[62,99738,99444],{"class":122},[62,99740,1049],{"class":72},[62,99742,3107],{"class":889},[62,99744,768],{"class":72},[62,99746,99747,99749,99751,99753],{"class":64,"line":209},[62,99748,360],{"class":68},[62,99750,99667],{"class":72},[62,99752,2621],{"class":122},[62,99754,2223],{"class":72},[62,99756,99757,99759,99761,99763,99765,99767,99769,99771,99773],{"class":64,"line":220},[62,99758,2418],{"class":72},[62,99760,3216],{"class":122},[62,99762,99680],{"class":72},[62,99764,800],{"class":68},[62,99766,99685],{"class":72},[62,99768,98981],{"class":122},[62,99770,3229],{"class":72},[62,99772,3232],{"class":122},[62,99774,32488],{"class":72},[62,99776,99777,99779,99781],{"class":64,"line":226},[62,99778,2418],{"class":72},[62,99780,3242],{"class":122},[62,99782,2223],{"class":72},[62,99784,99785,99787,99789,99791,99793,99795,99797,99799,99801],{"class":64,"line":231},[62,99786,2418],{"class":72},[62,99788,47433],{"class":122},[62,99790,797],{"class":72},[62,99792,800],{"class":68},[62,99794,466],{"class":68},[62,99796,99367],{"class":122},[62,99798,2109],{"class":72},[62,99800,99720],{"class":1675},[62,99802,6979],{"class":72},[62,99804,99805],{"class":64,"line":236},[62,99806,223],{"class":72},[62,99808,99809],{"class":64,"line":242},[62,99810,79],{"emptyLinePlaceholder":13},[62,99812,99813,99815],{"class":64,"line":247},[62,99814,2143],{"class":72},[62,99816,3273],{"class":68},[62,99818,99819,99821,99823,99825],{"class":64,"line":252},[62,99820,137],{"class":68},[62,99822,200],{"class":68},[62,99824,3282],{"class":122},[62,99826,206],{"class":72},[62,99828,99829,99832,99834,99836,99838,99840,99842,99844,99846,99848,99850,99853],{"class":64,"line":257},[62,99830,99831],{"class":72}," authors.",[62,99833,5806],{"class":122},[62,99835,2109],{"class":72},[62,99837,2426],{"class":68},[62,99839,98965],{"class":122},[62,99841,2109],{"class":72},[62,99843,6689],{"class":149},[62,99845,32225],{"class":72},[62,99847,32331],{"class":1675},[62,99849,32225],{"class":72},[62,99851,99852],{"class":1675},"\"Long\"",[62,99854,6979],{"class":72},[62,99856,99857,99859,99861,99863,99865,99867,99869,99871,99873,99876,99878,99881],{"class":64,"line":271},[62,99858,99831],{"class":72},[62,99860,5806],{"class":122},[62,99862,2109],{"class":72},[62,99864,2426],{"class":68},[62,99866,98965],{"class":122},[62,99868,2109],{"class":72},[62,99870,5219],{"class":149},[62,99872,32225],{"class":72},[62,99874,99875],{"class":1675},"\"Mark\"",[62,99877,32225],{"class":72},[62,99879,99880],{"class":1675},"\"Heckler\"",[62,99882,6979],{"class":72},[62,99884,99885,99887,99889,99891,99893,99895,99897,99899,99901,99904,99906,99909],{"class":64,"line":281},[62,99886,99831],{"class":72},[62,99888,5806],{"class":122},[62,99890,2109],{"class":72},[62,99892,2426],{"class":68},[62,99894,98965],{"class":122},[62,99896,2109],{"class":72},[62,99898,4472],{"class":149},[62,99900,32225],{"class":72},[62,99902,99903],{"class":1675},"\"Greg\"",[62,99905,32225],{"class":72},[62,99907,99908],{"class":1675},"\"Turnquist\"",[62,99910,6979],{"class":72},[62,99912,99913],{"class":64,"line":286},[62,99914,223],{"class":72},[62,99916,99917],{"class":64,"line":291},[62,99918,379],{"class":72},[636,99920,99922],{"id":99921},"graphql-layer","GraphQL Layer",[22,99924,99925,99926,99928,99929,99931],{},"With your data layer in place it’s time to turn your attention to GraphQL. By convention Spring will look for GraphQL Schema’s in the ",[59,99927,46280],{}," directory. You can configure this location but for this example let’s just use the default by creating a new file ",[59,99930,46276],{}," in that directory.",[22,99933,99934,99935,99940],{},"You will start by defining your object types which look like the model objects you create in Java above. The field names are the left and the types are on the right. There are ",[677,99936,99939],{"href":99937,"rel":99938},"https://graphql.org/learn/schema/",[681],"built-in scalar types"," like ID, String, Int, Float, and Boolean and you can also reference object types that you have created.",[52,99942,99944],{"className":8822,"code":99943,"language":8824,"meta":57,"style":57},"type Book {\n id: ID!\n title: String\n pages: Int\n rating: Rating\n author: Author\n}\n\ntype Rating {\n rating: String\n star: String\n}\n\ntype Author {\n id: ID!\n firstName: String\n lastName: String\n books: [Book]\n}\n",[59,99945,99946,99954,99964,99972,99980,99988,99996,100000,100004,100012,100020,100029,100033,100037,100045,100055,100064,100073,100084],{"__ignoreMap":57},[62,99947,99948,99950,99952],{"class":64,"line":65},[62,99949,1947],{"class":68},[62,99951,8833],{"class":149},[62,99953,126],{"class":72},[62,99955,99956,99958,99960,99962],{"class":64,"line":76},[62,99957,98500],{"class":889},[62,99959,3696],{"class":72},[62,99961,8845],{"class":149},[62,99963,8848],{"class":68},[62,99965,99966,99968,99970],{"class":64,"line":82},[62,99967,50719],{"class":889},[62,99969,3696],{"class":72},[62,99971,8858],{"class":149},[62,99973,99974,99976,99978],{"class":64,"line":89},[62,99975,98519],{"class":889},[62,99977,3696],{"class":72},[62,99979,8877],{"class":149},[62,99981,99982,99984,99986],{"class":64,"line":95},[62,99983,98528],{"class":889},[62,99985,3696],{"class":72},[62,99987,98533],{"class":149},[62,99989,99990,99992,99994],{"class":64,"line":101},[62,99991,98538],{"class":889},[62,99993,3696],{"class":72},[62,99995,98543],{"class":149},[62,99997,99998],{"class":64,"line":107},[62,99999,379],{"class":72},[62,100001,100002],{"class":64,"line":113},[62,100003,79],{"emptyLinePlaceholder":13},[62,100005,100006,100008,100010],{"class":64,"line":129},[62,100007,1947],{"class":68},[62,100009,99026],{"class":149},[62,100011,126],{"class":72},[62,100013,100014,100016,100018],{"class":64,"line":134},[62,100015,98528],{"class":889},[62,100017,3696],{"class":72},[62,100019,8858],{"class":149},[62,100021,100022,100025,100027],{"class":64,"line":156},[62,100023,100024],{"class":889}," star",[62,100026,3696],{"class":72},[62,100028,8858],{"class":149},[62,100030,100031],{"class":64,"line":161},[62,100032,379],{"class":72},[62,100034,100035],{"class":64,"line":167},[62,100036,79],{"emptyLinePlaceholder":13},[62,100038,100039,100041,100043],{"class":64,"line":173},[62,100040,1947],{"class":68},[62,100042,98965],{"class":149},[62,100044,126],{"class":72},[62,100046,100047,100049,100051,100053],{"class":64,"line":179},[62,100048,98500],{"class":889},[62,100050,3696],{"class":72},[62,100052,8845],{"class":149},[62,100054,8848],{"class":68},[62,100056,100057,100060,100062],{"class":64,"line":185},[62,100058,100059],{"class":889}," firstName",[62,100061,3696],{"class":72},[62,100063,8858],{"class":149},[62,100065,100066,100069,100071],{"class":64,"line":191},[62,100067,100068],{"class":889}," lastName",[62,100070,3696],{"class":72},[62,100072,8858],{"class":149},[62,100074,100075,100078,100080,100082],{"class":64,"line":209},[62,100076,100077],{"class":889}," books",[62,100079,36721],{"class":72},[62,100081,8545],{"class":149},[62,100083,8959],{"class":72},[62,100085,100086],{"class":64,"line":220},[62,100087,379],{"class":72},[22,100089,100090,100091,100094],{},"Next you will create a root level operation called Query. You will name the query ",[59,100092,100093],{},"allBooks"," and this will return a list of books:",[52,100096,100098],{"className":8822,"code":100097,"language":8824,"meta":57,"style":57},"type Query {\n allBooks: [Book]\n}\n",[59,100099,100100,100108,100118],{"__ignoreMap":57},[62,100101,100102,100104,100106],{"class":64,"line":65},[62,100103,1947],{"class":68},[62,100105,8934],{"class":149},[62,100107,126],{"class":72},[62,100109,100110,100112,100114,100116],{"class":64,"line":76},[62,100111,98399],{"class":889},[62,100113,36721],{"class":72},[62,100115,8545],{"class":149},[62,100117,8959],{"class":72},[62,100119,100120],{"class":64,"line":82},[62,100121,379],{"class":72},[636,100123,100125],{"id":100124},"the-web-layer","The Web Layer",[22,100127,100128],{},"With your GraphQL Schema in place you need a transport layer for your API. In other words, how are clients going to access your API? In this example you selected Spring Web (Spring MVC) as a dependency so you can use HTTP.",[22,100130,35770,100131,100133,100134,100136,100137,100139,100140,100144],{},[59,100132,83434],{}," and mark with the ",[59,100135,55837],{}," annotation. You will need a way to retrieve your data so create a new field of type ",[59,100138,82823],{}," and use ",[677,100141,100143],{"href":56186,"rel":100142},[681],"constructor injection"," to get an instance of that injected into your controller.",[52,100146,100148],{"className":54,"code":100147,"language":56,"meta":57,"style":57},"@Controller\npublic class BookController {\n\n private final BookRepository bookRepository;\n\n public BookController(BookRepository bookRepository) {\n this.bookRepository = bookRepository;\n }\n}\n",[59,100149,100150,100156,100166,100170,100178,100182,100194,100204,100208],{"__ignoreMap":57},[62,100151,100152,100154],{"class":64,"line":65},[62,100153,942],{"class":72},[62,100155,16624],{"class":68},[62,100157,100158,100160,100162,100164],{"class":64,"line":76},[62,100159,116],{"class":68},[62,100161,119],{"class":68},[62,100163,18573],{"class":122},[62,100165,126],{"class":72},[62,100167,100168],{"class":64,"line":82},[62,100169,79],{"emptyLinePlaceholder":13},[62,100171,100172,100174,100176],{"class":64,"line":89},[62,100173,137],{"class":68},[62,100175,458],{"class":68},[62,100177,83493],{"class":72},[62,100179,100180],{"class":64,"line":95},[62,100181,79],{"emptyLinePlaceholder":13},[62,100183,100184,100186,100188,100190,100192],{"class":64,"line":101},[62,100185,194],{"class":68},[62,100187,18573],{"class":122},[62,100189,83506],{"class":72},[62,100191,83509],{"class":889},[62,100193,768],{"class":72},[62,100195,100196,100198,100200,100202],{"class":64,"line":107},[62,100197,2405],{"class":149},[62,100199,83518],{"class":72},[62,100201,146],{"class":68},[62,100203,83523],{"class":72},[62,100205,100206],{"class":64,"line":113},[62,100207,223],{"class":72},[62,100209,100210],{"class":64,"line":129},[62,100211,379],{"class":72},[22,100213,100214,100215,100217],{},"Next create a method called ",[59,100216,10287],{}," that will delegate to the repository to return all of the books in the collection:",[52,100219,100221],{"className":54,"code":100220,"language":56,"meta":57,"style":57},"@Controller\npublic class BookController {\n\n private final BookRepository bookRepository;\n\n public BookController(BookRepository bookRepository) {\n this.bookRepository = bookRepository;\n }\n\n public List\u003CBook> findAll() {\n return bookRepository.findAll();\n }\n}\n",[59,100222,100223,100229,100239,100243,100251,100255,100267,100277,100281,100285,100299,100309,100313],{"__ignoreMap":57},[62,100224,100225,100227],{"class":64,"line":65},[62,100226,942],{"class":72},[62,100228,16624],{"class":68},[62,100230,100231,100233,100235,100237],{"class":64,"line":76},[62,100232,116],{"class":68},[62,100234,119],{"class":68},[62,100236,18573],{"class":122},[62,100238,126],{"class":72},[62,100240,100241],{"class":64,"line":82},[62,100242,79],{"emptyLinePlaceholder":13},[62,100244,100245,100247,100249],{"class":64,"line":89},[62,100246,137],{"class":68},[62,100248,458],{"class":68},[62,100250,83493],{"class":72},[62,100252,100253],{"class":64,"line":95},[62,100254,79],{"emptyLinePlaceholder":13},[62,100256,100257,100259,100261,100263,100265],{"class":64,"line":101},[62,100258,194],{"class":68},[62,100260,18573],{"class":122},[62,100262,83506],{"class":72},[62,100264,83509],{"class":889},[62,100266,768],{"class":72},[62,100268,100269,100271,100273,100275],{"class":64,"line":107},[62,100270,2405],{"class":149},[62,100272,83518],{"class":72},[62,100274,146],{"class":68},[62,100276,83523],{"class":72},[62,100278,100279],{"class":64,"line":113},[62,100280,223],{"class":72},[62,100282,100283],{"class":64,"line":129},[62,100284,79],{"emptyLinePlaceholder":13},[62,100286,100287,100289,100291,100293,100295,100297],{"class":64,"line":134},[62,100288,194],{"class":68},[62,100290,3079],{"class":72},[62,100292,8545],{"class":68},[62,100294,3135],{"class":72},[62,100296,10287],{"class":122},[62,100298,206],{"class":72},[62,100300,100301,100303,100305,100307],{"class":64,"line":156},[62,100302,360],{"class":68},[62,100304,83559],{"class":72},[62,100306,10287],{"class":122},[62,100308,822],{"class":72},[62,100310,100311],{"class":64,"line":161},[62,100312,223],{"class":72},[62,100314,100315],{"class":64,"line":167},[62,100316,379],{"class":72},[22,100318,100319,100320,100322,100323,100325],{},"With that method in place you need a way to map that method to the GraphQL Schema you created earlier. How can you tell Spring that when the query named ",[59,100321,100093],{}," is called to execute the ",[59,100324,10287],{}," method?",[52,100327,100328],{"className":8822,"code":100097,"language":8824,"meta":57,"style":57},[59,100329,100330,100338,100348],{"__ignoreMap":57},[62,100331,100332,100334,100336],{"class":64,"line":65},[62,100333,1947],{"class":68},[62,100335,8934],{"class":149},[62,100337,126],{"class":72},[62,100339,100340,100342,100344,100346],{"class":64,"line":76},[62,100341,98399],{"class":889},[62,100343,36721],{"class":72},[62,100345,8545],{"class":149},[62,100347,8959],{"class":72},[62,100349,100350],{"class":64,"line":82},[62,100351,379],{"class":72},[22,100353,100354,100355,100358],{},"There are annotations that are going to help us with the schema mappings. The first one is called ",[59,100356,100357],{},"@SchemaMapping"," and you will need to supply 2 arguments. Type name is the top level operation like Query, Mutation or Subscription. Value is the name of the query as its defined in the GraphQL schema.",[52,100360,100362],{"className":54,"code":100361,"language":56,"meta":57,"style":57},"@Controller\npublic class BookController {\n\n private final BookRepository bookRepository;\n\n public BookController(BookRepository bookRepository) {\n this.bookRepository = bookRepository;\n }\n\n @SchemaMapping(typeName = \"Query\",value = \"allBooks\")\n public List\u003CBook> findAll() {\n return bookRepository.findAll();\n }\n}\n",[59,100363,100364,100370,100380,100384,100392,100396,100408,100418,100422,100426,100454,100468,100478,100482],{"__ignoreMap":57},[62,100365,100366,100368],{"class":64,"line":65},[62,100367,942],{"class":72},[62,100369,16624],{"class":68},[62,100371,100372,100374,100376,100378],{"class":64,"line":76},[62,100373,116],{"class":68},[62,100375,119],{"class":68},[62,100377,18573],{"class":122},[62,100379,126],{"class":72},[62,100381,100382],{"class":64,"line":82},[62,100383,79],{"emptyLinePlaceholder":13},[62,100385,100386,100388,100390],{"class":64,"line":89},[62,100387,137],{"class":68},[62,100389,458],{"class":68},[62,100391,83493],{"class":72},[62,100393,100394],{"class":64,"line":95},[62,100395,79],{"emptyLinePlaceholder":13},[62,100397,100398,100400,100402,100404,100406],{"class":64,"line":101},[62,100399,194],{"class":68},[62,100401,18573],{"class":122},[62,100403,83506],{"class":72},[62,100405,83509],{"class":889},[62,100407,768],{"class":72},[62,100409,100410,100412,100414,100416],{"class":64,"line":107},[62,100411,2405],{"class":149},[62,100413,83518],{"class":72},[62,100415,146],{"class":68},[62,100417,83523],{"class":72},[62,100419,100420],{"class":64,"line":113},[62,100421,223],{"class":72},[62,100423,100424],{"class":64,"line":129},[62,100425,79],{"emptyLinePlaceholder":13},[62,100427,100428,100430,100433,100435,100438,100440,100443,100445,100447,100449,100452],{"class":64,"line":134},[62,100429,2143],{"class":72},[62,100431,100432],{"class":68},"SchemaMapping",[62,100434,2109],{"class":72},[62,100436,100437],{"class":149},"typeName",[62,100439,2556],{"class":68},[62,100441,100442],{"class":1675}," \"Query\"",[62,100444,32225],{"class":72},[62,100446,2553],{"class":149},[62,100448,2556],{"class":68},[62,100450,100451],{"class":1675}," \"allBooks\"",[62,100453,2212],{"class":72},[62,100455,100456,100458,100460,100462,100464,100466],{"class":64,"line":156},[62,100457,194],{"class":68},[62,100459,3079],{"class":72},[62,100461,8545],{"class":68},[62,100463,3135],{"class":72},[62,100465,10287],{"class":122},[62,100467,206],{"class":72},[62,100469,100470,100472,100474,100476],{"class":64,"line":161},[62,100471,360],{"class":68},[62,100473,83559],{"class":72},[62,100475,10287],{"class":122},[62,100477,822],{"class":72},[62,100479,100480],{"class":64,"line":167},[62,100481,223],{"class":72},[62,100483,100484],{"class":64,"line":173},[62,100485,379],{"class":72},[22,100487,100488],{},"This is the minimum you need to get a GraphQL API up and running. You need a way to test what you have done so far and you could write some tests but we will look at that later.",[636,100490,100492],{"id":100491},"graphiql","GraphiQL",[22,100494,100495,100496,100498],{},"GraphiQL is a graphical interactive in-browser GraphQL IDE. This is a great place to learn and test out your GraphQL queries. GraphiQL is disabled by default so if you would like to use it you will need to open up ",[59,100497,41028],{}," and enable it with the following property:",[52,100500,100501],{"className":1663,"code":9133,"language":1665,"meta":57,"style":57},[59,100502,100503],{"__ignoreMap":57},[62,100504,100505,100507,100509],{"class":64,"line":65},[62,100506,9140],{"class":122},[62,100508,146],{"class":1675},[62,100510,51914],{"class":149},[22,100512,100513,100514,100517],{},"With that in place you can run your application and visit ",[677,100515,83577],{"href":83577,"rel":100516},[681],". The left pane is for writing queries and the right pane will display your results. The first thing you will notice is that you get some help context on how to start writing queries.",[22,100519,100520],{},[653,100521],{"alt":100522,"src":100523},"graphiql.png","/images/blog/2022/05/17/graphiql.png",[22,100525,100526,100527,100529],{},"One of the features I really love about GraphQL is that it’s self documenting. If you look on the far right pane you will see a documentation explorer. The root types (Query, Mutation & Subscription) are listed if you click on the right side ",[59,100528,83344],{}," you will be given a list of queries defined on the schema. Right now you only have one and it returns a collection of books. If you click on Book you can see what fields are available along with their types.",[22,100531,100532,100533,100536],{},"Because of that schema you will also get intellisense as you start writing queries. A query starts with ",[59,100534,100535],{},"query {"," type that and on the next line type the letter a:",[52,100538,100540],{"className":8822,"code":100539,"language":8824,"meta":57,"style":57},"query {\n a\n}\n",[59,100541,100542,100548,100553],{"__ignoreMap":57},[62,100543,100544,100546],{"class":64,"line":65},[62,100545,9155],{"class":68},[62,100547,126],{"class":72},[62,100549,100550],{"class":64,"line":76},[62,100551,100552],{"class":889}," a\n",[62,100554,100555],{"class":64,"line":82},[62,100556,379],{"class":72},[22,100558,100559,100560,100562],{},"You should be presented with a drop down with a value of ",[59,100561,100093],{},". Based on the schema the UI here knows what queries are available to you. This is why you will hear a lot of people praise the developer experience for GraphQL and I am in that group of people 👏🏻",[22,100564,100565],{},"Paste the following query in the left pane and hit the play button above to execute it:",[52,100567,100568],{"className":8822,"code":98386,"language":8824,"meta":57,"style":57},[59,100569,100570,100576,100582,100586,100590,100596,100600,100604,100610,100614,100618,100622,100626],{"__ignoreMap":57},[62,100571,100572,100574],{"class":64,"line":65},[62,100573,9155],{"class":68},[62,100575,126],{"class":72},[62,100577,100578,100580],{"class":64,"line":76},[62,100579,98399],{"class":889},[62,100581,126],{"class":72},[62,100583,100584],{"class":64,"line":82},[62,100585,83608],{"class":889},[62,100587,100588],{"class":64,"line":89},[62,100589,83613],{"class":889},[62,100591,100592,100594],{"class":64,"line":95},[62,100593,98414],{"class":889},[62,100595,126],{"class":72},[62,100597,100598],{"class":64,"line":101},[62,100599,98421],{"class":889},[62,100601,100602],{"class":64,"line":107},[62,100603,223],{"class":72},[62,100605,100606,100608],{"class":64,"line":113},[62,100607,8863],{"class":889},[62,100609,126],{"class":72},[62,100611,100612],{"class":64,"line":129},[62,100613,98436],{"class":889},[62,100615,100616],{"class":64,"line":134},[62,100617,98441],{"class":889},[62,100619,100620],{"class":64,"line":156},[62,100621,223],{"class":72},[62,100623,100624],{"class":64,"line":161},[62,100625,3731],{"class":72},[62,100627,100628],{"class":64,"line":167},[62,100629,379],{"class":72},[22,100631,100632],{},[653,100633],{"alt":100634,"src":100635},"books-execute-query.png","/images/blog/2022/05/17/books-execute-query.png",[22,100637,100638,100639,100641],{},"Now that you can fetch all of the records in a collection how about finding a single record. In your schema you will need to define a new query with the name ",[59,100640,39503],{}," but this time it will take an argument. This argument is named id, has a type of ID and will return a single Book.",[52,100643,100645],{"className":54,"code":100644,"language":56,"meta":57,"style":57},"type Query {\n allBooks: [Book]\n findOne(id: ID!): Book\n}\n",[59,100646,100647,100651,100661,100683],{"__ignoreMap":57},[62,100648,100649],{"class":64,"line":65},[62,100650,83419],{"class":72},[62,100652,100653,100656,100658],{"class":64,"line":76},[62,100654,100655],{"class":72}," allBooks",[62,100657,1266],{"class":68},[62,100659,100660],{"class":72}," [Book]\n",[62,100662,100663,100666,100669,100671,100674,100676,100678,100680],{"class":64,"line":82},[62,100664,100665],{"class":122}," findOne",[62,100667,100668],{"class":72},"(id",[62,100670,1266],{"class":68},[62,100672,100673],{"class":72}," ID",[62,100675,6277],{"class":68},[62,100677,2712],{"class":72},[62,100679,1266],{"class":68},[62,100681,100682],{"class":72}," Book\n",[62,100684,100685],{"class":64,"line":89},[62,100686,379],{"class":72},[22,100688,100689,100690,100692,100693,2755],{},"If you head back over to the ",[59,100691,83434],{}," you can define a new method called ",[59,100694,39503],{},[52,100696,100698],{"className":54,"code":100697,"language":56,"meta":57,"style":57},"@Controller\npublic class BookController {\n\n private final BookRepository bookRepository;\n\n public BookController(BookRepository bookRepository) {\n this.bookRepository = bookRepository;\n }\n\n @SchemaMapping(typeName = \"Query\",value = \"allBooks\")\n public List\u003CBook> findAll() {\n return bookRepository.findAll();\n }\n\n public Book findOne(@Argument Integer id) {\n return bookRepository.findOne(id);\n }\n}\n",[59,100699,100700,100706,100716,100720,100728,100732,100744,100754,100758,100762,100786,100800,100810,100814,100818,100836,100846,100850],{"__ignoreMap":57},[62,100701,100702,100704],{"class":64,"line":65},[62,100703,942],{"class":72},[62,100705,16624],{"class":68},[62,100707,100708,100710,100712,100714],{"class":64,"line":76},[62,100709,116],{"class":68},[62,100711,119],{"class":68},[62,100713,18573],{"class":122},[62,100715,126],{"class":72},[62,100717,100718],{"class":64,"line":82},[62,100719,79],{"emptyLinePlaceholder":13},[62,100721,100722,100724,100726],{"class":64,"line":89},[62,100723,137],{"class":68},[62,100725,458],{"class":68},[62,100727,83493],{"class":72},[62,100729,100730],{"class":64,"line":95},[62,100731,79],{"emptyLinePlaceholder":13},[62,100733,100734,100736,100738,100740,100742],{"class":64,"line":101},[62,100735,194],{"class":68},[62,100737,18573],{"class":122},[62,100739,83506],{"class":72},[62,100741,83509],{"class":889},[62,100743,768],{"class":72},[62,100745,100746,100748,100750,100752],{"class":64,"line":107},[62,100747,2405],{"class":149},[62,100749,83518],{"class":72},[62,100751,146],{"class":68},[62,100753,83523],{"class":72},[62,100755,100756],{"class":64,"line":113},[62,100757,223],{"class":72},[62,100759,100760],{"class":64,"line":129},[62,100761,79],{"emptyLinePlaceholder":13},[62,100763,100764,100766,100768,100770,100772,100774,100776,100778,100780,100782,100784],{"class":64,"line":134},[62,100765,2143],{"class":72},[62,100767,100432],{"class":68},[62,100769,2109],{"class":72},[62,100771,100437],{"class":149},[62,100773,2556],{"class":68},[62,100775,100442],{"class":1675},[62,100777,32225],{"class":72},[62,100779,2553],{"class":149},[62,100781,2556],{"class":68},[62,100783,100451],{"class":1675},[62,100785,2212],{"class":72},[62,100787,100788,100790,100792,100794,100796,100798],{"class":64,"line":156},[62,100789,194],{"class":68},[62,100791,3079],{"class":72},[62,100793,8545],{"class":68},[62,100795,3135],{"class":72},[62,100797,10287],{"class":122},[62,100799,206],{"class":72},[62,100801,100802,100804,100806,100808],{"class":64,"line":161},[62,100803,360],{"class":68},[62,100805,83559],{"class":72},[62,100807,10287],{"class":122},[62,100809,822],{"class":72},[62,100811,100812],{"class":64,"line":167},[62,100813,223],{"class":72},[62,100815,100816],{"class":64,"line":173},[62,100817,79],{"emptyLinePlaceholder":13},[62,100819,100820,100822,100824,100826,100828,100830,100832,100834],{"class":64,"line":179},[62,100821,194],{"class":68},[62,100823,83704],{"class":72},[62,100825,39503],{"class":122},[62,100827,2475],{"class":72},[62,100829,83711],{"class":68},[62,100831,39510],{"class":72},[62,100833,6283],{"class":889},[62,100835,768],{"class":72},[62,100837,100838,100840,100842,100844],{"class":64,"line":185},[62,100839,360],{"class":68},[62,100841,83559],{"class":72},[62,100843,39503],{"class":122},[62,100845,23764],{"class":72},[62,100847,100848],{"class":64,"line":191},[62,100849,223],{"class":72},[62,100851,100852],{"class":64,"line":209},[62,100853,379],{"class":72},[22,100855,100856,100857,100860,100861,100864],{},"The first thing you will notice is a new annotation ",[59,100858,100859],{},"@Argument."," In the web world if you wanted to bind a method argument to a request parameter you would use the ",[59,100862,100863],{},"@RequestParam"," annotation. This is similar but you aren’t working with request parameters you’re working with arguments. This annotation will bind a named GraphQL argument onto a method parameter.",[22,100866,100867,100868,100870,100871,43329],{},"Remember that to map the GraphQL schema query named ",[59,100869,39503],{}," to this method you need to add a ",[59,100872,100357],{},[52,100874,100876],{"className":54,"code":100875,"language":56,"meta":57,"style":57},"@SchemaMapping(typeName = \"Query\", value=\"findOne\")\npublic Book findOne(@Argument Integer id) {\n return bookRepository.findOne(id);\n}\n",[59,100877,100878,100903,100918,100928],{"__ignoreMap":57},[62,100879,100880,100882,100884,100886,100888,100890,100892,100894,100896,100898,100901],{"class":64,"line":65},[62,100881,942],{"class":72},[62,100883,100432],{"class":68},[62,100885,2109],{"class":72},[62,100887,100437],{"class":149},[62,100889,2556],{"class":68},[62,100891,100442],{"class":1675},[62,100893,976],{"class":72},[62,100895,2553],{"class":149},[62,100897,146],{"class":68},[62,100899,100900],{"class":1675},"\"findOne\"",[62,100902,2212],{"class":72},[62,100904,100905,100907,100909,100911,100913,100915],{"class":64,"line":76},[62,100906,116],{"class":68},[62,100908,83704],{"class":72},[62,100910,39503],{"class":122},[62,100912,2475],{"class":72},[62,100914,83711],{"class":68},[62,100916,100917],{"class":72}," Integer id) {\n",[62,100919,100920,100922,100924,100926],{"class":64,"line":82},[62,100921,2599],{"class":68},[62,100923,83559],{"class":72},[62,100925,39503],{"class":122},[62,100927,23764],{"class":72},[62,100929,100930],{"class":64,"line":89},[62,100931,379],{"class":72},[22,100933,100934,100935,100937,100938,976,100940,100942,100943,976,100945,4201,100947,43654,100950,100952,100953,43329],{},"This is a little verbose and lucky for you there is an alternative. In the web world you have specialized annotations of ",[59,100936,56483],{}," like ",[59,100939,48908],{},[59,100941,48911],{}," and so on. In GraphQL there are special annotations for root types like ",[59,100944,83441],{},[59,100946,83680],{},[59,100948,100949],{},"@SubscriptionMapping",[59,100951,83441],{}," is a composed annotation that acts as a shortcut for @SchemaMapping with typeName=\"Query\". The trick here is that the value is derived from the method name. This means that if your method name matches the name of the query defined in your schema all you need is the ",[59,100954,83441],{},[52,100956,100958],{"className":54,"code":100957,"language":56,"meta":57,"style":57},"@QueryMapping\npublic Book findOne(@Argument Integer id) {\n return bookRepository.findOne(id);\n}\n",[59,100959,100960,100966,100980,100990],{"__ignoreMap":57},[62,100961,100962,100964],{"class":64,"line":65},[62,100963,942],{"class":72},[62,100965,83538],{"class":68},[62,100967,100968,100970,100972,100974,100976,100978],{"class":64,"line":76},[62,100969,116],{"class":68},[62,100971,83704],{"class":72},[62,100973,39503],{"class":122},[62,100975,2475],{"class":72},[62,100977,83711],{"class":68},[62,100979,100917],{"class":72},[62,100981,100982,100984,100986,100988],{"class":64,"line":82},[62,100983,2599],{"class":68},[62,100985,83559],{"class":72},[62,100987,39503],{"class":122},[62,100989,23764],{"class":72},[62,100991,100992],{"class":64,"line":89},[62,100993,379],{"class":72},[22,100995,100996],{},"If you restart the application you should be able to execute the following query.",[52,100998,101000],{"className":8822,"code":100999,"language":8824,"meta":57,"style":57},"query {\n findOne(id: 1) {\n title\n pages\n rating {\n star\n }\n author {\n firstName\n lastName\n }\n }\n}\n",[59,101001,101002,101008,101022,101026,101030,101036,101040,101044,101050,101054,101058,101062,101066],{"__ignoreMap":57},[62,101003,101004,101006],{"class":64,"line":65},[62,101005,9155],{"class":68},[62,101007,126],{"class":72},[62,101009,101010,101012,101014,101016,101018,101020],{"class":64,"line":76},[62,101011,98682],{"class":889},[62,101013,2109],{"class":72},[62,101015,6283],{"class":889},[62,101017,3696],{"class":72},[62,101019,6689],{"class":149},[62,101021,768],{"class":72},[62,101023,101024],{"class":64,"line":82},[62,101025,83608],{"class":889},[62,101027,101028],{"class":64,"line":89},[62,101029,83613],{"class":889},[62,101031,101032,101034],{"class":64,"line":95},[62,101033,98414],{"class":889},[62,101035,126],{"class":72},[62,101037,101038],{"class":64,"line":101},[62,101039,98421],{"class":889},[62,101041,101042],{"class":64,"line":107},[62,101043,223],{"class":72},[62,101045,101046,101048],{"class":64,"line":113},[62,101047,8863],{"class":889},[62,101049,126],{"class":72},[62,101051,101052],{"class":64,"line":129},[62,101053,98436],{"class":889},[62,101055,101056],{"class":64,"line":134},[62,101057,98441],{"class":889},[62,101059,101060],{"class":64,"line":156},[62,101061,223],{"class":72},[62,101063,101064],{"class":64,"line":161},[62,101065,3731],{"class":72},[62,101067,101068],{"class":64,"line":167},[62,101069,379],{"class":72},[22,101071,101072],{},"Congratulations 🥳 you just built your first GraphQL API in Spring. There is more to learn as you start building out full featured applications but you can do a lot with the basics you learned here today!",[636,101074,101075],{"id":32867},"Frontend",[22,101077,101078,101079,101082,101083,2755],{},"I’m not going to walk through this code but there is a fronted built in Vue located in ",[59,101080,101081],{},"/src/frontend."," If you want to learn more about running the frontend and the backend you can checkout the readme. If you want to learn more about how I build full-stack Java applications with Spring boot you can check out this ",[677,101084,38838],{"href":101085,"rel":101086},"https://www.danvega.dev/blog/2021/01/22/full-stack-java-vue/",[681],[22,101088,101089,101090,101093,101094,101097],{},"The important part on the GraphQL side is that everything runs through a single endpoint ",[59,101091,101092],{},"/graphql",". In a REST application you might add a ",[59,101095,101096],{},"@CORS"," annotation to a REST Controller but because you aren’t configuring request mappings in each controller you need a way to configure CORS. The easiest way to do this is by setting the one of the available CORS properties available to you. In this example I just allow everything by setting the following property:",[52,101099,101101],{"className":1663,"code":101100,"language":1665,"meta":57,"style":57},"spring.graphql.cors.allowed-origins=*\n",[59,101102,101103],{"__ignoreMap":57},[62,101104,101105,101108],{"class":64,"line":65},[62,101106,101107],{"class":122},"spring.graphql.cors.allowed-origins",[62,101109,41042],{"class":1675},[26,101111,37157],{"id":37156},[22,101113,101114],{},"We really just touched on the basics on how to get started with building GraphQL APIs in Spring. If you would like to learn more I have some resources for you below. I also want to let you know that I have a bunch of content planned.",[915,101116,101117,101123,101131],{},[37,101118,101119,101120,65140],{},"I’m working on the outline of a course that will teach you all about the basics of building, testing and deploying GraphQL applications for Spring. If you want to be notified when this available I will be releasing it to everyone subscribed to my ",[677,101121,33895],{"href":38005,"rel":101122},[681],[37,101124,101125,101126,2755],{},"I have a webinar coming up where we will cover some of this on June 21 and you can ",[677,101127,101130],{"href":101128,"rel":101129},"https://tanzu.vmware.com/content/webinars/jun-introduction-to-spring-for-graphql",[681],"register here",[37,101132,101133,101138],{},[677,101134,101137],{"href":101135,"rel":101136},"https://tanzu.vmware.com/developer/springone-tour/",[681],"SpringOne Tour"," - I will be giving an intro to Spring for GraphQL in some of of tour stops so if your a location where we are visiting please stop by and say hello 👋🏻",[636,101140,4098],{"id":4097},[915,101142,101143,101150,101155,101160],{},[37,101144,101145],{},[677,101146,101149],{"href":101147,"rel":101148},"https://github.com/danvega/spring-books",[681],"Github Repository for this tutorial",[37,101151,101152],{},[677,101153,88091],{"href":98293,"rel":101154},[681],[37,101156,101157],{},[677,101158,98300],{"href":98298,"rel":101159},[681],[37,101161,101162],{},[677,101163,101166],{"href":101164,"rel":101165},"https://graphql.org/learn/",[681],"Learn GraphQL",[26,101168,1499],{"id":1498},[22,101170,101171,101172,101175],{},"Spring for GraphQL is an exciting new project and we are just getting started. If you have any questions about building GraphQL APIs in Spring feel free to reach out and ask them. In the meantime follow me on ",[677,101173,86336],{"href":93581,"rel":101174},[681]," to stay up to date with everything I am working on.",[1527,101177,101178],{},"html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .spoOn, html code.shiki .spoOn{--shiki-default:#E36209;--shiki-dark:#FFAB70;--shiki-sepia:#E36209}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}",{"title":57,"searchDepth":76,"depth":76,"links":101180},[101181,101182,101186,101190,101197,101200],{"id":98307,"depth":76,"text":98308},{"id":98335,"depth":76,"text":98336,"children":101183},[101184,101185],{"id":98376,"depth":82,"text":98377},{"id":98459,"depth":82,"text":98460},{"id":98282,"depth":76,"text":88091,"children":101187},[101188,101189],{"id":98801,"depth":82,"text":98802},{"id":98821,"depth":82,"text":88091},{"id":98897,"depth":76,"text":98898,"children":101191},[101192,101193,101194,101195,101196],{"id":98920,"depth":82,"text":98921},{"id":99921,"depth":82,"text":99922},{"id":100124,"depth":82,"text":100125},{"id":100491,"depth":82,"text":100492},{"id":32867,"depth":82,"text":101075},{"id":37156,"depth":76,"text":37157,"children":101198},[101199],{"id":4097,"depth":82,"text":4098},{"id":1498,"depth":76,"text":1499},{"_id":101202,"path":101203,"title":101204,"description":101205,"meta":101206,"body":101211},"content/blog/2022/05/12/spring-data-jpa-pagination.md","/blog/2022/05/12/spring-data-jpa-pagination","Spring Data JPA Pagination","In this tutorial, you are going to learn how to work with pagination in Spring Data JPA.",{"slug":101207,"date":101208,"published":13,"tags":101209,"author":17,"cover":101210,"excerpt":-1},"spring-data-jpa-pagination","2022-05-12T16:00:00.000Z",[16,10159],"./spring-data-jpa-pagination-thumbnail.png",{"type":19,"value":101212,"toc":103686},[101213,101216,101219,101223,101230,101235,101241,101281,101290,101535,101713,101717,101725,101732,101762,101773,101788,101854,101858,101865,101884,101897,102037,102043,102057,102163,102166,102170,102182,102192,102473,102481,102487,102490,102492,102512,102597,102609,102814,102827,102834,102990,102998,103005,103665,103667,103670,103679,103683],[22,101214,101215],{},"In this tutorial, you are going to learn how to work with pagination in Spring Data JPA. If you have a few records you can just return all of them from a single API endpoint. In the real world though you will probably have more than a few records. In this case you wouldn’t want to return all of the records to the client for performance reasons.",[22,101217,101218],{},"On the client you would display a smaller number of records at a time and allow the user to click on next, previous or a specific page number. To simulate that in this demo you will use a third party library to create a large dataset.",[26,101220,101222],{"id":101221},"spring-data-jpa","Spring Data JPA",[22,101224,101225,101226,101229],{},"To get started head over to ",[677,101227,2903],{"href":40207,"rel":101228},[681]," and create a new application with the following:",[22,101231,101232],{},[653,101233],{"alt":24606,"src":101234},"/images/blog/2022/05/12/spring-data-jpa-pagination.png",[22,101236,101237,101238,101240],{},"To get started open up ",[59,101239,1265],{}," and set the following properties for your database:",[52,101242,101244],{"className":1663,"code":101243,"language":1665,"meta":57,"style":57},"spring.h2.console.enabled=true\nspring.datasource.generate-unique-name=false\nspring.datasource.name=people\n\nspring.jpa.show-sql=true\n",[59,101245,101246,101254,101262,101269,101273],{"__ignoreMap":57},[62,101247,101248,101250,101252],{"class":64,"line":65},[62,101249,41993],{"class":122},[62,101251,146],{"class":1675},[62,101253,51914],{"class":149},[62,101255,101256,101258,101260],{"class":64,"line":76},[62,101257,41978],{"class":122},[62,101259,146],{"class":1675},[62,101261,40782],{"class":149},[62,101263,101264,101266],{"class":64,"line":82},[62,101265,41985],{"class":122},[62,101267,101268],{"class":1675},"=people\n",[62,101270,101271],{"class":64,"line":89},[62,101272,79],{"emptyLinePlaceholder":13},[62,101274,101275,101277,101279],{"class":64,"line":95},[62,101276,83156],{"class":122},[62,101278,146],{"class":1675},[62,101280,51914],{"class":149},[22,101282,101283,101284,19931,101286,101289],{},"With the database connection in place you need to start by defining your entities. This application is going to model a CRM where you have a person and their address. Create a new model package and add the following ",[59,101285,32459],{},[59,101287,101288],{},"Address"," entities:",[52,101291,101293],{"className":54,"code":101292,"language":56,"meta":57,"style":57},"@Entity\npublic class Person {\n\n @Id @GeneratedValue\n private Integer id;\n private String firstName;\n private String lastName;\n private String phoneNumber;\n private String email;\n @OneToOne(cascade = CascadeType.ALL)\n @JoinColumn(name = \"address_id\", referencedColumnName = \"id\")\n private Address address;\n\n public Person() {\n }\n\n public Person(String firstName, String lastName, String phoneNumber, String email, Address address) {\n this.firstName = firstName;\n this.lastName = lastName;\n this.phoneNumber = phoneNumber;\n this.email = email;\n this.address = address;\n }\n\n // getters, setters & toString\n\n}\n",[59,101294,101295,101301,101311,101315,101325,101331,101337,101343,101350,101356,101371,101398,101405,101409,101417,101421,101425,101457,101469,101480,101492,101502,101514,101518,101522,101527,101531],{"__ignoreMap":57},[62,101296,101297,101299],{"class":64,"line":65},[62,101298,942],{"class":72},[62,101300,8999],{"class":68},[62,101302,101303,101305,101307,101309],{"class":64,"line":76},[62,101304,116],{"class":68},[62,101306,119],{"class":68},[62,101308,32485],{"class":122},[62,101310,126],{"class":72},[62,101312,101313],{"class":64,"line":82},[62,101314,79],{"emptyLinePlaceholder":13},[62,101316,101317,101319,101321,101323],{"class":64,"line":89},[62,101318,2143],{"class":72},[62,101320,9016],{"class":68},[62,101322,9019],{"class":72},[62,101324,9022],{"class":68},[62,101326,101327,101329],{"class":64,"line":95},[62,101328,137],{"class":68},[62,101330,45372],{"class":72},[62,101332,101333,101335],{"class":64,"line":101},[62,101334,137],{"class":68},[62,101336,22329],{"class":72},[62,101338,101339,101341],{"class":64,"line":107},[62,101340,137],{"class":68},[62,101342,22353],{"class":72},[62,101344,101345,101347],{"class":64,"line":113},[62,101346,137],{"class":68},[62,101348,101349],{"class":72}," String phoneNumber;\n",[62,101351,101352,101354],{"class":64,"line":129},[62,101353,137],{"class":68},[62,101355,22360],{"class":72},[62,101357,101358,101360,101363,101365,101367,101369],{"class":64,"line":134},[62,101359,2143],{"class":72},[62,101361,101362],{"class":68},"OneToOne",[62,101364,2109],{"class":72},[62,101366,82935],{"class":149},[62,101368,2556],{"class":68},[62,101370,82940],{"class":72},[62,101372,101373,101375,101377,101379,101381,101383,101386,101388,101391,101393,101396],{"class":64,"line":156},[62,101374,2143],{"class":72},[62,101376,82947],{"class":68},[62,101378,2109],{"class":72},[62,101380,3107],{"class":149},[62,101382,2556],{"class":68},[62,101384,101385],{"class":1675}," \"address_id\"",[62,101387,976],{"class":72},[62,101389,101390],{"class":149},"referencedColumnName",[62,101392,2556],{"class":68},[62,101394,101395],{"class":1675}," \"id\"",[62,101397,2212],{"class":72},[62,101399,101400,101402],{"class":64,"line":161},[62,101401,137],{"class":68},[62,101403,101404],{"class":72}," Address address;\n",[62,101406,101407],{"class":64,"line":167},[62,101408,79],{"emptyLinePlaceholder":13},[62,101410,101411,101413,101415],{"class":64,"line":173},[62,101412,194],{"class":68},[62,101414,32485],{"class":122},[62,101416,206],{"class":72},[62,101418,101419],{"class":64,"line":179},[62,101420,223],{"class":72},[62,101422,101423],{"class":64,"line":185},[62,101424,79],{"emptyLinePlaceholder":13},[62,101426,101427,101429,101431,101433,101436,101438,101441,101443,101446,101448,101450,101453,101455],{"class":64,"line":191},[62,101428,194],{"class":68},[62,101430,32485],{"class":122},[62,101432,1049],{"class":72},[62,101434,101435],{"class":889},"firstName",[62,101437,8624],{"class":72},[62,101439,101440],{"class":889},"lastName",[62,101442,8624],{"class":72},[62,101444,101445],{"class":889},"phoneNumber",[62,101447,8624],{"class":72},[62,101449,52059],{"class":889},[62,101451,101452],{"class":72},", Address ",[62,101454,76713],{"class":889},[62,101456,768],{"class":72},[62,101458,101459,101461,101464,101466],{"class":64,"line":209},[62,101460,2405],{"class":149},[62,101462,101463],{"class":72},".firstName ",[62,101465,146],{"class":68},[62,101467,101468],{"class":72}," firstName;\n",[62,101470,101471,101473,101476,101478],{"class":64,"line":220},[62,101472,2405],{"class":149},[62,101474,101475],{"class":72},".lastName ",[62,101477,146],{"class":68},[62,101479,99000],{"class":72},[62,101481,101482,101484,101487,101489],{"class":64,"line":226},[62,101483,2405],{"class":149},[62,101485,101486],{"class":72},".phoneNumber ",[62,101488,146],{"class":68},[62,101490,101491],{"class":72}," phoneNumber;\n",[62,101493,101494,101496,101498,101500],{"class":64,"line":231},[62,101495,2405],{"class":149},[62,101497,52092],{"class":72},[62,101499,146],{"class":68},[62,101501,52097],{"class":72},[62,101503,101504,101506,101509,101511],{"class":64,"line":236},[62,101505,2405],{"class":149},[62,101507,101508],{"class":72},".address ",[62,101510,146],{"class":68},[62,101512,101513],{"class":72}," address;\n",[62,101515,101516],{"class":64,"line":242},[62,101517,223],{"class":72},[62,101519,101520],{"class":64,"line":247},[62,101521,79],{"emptyLinePlaceholder":13},[62,101523,101524],{"class":64,"line":252},[62,101525,101526],{"class":85}," // getters, setters & toString\n",[62,101528,101529],{"class":64,"line":257},[62,101530,79],{"emptyLinePlaceholder":13},[62,101532,101533],{"class":64,"line":271},[62,101534,379],{"class":72},[52,101536,101538],{"className":54,"code":101537,"language":56,"meta":57,"style":57},"@Entity\npublic class Address {\n\n @Id @GeneratedValue\n private Integer id;\n private String address;\n private String city;\n private String state;\n private String zip;\n\n public Address() {\n }\n\n public Address(String address, String city, String state, String zip) {\n this.address = address;\n this.city = city;\n this.state = state;\n this.zip = zip;\n }\n\n // getters, setters & toString\n\n}\n",[59,101539,101540,101546,101557,101561,101571,101577,101584,101591,101597,101604,101608,101616,101620,101624,101650,101660,101671,101681,101693,101697,101701,101705,101709],{"__ignoreMap":57},[62,101541,101542,101544],{"class":64,"line":65},[62,101543,942],{"class":72},[62,101545,8999],{"class":68},[62,101547,101548,101550,101552,101555],{"class":64,"line":76},[62,101549,116],{"class":68},[62,101551,119],{"class":68},[62,101553,101554],{"class":122}," Address",[62,101556,126],{"class":72},[62,101558,101559],{"class":64,"line":82},[62,101560,79],{"emptyLinePlaceholder":13},[62,101562,101563,101565,101567,101569],{"class":64,"line":89},[62,101564,2143],{"class":72},[62,101566,9016],{"class":68},[62,101568,9019],{"class":72},[62,101570,9022],{"class":68},[62,101572,101573,101575],{"class":64,"line":95},[62,101574,137],{"class":68},[62,101576,45372],{"class":72},[62,101578,101579,101581],{"class":64,"line":101},[62,101580,137],{"class":68},[62,101582,101583],{"class":72}," String address;\n",[62,101585,101586,101588],{"class":64,"line":107},[62,101587,137],{"class":68},[62,101589,101590],{"class":72}," String city;\n",[62,101592,101593,101595],{"class":64,"line":113},[62,101594,137],{"class":68},[62,101596,58796],{"class":72},[62,101598,101599,101601],{"class":64,"line":129},[62,101600,137],{"class":68},[62,101602,101603],{"class":72}," String zip;\n",[62,101605,101606],{"class":64,"line":134},[62,101607,79],{"emptyLinePlaceholder":13},[62,101609,101610,101612,101614],{"class":64,"line":156},[62,101611,194],{"class":68},[62,101613,101554],{"class":122},[62,101615,206],{"class":72},[62,101617,101618],{"class":64,"line":161},[62,101619,223],{"class":72},[62,101621,101622],{"class":64,"line":167},[62,101623,79],{"emptyLinePlaceholder":13},[62,101625,101626,101628,101630,101632,101634,101636,101639,101641,101643,101645,101648],{"class":64,"line":173},[62,101627,194],{"class":68},[62,101629,101554],{"class":122},[62,101631,1049],{"class":72},[62,101633,76713],{"class":889},[62,101635,8624],{"class":72},[62,101637,101638],{"class":889},"city",[62,101640,8624],{"class":72},[62,101642,43061],{"class":889},[62,101644,8624],{"class":72},[62,101646,101647],{"class":889},"zip",[62,101649,768],{"class":72},[62,101651,101652,101654,101656,101658],{"class":64,"line":179},[62,101653,2405],{"class":149},[62,101655,101508],{"class":72},[62,101657,146],{"class":68},[62,101659,101513],{"class":72},[62,101661,101662,101664,101667,101669],{"class":64,"line":185},[62,101663,2405],{"class":149},[62,101665,101666],{"class":72},".city ",[62,101668,146],{"class":68},[62,101670,76671],{"class":72},[62,101672,101673,101675,101677,101679],{"class":64,"line":191},[62,101674,2405],{"class":149},[62,101676,59335],{"class":72},[62,101678,146],{"class":68},[62,101680,59340],{"class":72},[62,101682,101683,101685,101688,101690],{"class":64,"line":209},[62,101684,2405],{"class":149},[62,101686,101687],{"class":72},".zip ",[62,101689,146],{"class":68},[62,101691,101692],{"class":72}," zip;\n",[62,101694,101695],{"class":64,"line":220},[62,101696,223],{"class":72},[62,101698,101699],{"class":64,"line":226},[62,101700,79],{"emptyLinePlaceholder":13},[62,101702,101703],{"class":64,"line":231},[62,101704,101526],{"class":85},[62,101706,101707],{"class":64,"line":236},[62,101708,79],{"emptyLinePlaceholder":13},[62,101710,101711],{"class":64,"line":242},[62,101712,379],{"class":72},[636,101714,101716],{"id":101715},"spring-data-jpa-repositories","Spring Data JPA Repositories",[22,101718,101719,101720,101724],{},"With your entities in place you need a way to read and persist data from your database. This is where Spring Data really shines in giving you the functionality to perform most of your common operations right out of the box. What I really love is that this is a consistent programming model across all of the ",[677,101721,10159],{"href":101722,"rel":101723},"https://spring.io/projects/spring-data",[681]," projects 👏🏻",[22,101726,101727,101728,101731],{},"Create a new interface (you heard me right) named ",[59,101729,101730],{},"PersonRepository",". Alone this wont do much but the real power is made available to you when you extend one of the Spring Data Repositories:",[915,101733,101734,101741,101748,101755],{},[37,101735,101736],{},[677,101737,101740],{"href":101738,"rel":101739},"https://docs.spring.io/spring-data/commons/docs/current/api/org/springframework/data/repository/Repository.html",[681],"Repository\u003CT,ID>",[37,101742,101743],{},[677,101744,101747],{"href":101745,"rel":101746},"https://docs.spring.io/spring-data/commons/docs/current/api/org/springframework/data/repository/CrudRepository.html",[681],"CrudRepository\u003CT,ID>",[37,101749,101750],{},[677,101751,101754],{"href":101752,"rel":101753},"https://docs.spring.io/spring-data/commons/docs/current/api/org/springframework/data/repository/PagingAndSortingRepository.html",[681],"PagingAndSortingRepository\u003CT,ID>",[37,101756,101757],{},[677,101758,101761],{"href":101759,"rel":101760},"https://docs.spring.io/spring-data/jpa/docs/current/api/org/springframework/data/jpa/repository/JpaRepository.html",[681],"JpaRepository\u003CT,ID>",[22,101763,101764,101765,101768,101769,101772],{},"You are going to extend the ",[59,101766,101767],{},"PagingAndSortingRepository"," which gives you 2 methods you will need for pagination. This interface in turn extends the ",[59,101770,101771],{},"CrudRepostiory"," so you will also get all of the CRUD methods for free.",[22,101774,101775,101776,101778,101779,46277,101782,101784,101785,101787],{},"When you extend a repository you have to give a type which for this example will be ",[59,101777,32459],{}," and the 2nd argument is the type of the ",[59,101780,101781],{},"@id",[59,101783,32459],{}," which is an ",[59,101786,979],{},". At runtime Spring will see that you have extended one of the repositories and turn this into an implementation that you can use.",[52,101789,101791],{"className":54,"code":101790,"language":56,"meta":57,"style":57},"package dev.danvega.graphqlpaging.repository;\n\nimport dev.danvega.graphqlpaging.model.Person;\nimport org.springframework.data.repository.PagingAndSortingRepository;\n\npublic interface PersonRepository extends PagingAndSortingRepository\u003CPerson,Integer> {\n\n}\n",[59,101792,101793,101800,101804,101811,101818,101822,101846,101850],{"__ignoreMap":57},[62,101794,101795,101797],{"class":64,"line":65},[62,101796,69],{"class":68},[62,101798,101799],{"class":72}," dev.danvega.graphqlpaging.repository;\n",[62,101801,101802],{"class":64,"line":76},[62,101803,79],{"emptyLinePlaceholder":13},[62,101805,101806,101808],{"class":64,"line":82},[62,101807,27875],{"class":68},[62,101809,101810],{"class":72}," dev.danvega.graphqlpaging.model.Person;\n",[62,101812,101813,101815],{"class":64,"line":89},[62,101814,27875],{"class":68},[62,101816,101817],{"class":72}," org.springframework.data.repository.PagingAndSortingRepository;\n",[62,101819,101820],{"class":64,"line":95},[62,101821,79],{"emptyLinePlaceholder":13},[62,101823,101824,101826,101828,101831,101833,101836,101838,101840,101842,101844],{"class":64,"line":101},[62,101825,116],{"class":68},[62,101827,8531],{"class":68},[62,101829,101830],{"class":122}," PersonRepository",[62,101832,8537],{"class":68},[62,101834,101835],{"class":122}," PagingAndSortingRepository",[62,101837,760],{"class":72},[62,101839,32459],{"class":68},[62,101841,32225],{"class":72},[62,101843,979],{"class":68},[62,101845,8552],{"class":72},[62,101847,101848],{"class":64,"line":107},[62,101849,79],{"emptyLinePlaceholder":13},[62,101851,101852],{"class":64,"line":113},[62,101853,379],{"class":72},[636,101855,101857],{"id":101856},"sample-data-loader-with-a-command-line-runner","Sample Data Loader with a Command Line Runner",[22,101859,101860,101861,101864],{},"You need a way to load some data when the application starts. You could create a SQL file in the ",[59,101862,101863],{},"resources/"," directory but who wants to write SQL by hand? Instead you can write some Java code and persist some new records to the database.",[22,101866,101867,101868,101870,101871,101874,101875,101878,101879,101883],{},"The way you can achieve this is by utilizing an interface called the ",[59,101869,2066],{},". This will allow you to execute some code after the ",[59,101872,101873],{},"ApplicationContext"," is created and right before the ",[59,101876,101877],{},"SpringApplication.run()"," method executes. I have written a post on the ",[677,101880,101882],{"href":101881},"/command-line-runner","CommandLineRunner here"," if you would like to read more about it.",[22,101885,101886,101887,101890,101891,101893,101894,101896],{},"You will need to create a new class called ",[59,101888,101889],{},"SampleDataLoader"," that implements the ",[59,101892,2066],{}," interface. Implement the single method from the command line runner named ",[59,101895,61690],{}," and for now just log a message. If you run the application you should see that message in your console.",[52,101898,101900],{"className":54,"code":101899,"language":56,"meta":57,"style":57},"@Component\npublic class SampleDataLoader implements CommandLineRunner {\n\n private final Logger logger = LoggerFactory.getLogger(SampleDataLoader.class);\n private final PersonRepository repository;\n\n public SampleDataLoader(PersonRepository repository) {\n this.repository = repository;\n }\n\n @Override\n public void run(String... args) throws Exception {\n logger.info(\"Loading sample data...\");\n }\n\n}\n",[59,101901,101902,101908,101923,101927,101944,101953,101957,101970,101980,101984,101988,101994,102012,102025,102029,102033],{"__ignoreMap":57},[62,101903,101904,101906],{"class":64,"line":65},[62,101905,942],{"class":72},[62,101907,12585],{"class":68},[62,101909,101910,101912,101914,101917,101919,101921],{"class":64,"line":76},[62,101911,116],{"class":68},[62,101913,119],{"class":68},[62,101915,101916],{"class":122}," SampleDataLoader",[62,101918,13520],{"class":68},[62,101920,17592],{"class":122},[62,101922,126],{"class":72},[62,101924,101925],{"class":64,"line":82},[62,101926,79],{"emptyLinePlaceholder":13},[62,101928,101929,101931,101933,101935,101937,101939,101941],{"class":64,"line":89},[62,101930,137],{"class":68},[62,101932,458],{"class":68},[62,101934,61921],{"class":72},[62,101936,146],{"class":68},[62,101938,3066],{"class":72},[62,101940,3069],{"class":122},[62,101942,101943],{"class":72},"(SampleDataLoader.class);\n",[62,101945,101946,101948,101950],{"class":64,"line":95},[62,101947,137],{"class":68},[62,101949,458],{"class":68},[62,101951,101952],{"class":72}," PersonRepository repository;\n",[62,101954,101955],{"class":64,"line":101},[62,101956,79],{"emptyLinePlaceholder":13},[62,101958,101959,101961,101963,101966,101968],{"class":64,"line":107},[62,101960,194],{"class":68},[62,101962,101916],{"class":122},[62,101964,101965],{"class":72},"(PersonRepository ",[62,101967,23540],{"class":889},[62,101969,768],{"class":72},[62,101971,101972,101974,101976,101978],{"class":64,"line":113},[62,101973,2405],{"class":149},[62,101975,23549],{"class":72},[62,101977,146],{"class":68},[62,101979,23554],{"class":72},[62,101981,101982],{"class":64,"line":129},[62,101983,223],{"class":72},[62,101985,101986],{"class":64,"line":134},[62,101987,79],{"emptyLinePlaceholder":13},[62,101989,101990,101992],{"class":64,"line":156},[62,101991,2143],{"class":72},[62,101993,13555],{"class":68},[62,101995,101996,101998,102000,102002,102004,102006,102008,102010],{"class":64,"line":161},[62,101997,194],{"class":68},[62,101999,200],{"class":68},[62,102001,1716],{"class":122},[62,102003,17699],{"class":72},[62,102005,2117],{"class":889},[62,102007,5024],{"class":72},[62,102009,11501],{"class":68},[62,102011,11504],{"class":72},[62,102013,102014,102016,102018,102020,102023],{"class":64,"line":167},[62,102015,61964],{"class":72},[62,102017,12688],{"class":122},[62,102019,2109],{"class":72},[62,102021,102022],{"class":1675},"\"Loading sample data...\"",[62,102024,1133],{"class":72},[62,102026,102027],{"class":64,"line":173},[62,102028,223],{"class":72},[62,102030,102031],{"class":64,"line":179},[62,102032,79],{"emptyLinePlaceholder":13},[62,102034,102035],{"class":64,"line":185},[62,102036,379],{"class":72},[22,102038,102039],{},[653,102040],{"alt":102041,"src":102042},"Loading Sample Data","/images/blog/2022/05/12/loading-sample-data.png",[22,102044,102045,102046,102048,102049,102052,102053,102056],{},"Now that you know that the run method will execute right before the application starts this is a good place to add some data to our database. Create a new instance of ",[59,102047,32459],{}," and use the ",[59,102050,102051],{},"CrudRepositories"," save method to persist a new record to the database. If you visit the ",[677,102054,62194],{"href":52672,"rel":102055},[681]," you should your new person and address records.",[52,102058,102060],{"className":54,"code":102059,"language":56,"meta":57,"style":57},"@Override\npublic void run(String... args) throws Exception {\n Person person = new Person(\"Dan\",\n \"Vega\",\n \"216.555.1212\",\n \"danvega@gmail.com\",\n new Address(\"Street\",\"City\",\"State\",\"Zip\"));\n\n repository.save(person);\n}\n",[59,102061,102062,102068,102079,102096,102103,102110,102117,102145,102149,102159],{"__ignoreMap":57},[62,102063,102064,102066],{"class":64,"line":65},[62,102065,942],{"class":72},[62,102067,13555],{"class":68},[62,102069,102070,102072,102074,102076],{"class":64,"line":76},[62,102071,116],{"class":68},[62,102073,200],{"class":68},[62,102075,1716],{"class":122},[62,102077,102078],{"class":72},"(String... args) throws Exception {\n",[62,102080,102081,102084,102086,102088,102090,102092,102094],{"class":64,"line":82},[62,102082,102083],{"class":72}," Person person ",[62,102085,146],{"class":68},[62,102087,466],{"class":68},[62,102089,32485],{"class":122},[62,102091,2109],{"class":72},[62,102093,25684],{"class":1675},[62,102095,3338],{"class":72},[62,102097,102098,102101],{"class":64,"line":89},[62,102099,102100],{"class":1675}," \"Vega\"",[62,102102,3338],{"class":72},[62,102104,102105,102108],{"class":64,"line":95},[62,102106,102107],{"class":1675}," \"216.555.1212\"",[62,102109,3338],{"class":72},[62,102111,102112,102115],{"class":64,"line":101},[62,102113,102114],{"class":1675}," \"danvega@gmail.com\"",[62,102116,3338],{"class":72},[62,102118,102119,102121,102123,102125,102128,102130,102133,102135,102138,102140,102143],{"class":64,"line":107},[62,102120,3306],{"class":68},[62,102122,101554],{"class":122},[62,102124,2109],{"class":72},[62,102126,102127],{"class":1675},"\"Street\"",[62,102129,32225],{"class":72},[62,102131,102132],{"class":1675},"\"City\"",[62,102134,32225],{"class":72},[62,102136,102137],{"class":1675},"\"State\"",[62,102139,32225],{"class":72},[62,102141,102142],{"class":1675},"\"Zip\"",[62,102144,6979],{"class":72},[62,102146,102147],{"class":64,"line":113},[62,102148,79],{"emptyLinePlaceholder":13},[62,102150,102151,102154,102156],{"class":64,"line":129},[62,102152,102153],{"class":72}," repository.",[62,102155,22562],{"class":122},[62,102157,102158],{"class":72},"(person);\n",[62,102160,102161],{"class":64,"line":134},[62,102162,379],{"class":72},[22,102164,102165],{},"While this works it’s going to take us a long time to get enough sample data in our database to properly test out pagination.",[636,102167,102169],{"id":102168},"java-faker","Java Faker",[22,102171,102172,102176,102177,102181],{},[677,102173,102169],{"href":102174,"rel":102175},"https://github.com/DiUS/java-faker",[681]," is a really great library that allows you to generate fake data. I ",[677,102178,86007],{"href":102179,"rel":102180},"https://youtu.be/UzBOv_SHUng",[681]," on this if you’re interested in watching that.",[22,102183,102184,102185,102188,102189,102191],{},"In the run method you can create a new ",[59,102186,102187],{},"IntStream"," which allows you to create 100 new ",[59,102190,32459],{}," objects. Inside of the person constructor you are going to use the Faker library to generate fake data for each of the fields.",[52,102193,102195],{"className":54,"code":102194,"language":56,"meta":57,"style":57},"private final PersonRepository repository;\nprivate final Faker faker;\n\npublic SampleDataLoader(PersonRepository repository) {\n this.repository = repository;\n this.faker = new Faker();\n}\n\n@Override\npublic void run(String... args) throws Exception {\n\n // create 100 rows of people in the database\n List\u003CPerson> people = IntStream.rangeClosed(1,100)\n .mapToObj(i -> new Person(\n faker.name().firstName(),\n faker.name().lastName(),\n faker.phoneNumber().cellPhone(),\n faker.internet().emailAddress(),\n new Address(\n faker.address().streetAddress(),\n faker.address().city(),\n faker.address().state(),\n faker.address().zipCode()\n )\n )).toList();\n\n repository.saveAll(people);\n}\n",[59,102196,102197,102205,102214,102218,102227,102237,102253,102257,102261,102267,102277,102281,102286,102314,102332,102345,102357,102370,102384,102392,102406,102418,102430,102443,102447,102456,102460,102469],{"__ignoreMap":57},[62,102198,102199,102201,102203],{"class":64,"line":65},[62,102200,1359],{"class":68},[62,102202,458],{"class":68},[62,102204,101952],{"class":72},[62,102206,102207,102209,102211],{"class":64,"line":76},[62,102208,1359],{"class":68},[62,102210,458],{"class":68},[62,102212,102213],{"class":72}," Faker faker;\n",[62,102215,102216],{"class":64,"line":82},[62,102217,79],{"emptyLinePlaceholder":13},[62,102219,102220,102222,102224],{"class":64,"line":89},[62,102221,116],{"class":68},[62,102223,101916],{"class":122},[62,102225,102226],{"class":72},"(PersonRepository repository) {\n",[62,102228,102229,102231,102233,102235],{"class":64,"line":95},[62,102230,39124],{"class":149},[62,102232,23549],{"class":72},[62,102234,146],{"class":68},[62,102236,23554],{"class":72},[62,102238,102239,102241,102244,102246,102248,102251],{"class":64,"line":101},[62,102240,39124],{"class":149},[62,102242,102243],{"class":72},".faker ",[62,102245,146],{"class":68},[62,102247,466],{"class":68},[62,102249,102250],{"class":122}," Faker",[62,102252,822],{"class":72},[62,102254,102255],{"class":64,"line":107},[62,102256,379],{"class":72},[62,102258,102259],{"class":64,"line":113},[62,102260,79],{"emptyLinePlaceholder":13},[62,102262,102263,102265],{"class":64,"line":129},[62,102264,942],{"class":72},[62,102266,13555],{"class":68},[62,102268,102269,102271,102273,102275],{"class":64,"line":134},[62,102270,116],{"class":68},[62,102272,200],{"class":68},[62,102274,1716],{"class":122},[62,102276,102078],{"class":72},[62,102278,102279],{"class":64,"line":156},[62,102280,79],{"emptyLinePlaceholder":13},[62,102282,102283],{"class":64,"line":161},[62,102284,102285],{"class":85}," // create 100 rows of people in the database\n",[62,102287,102288,102290,102292,102295,102297,102300,102303,102305,102307,102309,102312],{"class":64,"line":167},[62,102289,4396],{"class":72},[62,102291,32459],{"class":68},[62,102293,102294],{"class":72},"> people ",[62,102296,146],{"class":68},[62,102298,102299],{"class":72}," IntStream.",[62,102301,102302],{"class":122},"rangeClosed",[62,102304,2109],{"class":72},[62,102306,6689],{"class":149},[62,102308,32225],{"class":72},[62,102310,102311],{"class":149},"100",[62,102313,2212],{"class":72},[62,102315,102316,102318,102321,102324,102326,102328,102330],{"class":64,"line":173},[62,102317,2610],{"class":72},[62,102319,102320],{"class":122},"mapToObj",[62,102322,102323],{"class":72},"(i ",[62,102325,800],{"class":68},[62,102327,466],{"class":68},[62,102329,32485],{"class":122},[62,102331,3301],{"class":72},[62,102333,102334,102337,102339,102341,102343],{"class":64,"line":179},[62,102335,102336],{"class":72}," faker.",[62,102338,3107],{"class":122},[62,102340,3229],{"class":72},[62,102342,101435],{"class":122},[62,102344,4651],{"class":72},[62,102346,102347,102349,102351,102353,102355],{"class":64,"line":185},[62,102348,102336],{"class":72},[62,102350,3107],{"class":122},[62,102352,3229],{"class":72},[62,102354,101440],{"class":122},[62,102356,4651],{"class":72},[62,102358,102359,102361,102363,102365,102368],{"class":64,"line":191},[62,102360,102336],{"class":72},[62,102362,101445],{"class":122},[62,102364,3229],{"class":72},[62,102366,102367],{"class":122},"cellPhone",[62,102369,4651],{"class":72},[62,102371,102372,102374,102377,102379,102382],{"class":64,"line":209},[62,102373,102336],{"class":72},[62,102375,102376],{"class":122},"internet",[62,102378,3229],{"class":72},[62,102380,102381],{"class":122},"emailAddress",[62,102383,4651],{"class":72},[62,102385,102386,102388,102390],{"class":64,"line":220},[62,102387,89479],{"class":68},[62,102389,101554],{"class":122},[62,102391,3301],{"class":72},[62,102393,102394,102397,102399,102401,102404],{"class":64,"line":226},[62,102395,102396],{"class":72}," faker.",[62,102398,76713],{"class":122},[62,102400,3229],{"class":72},[62,102402,102403],{"class":122},"streetAddress",[62,102405,4651],{"class":72},[62,102407,102408,102410,102412,102414,102416],{"class":64,"line":231},[62,102409,102396],{"class":72},[62,102411,76713],{"class":122},[62,102413,3229],{"class":72},[62,102415,101638],{"class":122},[62,102417,4651],{"class":72},[62,102419,102420,102422,102424,102426,102428],{"class":64,"line":236},[62,102421,102396],{"class":72},[62,102423,76713],{"class":122},[62,102425,3229],{"class":72},[62,102427,43061],{"class":122},[62,102429,4651],{"class":72},[62,102431,102432,102434,102436,102438,102441],{"class":64,"line":242},[62,102433,102396],{"class":72},[62,102435,76713],{"class":122},[62,102437,3229],{"class":72},[62,102439,102440],{"class":122},"zipCode",[62,102442,2223],{"class":72},[62,102444,102445],{"class":64,"line":247},[62,102446,4716],{"class":72},[62,102448,102449,102452,102454],{"class":64,"line":252},[62,102450,102451],{"class":72}," )).",[62,102453,3903],{"class":122},[62,102455,822],{"class":72},[62,102457,102458],{"class":64,"line":257},[62,102459,79],{"emptyLinePlaceholder":13},[62,102461,102462,102464,102466],{"class":64,"line":271},[62,102463,102153],{"class":72},[62,102465,62170],{"class":122},[62,102467,102468],{"class":72},"(people);\n",[62,102470,102471],{"class":64,"line":281},[62,102472,379],{"class":72},[22,102474,102475,102476,19931,102478,102480],{},"If you run the application you should see 100 records in the ",[59,102477,32459],{},[59,102479,101288],{}," table. Not only do you have data but you have some pretty valid data that you can also do some filtering and sorting on.",[22,102482,102483],{},[653,102484],{"alt":102485,"src":102486},"100 Rows in the database","/images/blog/2022/05/12/100-people.png",[22,102488,102489],{},"Now that you have some data in the database you can focus on pagination.",[636,102491,101204],{"id":101207},[22,102493,102494,102495,102498,102499,19931,102501,102503,102504,102507,102508,2755],{},"To get started on the web side create a new ",[59,102496,102497],{},"PersonController"," that is annotated with ",[59,102500,40266],{},[59,102502,56483],{},". In this controller you will create a field of type ",[59,102505,102506],{},"PersonRepsoitory"," and have an instance of the repository autowired for you through constructor injection. If you want to learn why constructor injection is the preferred method for dependency injection in Spring check out ",[677,102509,102511],{"href":56186,"rel":102510},[681],"this video",[52,102513,102515],{"className":54,"code":102514,"language":56,"meta":57,"style":57},"@RestController\n@RequestMapping(\"/api/people\")\npublic class PersonController {\n\n private final PersonRepository repository;\n\n public PersonController(PersonRepository repository) {\n this.repository = repository;\n }\n\n}\n",[59,102516,102517,102523,102536,102547,102551,102559,102563,102575,102585,102589,102593],{"__ignoreMap":57},[62,102518,102519,102521],{"class":64,"line":65},[62,102520,942],{"class":72},[62,102522,2342],{"class":68},[62,102524,102525,102527,102529,102531,102534],{"class":64,"line":76},[62,102526,942],{"class":72},[62,102528,10592],{"class":68},[62,102530,2109],{"class":72},[62,102532,102533],{"class":1675},"\"/api/people\"",[62,102535,2212],{"class":72},[62,102537,102538,102540,102542,102545],{"class":64,"line":82},[62,102539,116],{"class":68},[62,102541,119],{"class":68},[62,102543,102544],{"class":122}," PersonController",[62,102546,126],{"class":72},[62,102548,102549],{"class":64,"line":89},[62,102550,79],{"emptyLinePlaceholder":13},[62,102552,102553,102555,102557],{"class":64,"line":95},[62,102554,137],{"class":68},[62,102556,458],{"class":68},[62,102558,101952],{"class":72},[62,102560,102561],{"class":64,"line":101},[62,102562,79],{"emptyLinePlaceholder":13},[62,102564,102565,102567,102569,102571,102573],{"class":64,"line":107},[62,102566,194],{"class":68},[62,102568,102544],{"class":122},[62,102570,101965],{"class":72},[62,102572,23540],{"class":889},[62,102574,768],{"class":72},[62,102576,102577,102579,102581,102583],{"class":64,"line":113},[62,102578,2405],{"class":149},[62,102580,23549],{"class":72},[62,102582,146],{"class":68},[62,102584,23554],{"class":72},[62,102586,102587],{"class":64,"line":129},[62,102588,223],{"class":72},[62,102590,102591],{"class":64,"line":134},[62,102592,79],{"emptyLinePlaceholder":13},[62,102594,102595],{"class":64,"line":156},[62,102596,379],{"class":72},[22,102598,102599,102600,102602,102603,102605,102606,2755],{},"If you take a look at the ",[59,102601,101767],{}," you will see a method called ",[59,102604,93870],{}," that takes an argument of type ",[59,102607,102608],{},"Pageable",[52,102610,102612],{"className":54,"code":102611,"language":56,"meta":57,"style":57},"public interface PagingAndSortingRepository\u003CT, ID> extends CrudRepository\u003CT, ID> {\n\n /**\n * Returns all entities sorted by the given options.\n *\n * @param sort the {@link Sort} specification to sort the results by,\n * can be {@link Sort#unsorted()}, must not be {@literal null}.\n * @return all entities sorted by the given options\n */\n Iterable\u003CT> findAll(Sort sort);\n\n /**\n * Returns a {@link Page} of entities meeting the paging restriction provided in\n * the {@link Pageable} object.\n *\n * @param pageable the pageable to request a paged result, can be\n * {@link Pageable#unpaged()}, must not be {@literal null}.\n * @return a page of entities\n */\n Page\u003CT> findAll(Pageable pageable);\n}\n",[59,102613,102614,102646,102650,102654,102659,102664,102676,102696,102705,102709,102727,102731,102735,102740,102745,102749,102761,102778,102787,102791,102810],{"__ignoreMap":57},[62,102615,102616,102618,102620,102622,102624,102626,102628,102630,102632,102634,102636,102638,102640,102642,102644],{"class":64,"line":65},[62,102617,116],{"class":68},[62,102619,8531],{"class":68},[62,102621,101835],{"class":122},[62,102623,760],{"class":72},[62,102625,71267],{"class":68},[62,102627,976],{"class":72},[62,102629,8845],{"class":68},[62,102631,3135],{"class":72},[62,102633,71594],{"class":68},[62,102635,22395],{"class":122},[62,102637,760],{"class":72},[62,102639,71267],{"class":68},[62,102641,976],{"class":72},[62,102643,8845],{"class":68},[62,102645,8552],{"class":72},[62,102647,102648],{"class":64,"line":76},[62,102649,79],{"emptyLinePlaceholder":13},[62,102651,102652],{"class":64,"line":82},[62,102653,164],{"class":85},[62,102655,102656],{"class":64,"line":89},[62,102657,102658],{"class":85}," * Returns all entities sorted by the given options.\n",[62,102660,102661],{"class":64,"line":95},[62,102662,102663],{"class":85}," *\n",[62,102665,102666,102668,102670,102673],{"class":64,"line":101},[62,102667,329],{"class":85},[62,102669,25823],{"class":68},[62,102671,102672],{"class":889}," sort",[62,102674,102675],{"class":85}," the {@link Sort} specification to sort the results by,\n",[62,102677,102678,102681,102684,102687,102690,102693],{"class":64,"line":107},[62,102679,102680],{"class":85}," * can be {",[62,102682,102683],{"class":68},"@link",[62,102685,102686],{"class":122}," Sort",[62,102688,102689],{"class":85},"#",[62,102691,102692],{"class":889},"unsorted()",[62,102694,102695],{"class":85},"}, must not be {@literal null}.\n",[62,102697,102698,102700,102702],{"class":64,"line":113},[62,102699,329],{"class":85},[62,102701,332],{"class":68},[62,102703,102704],{"class":85}," all entities sorted by the given options\n",[62,102706,102707],{"class":64,"line":129},[62,102708,188],{"class":85},[62,102710,102711,102714,102716,102718,102720,102723,102725],{"class":64,"line":134},[62,102712,102713],{"class":72}," Iterable\u003C",[62,102715,71267],{"class":68},[62,102717,3135],{"class":72},[62,102719,10287],{"class":122},[62,102721,102722],{"class":72},"(Sort ",[62,102724,7228],{"class":889},[62,102726,1133],{"class":72},[62,102728,102729],{"class":64,"line":156},[62,102730,79],{"emptyLinePlaceholder":13},[62,102732,102733],{"class":64,"line":161},[62,102734,164],{"class":85},[62,102736,102737],{"class":64,"line":167},[62,102738,102739],{"class":85}," * Returns a {@link Page} of entities meeting the paging restriction provided in\n",[62,102741,102742],{"class":64,"line":173},[62,102743,102744],{"class":85}," * the {@link Pageable} object.\n",[62,102746,102747],{"class":64,"line":179},[62,102748,102663],{"class":85},[62,102750,102751,102753,102755,102758],{"class":64,"line":185},[62,102752,329],{"class":85},[62,102754,25823],{"class":68},[62,102756,102757],{"class":889}," pageable",[62,102759,102760],{"class":85}," the pageable to request a paged result, can be\n",[62,102762,102763,102766,102768,102771,102773,102776],{"class":64,"line":191},[62,102764,102765],{"class":85}," * {",[62,102767,102683],{"class":68},[62,102769,102770],{"class":122}," Pageable",[62,102772,102689],{"class":85},[62,102774,102775],{"class":889},"unpaged()",[62,102777,102695],{"class":85},[62,102779,102780,102782,102784],{"class":64,"line":209},[62,102781,329],{"class":85},[62,102783,332],{"class":68},[62,102785,102786],{"class":85}," a page of entities\n",[62,102788,102789],{"class":64,"line":220},[62,102790,188],{"class":85},[62,102792,102793,102796,102798,102800,102802,102805,102808],{"class":64,"line":226},[62,102794,102795],{"class":72}," Page\u003C",[62,102797,71267],{"class":68},[62,102799,3135],{"class":72},[62,102801,10287],{"class":122},[62,102803,102804],{"class":72},"(Pageable ",[62,102806,102807],{"class":889},"pageable",[62,102809,1133],{"class":72},[62,102811,102812],{"class":64,"line":231},[62,102813,379],{"class":72},[22,102815,102816,102817,102822,102823,102826],{},"Pageable is actually an interface but there is an implementation of that interface that we can use called ",[677,102818,102821],{"href":102819,"rel":102820},"https://docs.spring.io/spring-data/commons/docs/current/api/org/springframework/data/domain/PageRequest.html",[681],"PageRequest",". If you look at the API there is a static method ",[59,102824,102825],{},"of(int page, int size)"," that takes the page and size of the request.",[22,102828,102829,102830,102833],{},"The page is the page number which starts at 0 and the size the number of records to display per page. This means that if you want the first 10 records you can call ",[59,102831,102832],{},"PageRequest.of(0,10)",". With that information you should have what you need to create an endpoint that allows the consumer to provide those values.",[52,102835,102837],{"className":54,"code":102836,"language":56,"meta":57,"style":57},"@RestController\n@RequestMapping(\"/api/people\")\npublic class PersonController {\n\n private final PersonRepository repository;\n\n public PersonController(PersonRepository repository) {\n this.repository = repository;\n }\n\n @GetMapping\n public Page\u003CPerson> findAll(@RequestParam int page, @RequestParam int size) {\n PageRequest pr = PageRequest.of(page,size);\n return repository.findAll(pr);\n }\n\n}\n",[59,102838,102839,102845,102857,102867,102871,102879,102883,102895,102905,102909,102913,102919,102952,102967,102978,102982,102986],{"__ignoreMap":57},[62,102840,102841,102843],{"class":64,"line":65},[62,102842,942],{"class":72},[62,102844,2342],{"class":68},[62,102846,102847,102849,102851,102853,102855],{"class":64,"line":76},[62,102848,942],{"class":72},[62,102850,10592],{"class":68},[62,102852,2109],{"class":72},[62,102854,102533],{"class":1675},[62,102856,2212],{"class":72},[62,102858,102859,102861,102863,102865],{"class":64,"line":82},[62,102860,116],{"class":68},[62,102862,119],{"class":68},[62,102864,102544],{"class":122},[62,102866,126],{"class":72},[62,102868,102869],{"class":64,"line":89},[62,102870,79],{"emptyLinePlaceholder":13},[62,102872,102873,102875,102877],{"class":64,"line":95},[62,102874,137],{"class":68},[62,102876,458],{"class":68},[62,102878,101952],{"class":72},[62,102880,102881],{"class":64,"line":101},[62,102882,79],{"emptyLinePlaceholder":13},[62,102884,102885,102887,102889,102891,102893],{"class":64,"line":107},[62,102886,194],{"class":68},[62,102888,102544],{"class":122},[62,102890,101965],{"class":72},[62,102892,23540],{"class":889},[62,102894,768],{"class":72},[62,102896,102897,102899,102901,102903],{"class":64,"line":113},[62,102898,2405],{"class":149},[62,102900,23549],{"class":72},[62,102902,146],{"class":68},[62,102904,23554],{"class":72},[62,102906,102907],{"class":64,"line":129},[62,102908,223],{"class":72},[62,102910,102911],{"class":64,"line":134},[62,102912,79],{"emptyLinePlaceholder":13},[62,102914,102915,102917],{"class":64,"line":156},[62,102916,2143],{"class":72},[62,102918,47319],{"class":68},[62,102920,102921,102923,102926,102928,102930,102932,102934,102936,102938,102941,102943,102945,102947,102950],{"class":64,"line":161},[62,102922,194],{"class":68},[62,102924,102925],{"class":72}," Page\u003C",[62,102927,32459],{"class":68},[62,102929,3135],{"class":72},[62,102931,10287],{"class":122},[62,102933,2475],{"class":72},[62,102935,2591],{"class":68},[62,102937,140],{"class":68},[62,102939,102940],{"class":889}," page",[62,102942,39583],{"class":72},[62,102944,2591],{"class":68},[62,102946,140],{"class":68},[62,102948,102949],{"class":889}," size",[62,102951,768],{"class":72},[62,102953,102954,102957,102959,102962,102964],{"class":64,"line":167},[62,102955,102956],{"class":72}," PageRequest pr ",[62,102958,146],{"class":68},[62,102960,102961],{"class":72}," PageRequest.",[62,102963,3298],{"class":122},[62,102965,102966],{"class":72},"(page,size);\n",[62,102968,102969,102971,102973,102975],{"class":64,"line":173},[62,102970,360],{"class":68},[62,102972,4069],{"class":72},[62,102974,10287],{"class":122},[62,102976,102977],{"class":72},"(pr);\n",[62,102979,102980],{"class":64,"line":179},[62,102981,223],{"class":72},[62,102983,102984],{"class":64,"line":185},[62,102985,79],{"emptyLinePlaceholder":13},[62,102987,102988],{"class":64,"line":191},[62,102989,379],{"class":72},[22,102991,102992,102993,102997],{},"If you run the application and visit ",[677,102994,102995],{"href":102995,"rel":102996},"http://localhost:8080/api/people?page=0&size=3",[681]," you should see 3 records displayed. If you change the page request parameter to 1 you should see the next 3 people in the database.",[22,102999,103000,103001,103004],{},"If you notice the return type of the endpoint it is a ",[59,103002,103003],{},"Page\u003CPerson>",". At this point it would serve you well to inspect the JSON returned from the API. There is a lot of data in there that might be useful to the client calling your API.",[52,103006,103008],{"className":54,"code":103007,"language":56,"meta":57,"style":57},"{\n \"content\": [\n {\n \"id\": 1,\n \"firstName\": \"Britany\",\n \"lastName\": \"Pacocha\",\n \"phoneNumber\": \"287-165-0052\",\n \"email\": \"andera.prohaska@hotmail.com\",\n \"address\": {\n \"id\": 2,\n \"address\": \"4638 Elmer Turnpike\",\n \"city\": \"Mannfort\",\n \"state\": \"Mississippi\",\n \"zip\": \"08278\"\n }\n },\n {\n \"id\": 3,\n \"firstName\": \"Darrick\",\n \"lastName\": \"Koepp\",\n \"phoneNumber\": \"900.079.5978\",\n \"email\": \"gigi.langworth@gmail.com\",\n \"address\": {\n \"id\": 4,\n \"address\": \"77288 Darleen Isle\",\n \"city\": \"Walshstad\",\n \"state\": \"Washington\",\n \"zip\": \"62580\"\n }\n },\n {\n \"id\": 5,\n \"firstName\": \"Kristal\",\n \"lastName\": \"Simonis\",\n \"phoneNumber\": \"(599) 177-7520\",\n \"email\": \"jasmine.kozey@yahoo.com\",\n \"address\": {\n \"id\": 6,\n \"address\": \"7532 Kirlin Glens\",\n \"city\": \"North Lauren\",\n \"state\": \"New Mexico\",\n \"zip\": \"09585\"\n }\n }\n ],\n \"pageable\": {\n \"sort\": {\n \"empty\": true,\n \"sorted\": false,\n \"unsorted\": true\n },\n \"offset\": 0,\n \"pageNumber\": 0,\n \"pageSize\": 3,\n \"paged\": true,\n \"unpaged\": false\n },\n \"last\": false,\n \"totalPages\": 34,\n \"totalElements\": 100,\n \"first\": true,\n \"size\": 3,\n \"number\": 0,\n \"sort\": {\n \"empty\": true,\n \"sorted\": false,\n \"unsorted\": true\n },\n \"numberOfElements\": 3,\n \"empty\": false\n}\n",[59,103009,103010,103014,103024,103028,103039,103051,103063,103075,103087,103096,103107,103119,103131,103143,103153,103157,103161,103165,103175,103186,103197,103208,103219,103227,103238,103249,103260,103271,103280,103284,103288,103292,103303,103314,103325,103336,103347,103355,103366,103377,103388,103399,103408,103412,103416,103420,103429,103438,103449,103460,103469,103473,103484,103495,103506,103517,103526,103530,103541,103553,103564,103575,103586,103597,103606,103617,103628,103637,103641,103652,103661],{"__ignoreMap":57},[62,103011,103012],{"class":64,"line":65},[62,103013,3680],{"class":72},[62,103015,103016,103019,103021],{"class":64,"line":76},[62,103017,103018],{"class":1675}," \"content\"",[62,103020,1266],{"class":68},[62,103022,103023],{"class":72}," [\n",[62,103025,103026],{"class":64,"line":82},[62,103027,49857],{"class":72},[62,103029,103030,103033,103035,103037],{"class":64,"line":89},[62,103031,103032],{"class":1675}," \"id\"",[62,103034,1266],{"class":68},[62,103036,22038],{"class":149},[62,103038,3338],{"class":72},[62,103040,103041,103044,103046,103049],{"class":64,"line":95},[62,103042,103043],{"class":1675}," \"firstName\"",[62,103045,1266],{"class":68},[62,103047,103048],{"class":1675}," \"Britany\"",[62,103050,3338],{"class":72},[62,103052,103053,103056,103058,103061],{"class":64,"line":101},[62,103054,103055],{"class":1675}," \"lastName\"",[62,103057,1266],{"class":68},[62,103059,103060],{"class":1675}," \"Pacocha\"",[62,103062,3338],{"class":72},[62,103064,103065,103068,103070,103073],{"class":64,"line":107},[62,103066,103067],{"class":1675}," \"phoneNumber\"",[62,103069,1266],{"class":68},[62,103071,103072],{"class":1675}," \"287-165-0052\"",[62,103074,3338],{"class":72},[62,103076,103077,103080,103082,103085],{"class":64,"line":113},[62,103078,103079],{"class":1675}," \"email\"",[62,103081,1266],{"class":68},[62,103083,103084],{"class":1675}," \"andera.prohaska@hotmail.com\"",[62,103086,3338],{"class":72},[62,103088,103089,103092,103094],{"class":64,"line":129},[62,103090,103091],{"class":1675}," \"address\"",[62,103093,1266],{"class":68},[62,103095,126],{"class":72},[62,103097,103098,103101,103103,103105],{"class":64,"line":134},[62,103099,103100],{"class":1675}," \"id\"",[62,103102,1266],{"class":68},[62,103104,26797],{"class":149},[62,103106,3338],{"class":72},[62,103108,103109,103112,103114,103117],{"class":64,"line":156},[62,103110,103111],{"class":1675}," \"address\"",[62,103113,1266],{"class":68},[62,103115,103116],{"class":1675}," \"4638 Elmer Turnpike\"",[62,103118,3338],{"class":72},[62,103120,103121,103124,103126,103129],{"class":64,"line":161},[62,103122,103123],{"class":1675}," \"city\"",[62,103125,1266],{"class":68},[62,103127,103128],{"class":1675}," \"Mannfort\"",[62,103130,3338],{"class":72},[62,103132,103133,103136,103138,103141],{"class":64,"line":167},[62,103134,103135],{"class":1675}," \"state\"",[62,103137,1266],{"class":68},[62,103139,103140],{"class":1675}," \"Mississippi\"",[62,103142,3338],{"class":72},[62,103144,103145,103148,103150],{"class":64,"line":173},[62,103146,103147],{"class":1675}," \"zip\"",[62,103149,1266],{"class":68},[62,103151,103152],{"class":1675}," \"08278\"\n",[62,103154,103155],{"class":64,"line":179},[62,103156,29042],{"class":72},[62,103158,103159],{"class":64,"line":185},[62,103160,50124],{"class":72},[62,103162,103163],{"class":64,"line":191},[62,103164,49857],{"class":72},[62,103166,103167,103169,103171,103173],{"class":64,"line":209},[62,103168,103032],{"class":1675},[62,103170,1266],{"class":68},[62,103172,21946],{"class":149},[62,103174,3338],{"class":72},[62,103176,103177,103179,103181,103184],{"class":64,"line":220},[62,103178,103043],{"class":1675},[62,103180,1266],{"class":68},[62,103182,103183],{"class":1675}," \"Darrick\"",[62,103185,3338],{"class":72},[62,103187,103188,103190,103192,103195],{"class":64,"line":226},[62,103189,103055],{"class":1675},[62,103191,1266],{"class":68},[62,103193,103194],{"class":1675}," \"Koepp\"",[62,103196,3338],{"class":72},[62,103198,103199,103201,103203,103206],{"class":64,"line":231},[62,103200,103067],{"class":1675},[62,103202,1266],{"class":68},[62,103204,103205],{"class":1675}," \"900.079.5978\"",[62,103207,3338],{"class":72},[62,103209,103210,103212,103214,103217],{"class":64,"line":236},[62,103211,103079],{"class":1675},[62,103213,1266],{"class":68},[62,103215,103216],{"class":1675}," \"gigi.langworth@gmail.com\"",[62,103218,3338],{"class":72},[62,103220,103221,103223,103225],{"class":64,"line":242},[62,103222,103091],{"class":1675},[62,103224,1266],{"class":68},[62,103226,126],{"class":72},[62,103228,103229,103231,103233,103236],{"class":64,"line":247},[62,103230,103100],{"class":1675},[62,103232,1266],{"class":68},[62,103234,103235],{"class":149}," 4",[62,103237,3338],{"class":72},[62,103239,103240,103242,103244,103247],{"class":64,"line":252},[62,103241,103111],{"class":1675},[62,103243,1266],{"class":68},[62,103245,103246],{"class":1675}," \"77288 Darleen Isle\"",[62,103248,3338],{"class":72},[62,103250,103251,103253,103255,103258],{"class":64,"line":257},[62,103252,103123],{"class":1675},[62,103254,1266],{"class":68},[62,103256,103257],{"class":1675}," \"Walshstad\"",[62,103259,3338],{"class":72},[62,103261,103262,103264,103266,103269],{"class":64,"line":271},[62,103263,103135],{"class":1675},[62,103265,1266],{"class":68},[62,103267,103268],{"class":1675}," \"Washington\"",[62,103270,3338],{"class":72},[62,103272,103273,103275,103277],{"class":64,"line":281},[62,103274,103147],{"class":1675},[62,103276,1266],{"class":68},[62,103278,103279],{"class":1675}," \"62580\"\n",[62,103281,103282],{"class":64,"line":286},[62,103283,29042],{"class":72},[62,103285,103286],{"class":64,"line":291},[62,103287,50124],{"class":72},[62,103289,103290],{"class":64,"line":296},[62,103291,49857],{"class":72},[62,103293,103294,103296,103298,103301],{"class":64,"line":302},[62,103295,103032],{"class":1675},[62,103297,1266],{"class":68},[62,103299,103300],{"class":149}," 5",[62,103302,3338],{"class":72},[62,103304,103305,103307,103309,103312],{"class":64,"line":308},[62,103306,103043],{"class":1675},[62,103308,1266],{"class":68},[62,103310,103311],{"class":1675}," \"Kristal\"",[62,103313,3338],{"class":72},[62,103315,103316,103318,103320,103323],{"class":64,"line":314},[62,103317,103055],{"class":1675},[62,103319,1266],{"class":68},[62,103321,103322],{"class":1675}," \"Simonis\"",[62,103324,3338],{"class":72},[62,103326,103327,103329,103331,103334],{"class":64,"line":320},[62,103328,103067],{"class":1675},[62,103330,1266],{"class":68},[62,103332,103333],{"class":1675}," \"(599) 177-7520\"",[62,103335,3338],{"class":72},[62,103337,103338,103340,103342,103345],{"class":64,"line":326},[62,103339,103079],{"class":1675},[62,103341,1266],{"class":68},[62,103343,103344],{"class":1675}," \"jasmine.kozey@yahoo.com\"",[62,103346,3338],{"class":72},[62,103348,103349,103351,103353],{"class":64,"line":338},[62,103350,103091],{"class":1675},[62,103352,1266],{"class":68},[62,103354,126],{"class":72},[62,103356,103357,103359,103361,103364],{"class":64,"line":343},[62,103358,103100],{"class":1675},[62,103360,1266],{"class":68},[62,103362,103363],{"class":149}," 6",[62,103365,3338],{"class":72},[62,103367,103368,103370,103372,103375],{"class":64,"line":357},[62,103369,103111],{"class":1675},[62,103371,1266],{"class":68},[62,103373,103374],{"class":1675}," \"7532 Kirlin Glens\"",[62,103376,3338],{"class":72},[62,103378,103379,103381,103383,103386],{"class":64,"line":366},[62,103380,103123],{"class":1675},[62,103382,1266],{"class":68},[62,103384,103385],{"class":1675}," \"North Lauren\"",[62,103387,3338],{"class":72},[62,103389,103390,103392,103394,103397],{"class":64,"line":371},[62,103391,103135],{"class":1675},[62,103393,1266],{"class":68},[62,103395,103396],{"class":1675}," \"New Mexico\"",[62,103398,3338],{"class":72},[62,103400,103401,103403,103405],{"class":64,"line":376},[62,103402,103147],{"class":1675},[62,103404,1266],{"class":68},[62,103406,103407],{"class":1675}," \"09585\"\n",[62,103409,103410],{"class":64,"line":16333},[62,103411,29042],{"class":72},[62,103413,103414],{"class":64,"line":16349},[62,103415,223],{"class":72},[62,103417,103418],{"class":64,"line":16365},[62,103419,50066],{"class":72},[62,103421,103422,103425,103427],{"class":64,"line":16381},[62,103423,103424],{"class":1675}," \"pageable\"",[62,103426,1266],{"class":68},[62,103428,126],{"class":72},[62,103430,103431,103434,103436],{"class":64,"line":16402},[62,103432,103433],{"class":1675}," \"sort\"",[62,103435,1266],{"class":68},[62,103437,126],{"class":72},[62,103439,103440,103443,103445,103447],{"class":64,"line":16411},[62,103441,103442],{"class":1675}," \"empty\"",[62,103444,1266],{"class":68},[62,103446,1227],{"class":149},[62,103448,3338],{"class":72},[62,103450,103451,103454,103456,103458],{"class":64,"line":16427},[62,103452,103453],{"class":1675}," \"sorted\"",[62,103455,1266],{"class":68},[62,103457,1165],{"class":149},[62,103459,3338],{"class":72},[62,103461,103462,103465,103467],{"class":64,"line":16448},[62,103463,103464],{"class":1675}," \"unsorted\"",[62,103466,1266],{"class":68},[62,103468,69203],{"class":149},[62,103470,103471],{"class":64,"line":16457},[62,103472,50124],{"class":72},[62,103474,103475,103478,103480,103482],{"class":64,"line":16466},[62,103476,103477],{"class":1675}," \"offset\"",[62,103479,1266],{"class":68},[62,103481,150],{"class":149},[62,103483,3338],{"class":72},[62,103485,103486,103489,103491,103493],{"class":64,"line":16471},[62,103487,103488],{"class":1675}," \"pageNumber\"",[62,103490,1266],{"class":68},[62,103492,150],{"class":149},[62,103494,3338],{"class":72},[62,103496,103497,103500,103502,103504],{"class":64,"line":16487},[62,103498,103499],{"class":1675}," \"pageSize\"",[62,103501,1266],{"class":68},[62,103503,21946],{"class":149},[62,103505,3338],{"class":72},[62,103507,103508,103511,103513,103515],{"class":64,"line":16504},[62,103509,103510],{"class":1675}," \"paged\"",[62,103512,1266],{"class":68},[62,103514,1227],{"class":149},[62,103516,3338],{"class":72},[62,103518,103519,103522,103524],{"class":64,"line":16517},[62,103520,103521],{"class":1675}," \"unpaged\"",[62,103523,1266],{"class":68},[62,103525,73447],{"class":149},[62,103527,103528],{"class":64,"line":16523},[62,103529,32848],{"class":72},[62,103531,103532,103535,103537,103539],{"class":64,"line":16532},[62,103533,103534],{"class":1675}," \"last\"",[62,103536,1266],{"class":68},[62,103538,1165],{"class":149},[62,103540,3338],{"class":72},[62,103542,103543,103546,103548,103551],{"class":64,"line":16546},[62,103544,103545],{"class":1675}," \"totalPages\"",[62,103547,1266],{"class":68},[62,103549,103550],{"class":149}," 34",[62,103552,3338],{"class":72},[62,103554,103555,103558,103560,103562],{"class":64,"line":16557},[62,103556,103557],{"class":1675}," \"totalElements\"",[62,103559,1266],{"class":68},[62,103561,26347],{"class":149},[62,103563,3338],{"class":72},[62,103565,103566,103569,103571,103573],{"class":64,"line":16563},[62,103567,103568],{"class":1675}," \"first\"",[62,103570,1266],{"class":68},[62,103572,1227],{"class":149},[62,103574,3338],{"class":72},[62,103576,103577,103580,103582,103584],{"class":64,"line":16572},[62,103578,103579],{"class":1675}," \"size\"",[62,103581,1266],{"class":68},[62,103583,21946],{"class":149},[62,103585,3338],{"class":72},[62,103587,103588,103591,103593,103595],{"class":64,"line":16581},[62,103589,103590],{"class":1675}," \"number\"",[62,103592,1266],{"class":68},[62,103594,150],{"class":149},[62,103596,3338],{"class":72},[62,103598,103599,103602,103604],{"class":64,"line":16590},[62,103600,103601],{"class":1675}," \"sort\"",[62,103603,1266],{"class":68},[62,103605,126],{"class":72},[62,103607,103608,103611,103613,103615],{"class":64,"line":16599},[62,103609,103610],{"class":1675}," \"empty\"",[62,103612,1266],{"class":68},[62,103614,1227],{"class":149},[62,103616,3338],{"class":72},[62,103618,103619,103622,103624,103626],{"class":64,"line":22013},[62,103620,103621],{"class":1675}," \"sorted\"",[62,103623,1266],{"class":68},[62,103625,1165],{"class":149},[62,103627,3338],{"class":72},[62,103629,103630,103633,103635],{"class":64,"line":22023},[62,103631,103632],{"class":1675}," \"unsorted\"",[62,103634,1266],{"class":68},[62,103636,69203],{"class":149},[62,103638,103639],{"class":64,"line":22049},[62,103640,32848],{"class":72},[62,103642,103643,103646,103648,103650],{"class":64,"line":22054},[62,103644,103645],{"class":1675}," \"numberOfElements\"",[62,103647,1266],{"class":68},[62,103649,21946],{"class":149},[62,103651,3338],{"class":72},[62,103653,103654,103657,103659],{"class":64,"line":22059},[62,103655,103656],{"class":1675}," \"empty\"",[62,103658,1266],{"class":68},[62,103660,73447],{"class":149},[62,103662,103663],{"class":64,"line":22064},[62,103664,379],{"class":72},[26,103666,1499],{"id":1498},[22,103668,103669],{},"Congratulations 🥳 You just created an application that uses the Spring Data Paging and Sorting Repository. This allows the client to display a small number of records when working with a large dataset.",[22,103671,103672,103673,103678],{},"If you want to check out the code for this tutorial you can view ",[677,103674,103677],{"href":103675,"rel":103676},"https://github.com/danvega/graphql-paging",[681],"this repo on Github"," which is part of a bigger tutorial I am working on. If you have any questions about what you built today, please let me know. As always friends...",[22,103680,80332,103681,82545],{},[36006,103682],{},[1527,103684,103685],{},"html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .spoOn, html code.shiki .spoOn{--shiki-default:#E36209;--shiki-dark:#FFAB70;--shiki-sepia:#E36209}html pre.shiki code .s4-Ip, html code.shiki .s4-Ip{--shiki-default:#6A737D;--shiki-dark:#6A737D;--shiki-sepia:#6A737D}",{"title":57,"searchDepth":76,"depth":76,"links":103687},[103688,103694],{"id":101221,"depth":76,"text":101222,"children":103689},[103690,103691,103692,103693],{"id":101715,"depth":82,"text":101716},{"id":101856,"depth":82,"text":101857},{"id":102168,"depth":82,"text":102169},{"id":101207,"depth":82,"text":101204},{"id":1498,"depth":76,"text":1499},{"_id":103696,"path":103697,"title":103698,"description":103699,"meta":103700,"body":103705},"content/blog/2022/05/11/spring-boot-value-annotation.md","/blog/2022/05/11/spring-boot-value-annotation","Spring Boot @Value Annotation","A brief introduction to the @Value annotation in Spring Boot.",{"slug":103701,"date":103702,"published":13,"tags":103703,"author":17,"cover":103704,"excerpt":-1},"spring-boot-value-annotation","2022-05-11T09:30:00.000Z",[11002],"./spring-boot-value-annotation-thumbnail.png",{"type":19,"value":103706,"toc":104275},[103707,103710,103716,103718,103725,103730,103751,103762,103825,103837,103850,103861,103933,103949,104039,104046,104055,104143,104146,104182,104186,104192,104201,104208,104221,104227,104233,104237,104242,104251,104254,104256,104268,104272],[22,103708,103709],{},"Spring Boot allows you to configure your application through a number of property sources. A property source, simply put, is a source for your configuration such as Java properties files, YAML files, environment variables, command line arguments and more.",[22,103711,103712,103713,103715],{},"In this article you will learn about how to set configuration properties and values and then inject them into your application using the ",[59,103714,14603],{}," annotation. You will also learn about Spring Boot’s property source order and how to override configuration values.",[26,103717,103698],{"id":103701},[22,103719,103720,103721,103724],{},"For this example I am using a simple application that I bootstrapped using ",[677,103722,2903],{"href":40207,"rel":103723},[681]," and the only dependency you need is Spring Web.",[22,103726,103727],{},[653,103728],{"alt":24606,"src":103729},"/images/blog/2022/05/11/spring-init.png",[22,103731,103732,103733,103736,103737,103740,103741,103743,103744,103747,103748],{},"The first thing you will need to do is to create a new class named ",[59,103734,103735],{},"GreetingController"," and to mark it as a ",[59,103738,103739],{},"@RestController."," Next, create a method named ",[59,103742,18647],{}," that returns the hard coded String “",[646,103745,103746],{},"Hello, World","”. To make this accessible on the root context annotate it with ",[59,103749,103750],{},"@GetMapping.",[22,103752,103753,103754,103757,103758,103761],{},"If you were to run this application and open ",[677,103755,16942],{"href":16942,"rel":103756},[681]," you should see the text ",[646,103759,103760],{},"Hello, World!"," While this works and is a bit of a contrived example this greeting message might not be something you want to hard code. Any value that you want to be configurable should move to configuration.",[52,103763,103765],{"className":54,"code":103764,"language":56,"meta":57,"style":57},"@RestController\npublic class GreetingController {\n\n @GetMapping\n public String home() {\n return \"Hello, World!!\";\n }\n\n}\n",[59,103766,103767,103773,103784,103788,103794,103804,103813,103817,103821],{"__ignoreMap":57},[62,103768,103769,103771],{"class":64,"line":65},[62,103770,942],{"class":72},[62,103772,2342],{"class":68},[62,103774,103775,103777,103779,103782],{"class":64,"line":76},[62,103776,116],{"class":68},[62,103778,119],{"class":68},[62,103780,103781],{"class":122}," GreetingController",[62,103783,126],{"class":72},[62,103785,103786],{"class":64,"line":82},[62,103787,79],{"emptyLinePlaceholder":13},[62,103789,103790,103792],{"class":64,"line":89},[62,103791,2143],{"class":72},[62,103793,47319],{"class":68},[62,103795,103796,103798,103800,103802],{"class":64,"line":95},[62,103797,194],{"class":68},[62,103799,2469],{"class":72},[62,103801,18647],{"class":122},[62,103803,206],{"class":72},[62,103805,103806,103808,103811],{"class":64,"line":101},[62,103807,360],{"class":68},[62,103809,103810],{"class":1675}," \"Hello, World!!\"",[62,103812,153],{"class":72},[62,103814,103815],{"class":64,"line":107},[62,103816,223],{"class":72},[62,103818,103819],{"class":64,"line":113},[62,103820,79],{"emptyLinePlaceholder":13},[62,103822,103823],{"class":64,"line":129},[62,103824,379],{"class":72},[22,103826,103827,103828,2749,103830,103833,103834,103836],{},"One of the property sources at your disposal are config data such as ",[59,103829,1265],{},[59,103831,103832],{},"YAML"," variants. Open up ",[59,103835,1265],{}," and add the following key/value:",[52,103838,103840],{"className":1663,"code":103839,"language":1665,"meta":57,"style":57},"welcome.salutation=Hello\n",[59,103841,103842],{"__ignoreMap":57},[62,103843,103844,103847],{"class":64,"line":65},[62,103845,103846],{"class":122},"welcome.salutation",[62,103848,103849],{"class":1675},"=Hello\n",[22,103851,103852,103853,103857,103858,2755],{},"With the property set you need a way to inject that value of it into your controller. This is where the ",[677,103854,14603],{"href":103855,"rel":103856},"https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/beans/factory/annotation/Value.html",[681]," annotation will help by allowing us to use property-driven dependency injection. Create a new field of type String called ",[59,103859,103860],{},"welcomeSalutation",[52,103862,103864],{"className":54,"code":103863,"language":56,"meta":57,"style":57},"@RestController\npublic class GreetingController {\n\n private String welcomeSalutation;\n\n @GetMapping\n public String home() {\n return \"Hello, World!!\";\n }\n\n}\n",[59,103865,103866,103872,103882,103886,103893,103897,103903,103913,103921,103925,103929],{"__ignoreMap":57},[62,103867,103868,103870],{"class":64,"line":65},[62,103869,942],{"class":72},[62,103871,2342],{"class":68},[62,103873,103874,103876,103878,103880],{"class":64,"line":76},[62,103875,116],{"class":68},[62,103877,119],{"class":68},[62,103879,103781],{"class":122},[62,103881,126],{"class":72},[62,103883,103884],{"class":64,"line":82},[62,103885,79],{"emptyLinePlaceholder":13},[62,103887,103888,103890],{"class":64,"line":89},[62,103889,137],{"class":68},[62,103891,103892],{"class":72}," String welcomeSalutation;\n",[62,103894,103895],{"class":64,"line":95},[62,103896,79],{"emptyLinePlaceholder":13},[62,103898,103899,103901],{"class":64,"line":101},[62,103900,2143],{"class":72},[62,103902,47319],{"class":68},[62,103904,103905,103907,103909,103911],{"class":64,"line":107},[62,103906,194],{"class":68},[62,103908,2469],{"class":72},[62,103910,18647],{"class":122},[62,103912,206],{"class":72},[62,103914,103915,103917,103919],{"class":64,"line":113},[62,103916,360],{"class":68},[62,103918,103810],{"class":1675},[62,103920,153],{"class":72},[62,103922,103923],{"class":64,"line":129},[62,103924,223],{"class":72},[62,103926,103927],{"class":64,"line":134},[62,103928,79],{"emptyLinePlaceholder":13},[62,103930,103931],{"class":64,"line":156},[62,103932,379],{"class":72},[22,103934,103935,103936,103938,103939,103942,103943,103946,103947,22831],{},"You can now add the ",[59,103937,14603],{}," annotation which expects The actual value expression such as ",[59,103940,103941],{},"#{systemProperties.myProp}","or property placeholder such as ",[59,103944,103945],{},"${my.app.myProp}."," Instead of returning the hard coded salutation you can now use the field in the return statement of your ",[59,103948,78742],{},[52,103950,103952],{"className":54,"code":103951,"language":56,"meta":57,"style":57},"@RestController\npublic class GreetingController {\n\n @Value(\"${welcome.salutation}\")\n private String welcomeSalutation;\n\n @GetMapping\n public String home() {\n return welcomeSalutation + \", World!\";\n }\n\n}\n",[59,103953,103954,103960,103970,103974,103987,103993,103997,104003,104013,104027,104031,104035],{"__ignoreMap":57},[62,103955,103956,103958],{"class":64,"line":65},[62,103957,942],{"class":72},[62,103959,2342],{"class":68},[62,103961,103962,103964,103966,103968],{"class":64,"line":76},[62,103963,116],{"class":68},[62,103965,119],{"class":68},[62,103967,103781],{"class":122},[62,103969,126],{"class":72},[62,103971,103972],{"class":64,"line":82},[62,103973,79],{"emptyLinePlaceholder":13},[62,103975,103976,103978,103980,103982,103985],{"class":64,"line":89},[62,103977,2143],{"class":72},[62,103979,14632],{"class":68},[62,103981,2109],{"class":72},[62,103983,103984],{"class":1675},"\"${welcome.salutation}\"",[62,103986,2212],{"class":72},[62,103988,103989,103991],{"class":64,"line":95},[62,103990,137],{"class":68},[62,103992,103892],{"class":72},[62,103994,103995],{"class":64,"line":101},[62,103996,79],{"emptyLinePlaceholder":13},[62,103998,103999,104001],{"class":64,"line":107},[62,104000,2143],{"class":72},[62,104002,47319],{"class":68},[62,104004,104005,104007,104009,104011],{"class":64,"line":113},[62,104006,194],{"class":68},[62,104008,2469],{"class":72},[62,104010,18647],{"class":122},[62,104012,206],{"class":72},[62,104014,104015,104017,104020,104022,104025],{"class":64,"line":129},[62,104016,360],{"class":68},[62,104018,104019],{"class":72}," welcomeSalutation ",[62,104021,1148],{"class":68},[62,104023,104024],{"class":1675}," \", World!\"",[62,104026,153],{"class":72},[62,104028,104029],{"class":64,"line":134},[62,104030,223],{"class":72},[62,104032,104033],{"class":64,"line":156},[62,104034,79],{"emptyLinePlaceholder":13},[62,104036,104037],{"class":64,"line":161},[62,104038,379],{"class":72},[22,104040,104041,104042,104045],{},"If you run the application you should see the same Hello, World! text displayed on the page. If you change the value in ",[59,104043,104044],{},"applications.properties"," and restart the app you should see that change reflected. Congrats, you have now externalized the value of welcomeSalutation to a property source.",[22,104047,104048,104049,104051,104052,104054],{},"You might not realize it yet but there is a problem you have solved for. If you remove the property from ",[59,104050,1265],{}," and try to run the application you will get an error. Spring is trying inject a value for a property it can’t find in any of its property sources. For this situation you should provide a default value in the case that ",[59,104053,103860],{}," isn’t set. You can do so by adding a colon after the property name and supplying the default value.",[52,104056,104058],{"className":54,"code":104057,"language":56,"meta":57,"style":57},"@RestController\npublic class GreetingController {\n\n @Value(\"${welcome.salutation:👋🏻 Hello}\")\n private String welcomeSalutation;\n\n @GetMapping\n public String home() {\n return welcomeSalutation + \", World!\";\n }\n\n}\n",[59,104059,104060,104066,104076,104080,104093,104099,104103,104109,104119,104131,104135,104139],{"__ignoreMap":57},[62,104061,104062,104064],{"class":64,"line":65},[62,104063,942],{"class":72},[62,104065,2342],{"class":68},[62,104067,104068,104070,104072,104074],{"class":64,"line":76},[62,104069,116],{"class":68},[62,104071,119],{"class":68},[62,104073,103781],{"class":122},[62,104075,126],{"class":72},[62,104077,104078],{"class":64,"line":82},[62,104079,79],{"emptyLinePlaceholder":13},[62,104081,104082,104084,104086,104088,104091],{"class":64,"line":89},[62,104083,2143],{"class":72},[62,104085,14632],{"class":68},[62,104087,2109],{"class":72},[62,104089,104090],{"class":1675},"\"${welcome.salutation:👋🏻 Hello}\"",[62,104092,2212],{"class":72},[62,104094,104095,104097],{"class":64,"line":95},[62,104096,137],{"class":68},[62,104098,103892],{"class":72},[62,104100,104101],{"class":64,"line":101},[62,104102,79],{"emptyLinePlaceholder":13},[62,104104,104105,104107],{"class":64,"line":107},[62,104106,2143],{"class":72},[62,104108,47319],{"class":68},[62,104110,104111,104113,104115,104117],{"class":64,"line":113},[62,104112,194],{"class":68},[62,104114,2469],{"class":72},[62,104116,18647],{"class":122},[62,104118,206],{"class":72},[62,104120,104121,104123,104125,104127,104129],{"class":64,"line":129},[62,104122,360],{"class":68},[62,104124,104019],{"class":72},[62,104126,1148],{"class":68},[62,104128,104024],{"class":1675},[62,104130,153],{"class":72},[62,104132,104133],{"class":64,"line":134},[62,104134,223],{"class":72},[62,104136,104137],{"class":64,"line":156},[62,104138,79],{"emptyLinePlaceholder":13},[62,104140,104141],{"class":64,"line":161},[62,104142,379],{"class":72},[22,104144,104145],{},"Another great feature is that property values can be contrived from other properties:",[52,104147,104149],{"className":54,"code":104148,"language":56,"meta":57,"style":57},"welcome.salutation=Hello\nwelcome.name=Dan\nwelcome.greeting=${welcome.salutation}, ${welcome.name}!\n",[59,104150,104151,104160,104170],{"__ignoreMap":57},[62,104152,104153,104155,104157],{"class":64,"line":65},[62,104154,103846],{"class":72},[62,104156,146],{"class":68},[62,104158,104159],{"class":72},"Hello\n",[62,104161,104162,104165,104167],{"class":64,"line":76},[62,104163,104164],{"class":72},"welcome.name",[62,104166,146],{"class":68},[62,104168,104169],{"class":72},"Dan\n",[62,104171,104172,104175,104177,104180],{"class":64,"line":82},[62,104173,104174],{"class":72},"welcome.greeting",[62,104176,146],{"class":68},[62,104178,104179],{"class":72},"${welcome.salutation}, ${welcome.name}",[62,104181,8848],{"class":68},[26,104183,104185],{"id":104184},"spring-boot-property-source-order","Spring Boot Property Source Order",[22,104187,104188,104189,104191],{},"Now that you understand how to set a property and inject it into your application using the ",[59,104190,14603],{}," annotation you need to understand the property source order. Spring Boot lets you externalize your configuration so that you can work with the same application code in different environments.",[22,104193,104194,104195,104200],{},"This means that while the welcome salutation is one value for local development it might be something different in another environment. I’m not going to cover every single property source here because the ",[677,104196,104199],{"href":104197,"rel":104198},"https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.external-config",[681],"Spring Boot Reference documentation"," does a great job of that. All you need to know is that as you go down the list of property sources they can override previous ones. This means that command-line arguments override anything in config data (application.properties).",[22,104202,104203,104204,104207],{},"To test this out you can set a command line argument in IntelliJ by going to ",[59,104205,104206],{},"Run > Edit Configurations > Environment > Program Arguments"," and add the following arg:",[52,104209,104211],{"className":1663,"code":104210,"language":1665,"meta":57,"style":57},"--welcome.salutation=🤩Hello\n",[59,104212,104213],{"__ignoreMap":57},[62,104214,104215,104218],{"class":64,"line":65},[62,104216,104217],{"class":122},"--welcome.salutation",[62,104219,104220],{"class":1675},"=🤩Hello\n",[22,104222,104223],{},[653,104224],{"alt":104225,"src":104226},"command-line arguments","/images/blog/2022/05/11/command-line-arguments.png",[22,104228,104229,104230,104232],{},"Even with a value set in ",[59,104231,1265],{}," this command line argument will now be used because of the property source order. Restart the application and see this change take affect.",[26,104234,104236],{"id":104235},"configuration-properties","Configuration Properties",[22,104238,3521,104239,104241],{},[59,104240,14603],{}," annotation is the easiest way to get a single value from a property source into your Spring application. One of the downsides is that there is no metadata generated for these properties and because of that you won’t get any Intellisense in your property files.",[22,104243,104244,104245,104247,104248,104250],{},"If you have one or more related properties you should really move them into a ",[59,104246,55054],{}," class and with that you will get metadata generated for them. I have a tutorial on creating a class or record using ",[59,104249,55054],{}," and setting default values that you can check out below.",[36089,104252],{"id":104253},"Gqn_q2sAebg",[26,104255,1499],{"id":1498},[22,104257,55935,104258,104260,104261,104263,104264,104267],{},[59,104259,14603],{}," annotation is the easiest way to inject configuration values into your application it might not always be the right choice. Knowing when to use it and when to reach for ",[59,104262,55054],{}," is half the battle (",[4534,104265,104266],{},"Go Joe!","). The other part of this is understanding that Spring Boot has a number of property sources and that there is a specific order in which they are loaded. I hope this tutorial helped you out and always friends...",[22,104269,36004,104270,82545],{},[36006,104271],{},[1527,104273,104274],{},"html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}",{"title":57,"searchDepth":76,"depth":76,"links":104276},[104277,104278,104279,104280],{"id":103701,"depth":76,"text":103698},{"id":104184,"depth":76,"text":104185},{"id":104235,"depth":76,"text":104236},{"id":1498,"depth":76,"text":1499},{"_id":104282,"path":104283,"title":104284,"description":104285,"meta":104286,"body":104291},"content/blog/2022/01/24/im-joining-vmware.md","/blog/2022/01/24/im-joining-vmware","Im Joining VMware","I'm so excited to announce that I am joining VMware as a Spring Developer Advocate!",{"slug":104287,"date":104288,"published":13,"tags":104289,"author":17,"cover":104290,"excerpt":-1},"im-joining-vmware","2022-01-24T08:00:00.000Z",[20701],"./im-joining-vmware-cover.png",{"type":19,"value":104292,"toc":104673},[104293,104302,104305,104311,104315,104318,104321,104324,104328,104331,104335,104350,104353,104361,104364,104369,104372,104376,104379,104382,104385,104405,104419,104425,104434,104437,104443,104446,104450,104453,104462,104465,104468,104471,104475,104481,104485,104488,104492,104495,104500,104503,104506,104509,104513,104521,104527,104530,104533,104536,104539,104545,104554,104557,104560,104563,104565,104568,104571,104574,104581,104587,104600,104603,104606,104665,104667],[22,104294,104295,104296,88515,104299,104301],{},"I am thrilled to announce that I have joined ",[646,104297,104298],{},"VMWare",[4534,104300,37107],{},". I have so many emotions going through my head right now as I sit down to write this article. The ones that stand out the most to me are excited and proud.",[22,104303,104304],{},"I’m excited for the opportunity that lies ahead and I know that this is just the start, there is a lot of work ahead. I’m proud of always believing in myself even when I had doubts if this was the career for me. I’m a self-taught developer and over my 22+ years, the only person I have been trying to prove anything to was myself. A job should never define you or your worth but this is a dream job and you better believe I’m going to celebrate this one 🥳",[22,104306,104307],{},[653,104308],{"alt":104309,"src":104310},"1642029002280.jpeg","/images/blog/2022/01/24/1642029002280.jpeg",[26,104312,104314],{"id":104313},"a-trip-down-spring-memory-lane","A Trip down Spring Memory Lane",[22,104316,104317],{},"If you have been following me and my content you know that I am a huge fan of Spring, the ecosystem, and the community. I thought I would take this opportunity to tell you how and when I got started with Spring as well as what I have been up to lately.",[22,104319,104320],{},"My first introduction to the Spring Framework was around 2012 when I did some freelancing on a few side projects. I remember being impressed with the versatility of the framework but I also remember it being incredibly complex to stand up a new project. Trying to figure out what dependencies you needed, which ones played nicely together, and how to configure a basic application was not easy.",[22,104322,104323],{},"In 2013 I was working for a company that was moving a lot of their ColdFusion-based web applications over to Groovy & Grails. I was a huge fan of Groovy because it made things that were complex in Java really easy. Grails was a convention over configuration framework that allowed us to focus on the business logic and less on the infrastructure. For those that weren’t around for this Groovy & Grails at this time did for us what modern Java & Spring Boot do today.",[636,104325,104327],{"id":104326},"springone-2013","SpringOne 2013",[22,104329,104330],{},"My company sent a few of us to SpringOne that year and my main goals for that conference were to see the rock stars of the Groovy & Grails world like Graeme Rocher, Burt Beckwith, Jeff Brown, Guillarme Laforge, Venkat Subramanian, Ken Kousen, Paul King, Dan Woods, and many more. I learned so much at that conference from them and a lot of others. I remember the sheer number of people at this conference and just being in awe. I was used to going to conferences where there were a few hundred and there were probably 2,000+ there.",[104332,104333,104334],"aside",{},"\n💡 Below is a picture from the room I stayed in and you can see the construction of what is now Levi Stadium in Santa Clara, where the San Francisco 49ers play. I also still have that USB key/bottle opener that I received at that conference!\n",[15944,104336,104338,104344],{"style":104337},"display:flex;gap:10px;",[22,104339,104340],{},[653,104341],{"alt":104342,"src":104343},"Levi Stadium","/images/blog/2022/01/24/spring_2013_room.jpg",[22,104345,104346],{},[653,104347],{"alt":104348,"src":104349},"Spring 2013 USB Key","/images/blog/2022/01/24/spring_2013_usb.jpg",[22,104351,104352],{},"It was in the opening night Keynote that I first heard about Spring Boot. Adrian Colyer and Dave Syer gave a good overview of what Spring Boot would do for Spring Developers. If you have never watched the keynote (or it's been a while) I would encourage you to check out just how far Spring has come since its debut!",[104354,104355],"iframe",{"width":104356,"height":104357,"src":104358,"title":104359,"frameBorder":1130,"allow":104360,"allowFullScreen":13},"100%",500,"https://www.youtube.com/embed/jplkJIHPGos","YouTube video player","accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",[22,104362,104363],{},"A quote that stuck out to me then and stays true today:",[29685,104365,104366],{},[22,104367,104368],{},"“Spring Boot really puts the developer enjoyment back.”",[22,104370,104371],{},"As I said before as a developer my experience with Spring wasn’t a great first impression. Spring could have a solution for every problem under the sun but if developers didn’t enjoy using it, it might not be as popular as it is today. While I was excited to hear about the work they were doing with Spring Boot it was still new and I was focused on working with Groovy and Grails.",[636,104373,104375],{"id":104374},"springone-2015","SpringOne 2015",[22,104377,104378],{},"I started playing with Spring Boot about 6 months later in April 2014 when version 1.0 was released. At this point, I wasn’t using it for anything serious but I do remember prototyping some applications at work to see if could solve some of the problems we were facing.",[22,104380,104381],{},"In 2015 I made my triumphant return to SpringOne which took place on September 14-17 at the Marriott in Washington DC. I remember this fondly because my coworker and friend Sam Farmer lived in DC and I got to spend some time with him and his family. The hotel and convention center was honestly one of the best settings for a conference I have ever been to. I also really enjoyed the chance to be a tourist in DC for a few days with some friends.",[104332,104383,104384],{},"\n💡 Here are a few images that I took from SpringOne 2015\n",[15944,104386,104387,104393,104399],{"style":104337},[22,104388,104389],{},[653,104390],{"alt":104391,"src":104392},"Spring 2015 Main Room","/images/blog/2022/01/24/spring_2015_main_room.jpeg",[22,104394,104395],{},[653,104396],{"alt":104397,"src":104398},"Spring 2015 Booths","/images/blog/2022/01/24/spring_2015_booths.jpeg",[22,104400,104401],{},[653,104402],{"alt":104403,"src":104404},"Spring 2015 Hotel","/images/blog/2022/01/24/spring_2015_hotel.jpeg",[15944,104406,104407,104413],{"style":104337},[22,104408,104409],{},[653,104410],{"alt":104411,"src":104412},"Spring 2015 Swag","/images/blog/2022/01/24/spring_2015_swag.jpeg",[22,104414,104415],{},[653,104416],{"alt":104417,"src":104418},"Spring 2015 Dave and Josh","/images/blog/2022/01/24/spring_2015_dave_josh.jpeg",[22,104420,104421,104422,104424],{},"This time around I was there to learn as much as I could about ",[646,104423,2925],{}," because at this point I realized it was going to take over the Java world. I mainly focused on beginner sessions around Spring Boot, Spring Data, Spring Cloud, and I focused on what questions developers were asking. I consumed as much knowledge as I could and I came home with a purpose.",[22,104426,104427,104428,104433],{},"I began working on my first course, ",[677,104429,104432],{"href":104430,"rel":104431},"https://www.udemy.com/course/spring-boot-intro/?referralCode=2B0F1F9DE0DC40C97DC5",[681],"Learn Spring Boot"," as soon as I got home. I realized that while there were some courses out there on Spring Boot, I could offer a different perspective. A lot of people believe that you need to be an expert to teach and that is a huge misconception. You only need to be one step ahead of your intended audience, which I now was.",[22,104435,104436],{},"I would launch this course on November 17th just in time for the big Udemy Black Friday Sale. This course would end up being almost 14 hours in length and as a sidebar, I would not recommend making that long of a course as your first one. As you can see from the screenshot this course has almost 24,000 students worldwide.",[22,104438,104439],{},[653,104440],{"alt":104441,"src":104442},"Learn Spring Boot Course","/images/blog/2022/01/24/learn_spring_boot_course.png",[22,104444,104445],{},"I cringe looking back at some of the videos I created for this course but you have to start somewhere. Don’t let production quality stop you if you know your content is good. I now have 6 courses and close to 140,000 students in 184 countries. That still blows my mind and I’m very grateful for that opportunity 🙏 I’m hoping I have some time this year to update my existing courses as well as the opportunity to create some new ones.",[636,104447,104449],{"id":104448},"springone-2021","SpringOne 2021",[22,104451,104452],{},"After attending a couple of conferences in person I was excited to be selected to speak at my first SpringOne. While this conference was virtual that didn’t change my excitement level. I was going to share the “virtual” stage with so many amazing speakers, some of which I have looked up to since that first conference in 2013.",[22,104454,104455,104456,104461],{},"My presentation was on ",[677,104457,104460],{"href":104458,"rel":104459},"https://www.danvega.dev/blog/2021/08/30/spring-one-2021/",[681],"Full-Stack Development with Spring Boot and Vue",". If you follow me you know I am a big fan of using Vue for frontend development. Much like Spring Boot does for backend development Vue simplifies so many things and lets me focus on building out my features.",[22,104463,104464],{},"A big advantage of giving a remote conference talk for me was that I was in a familiar setting. I create videos for YouTube so being able to sit down at my desk, have my external monitors, keyboard, and mouse made everything easy.",[22,104466,104467],{},"Well, easy until I got a text from my now boss Tasha telling me that Zoom had dropped on their end for everyone and that I was probably talking to air. She was right, I didn’t even notice and was just rambling to nobody. I logged right back in and without hesitation, I picked up right where I left off. I like to think that I impressed a few people, including Tasha with how calm and collected I was. What everyone fails to realize is that I have raised 2 babies during Covid, very few things rattle me these days 🤣",[104354,104469],{"width":104356,"height":104357,"src":104470,"title":104359,"frameBorder":1130,"allow":104360,"allowFullScreen":13},"https://www.youtube.com/embed/VkrGHqwSPVA",[636,104472,104474],{"id":104473},"springone-2022-beyond","SpringOne 2022 & Beyond!",[22,104476,104477,104478,2755],{},"My hope is that we return to an in-person conference this year and I get to spend some time talking to all of you. I can’t tell you how excited I am to hear what problems you’re facing and how I can help. In the meantime, if you’re interested in me speaking at your conference or user group please feel free to reach out to me on ",[677,104479,86336],{"href":93581,"rel":104480},[681],[26,104482,104484],{"id":104483},"what-have-i-been-up-to","What have I been up to?",[22,104486,104487],{},"After a quick history lesson, I thought I would take some time to tell you what I have been up to lately and what led to this opportunity.",[636,104489,104491],{"id":104490},"tech-elevator","Tech Elevator",[22,104493,104494],{},"I joined Tech Elevator back in 2018 and I can honestly say that no other company has had an impact on me the way they did. Tech Elevator is a coding BootCamp where you can learn how to code and the soft skills needed to start a career in software development. It was great to see people change careers and their lives after going through our program.",[22,104496,104497],{},[653,104498],{"alt":104491,"src":104499},"/images/blog/2022/01/24/tech_elevator.png",[22,104501,104502],{},"I was hired on as a Curriculum Developer. My job was to help improve the curriculum the students went through as well as the materials the instructors would use to teach with. We had 2 tracks, Java and .NET. In these tracks, the students would learn the skills needed to be well-rounded full-stack developers.",[22,104504,104505],{},"I was particularly excited to work on both the Java and Vue content. On the Java side, I helped us move from Java 8 to Java 11 and Spring + Eclipse over to Spring Boot & IntelliJ. I had a lot of fun updating content and in some cases rewriting entire days of the curriculum. I had a lot of fun improving our tests including our tests of tests which involved a lot of reflection 🤯",[22,104507,104508],{},"Even though I created my own curriculum in the past, Tech Elevator gave me a great guide to what it takes to create a really good experience for the instructor and the student. I miss it there, the energy the students came in with every day was infectious. I hope to make it back there and share some more wisdom with them this year.",[636,104510,104512],{"id":104511},"briebug","Briebug",[22,104514,104515,104516,104520],{},"I spent a year with ",[677,104517,104512],{"href":104518,"rel":104519},"https://briebug.com/",[681]," and I was recently promoted to Principal Enterprise Architect. Briebug is a consulting company and I worked on one client the entire time I was there. This client was the largest logistics company in the world and I got an opportunity to work on some really cool projects that were at a scale I wasn’t used to working on.",[22,104522,104523],{},[653,104524],{"alt":104525,"src":104526},"kari-shea-1SAnrIxw5OY-unsplash.jpg","/images/blog/2022/01/24/kari-shea-1SAnrIxw5OY-unsplash.jpg",[22,104528,104529],{},"All of the projects I worked on were projects using Spring Boot, Spring Cloud, and Pivotal Cloud Foundry (PCF). I also had the opportunity to design a new system from the ground up that ended up being a set of 10 or so Microservices.",[22,104531,104532],{},"What I really enjoyed about this process is that a lot of the team members were new to Microservices and Spring Cloud in general. I ended up setting up office hours meetings every single day that gave the developers the opportunity to ask questions, and if there were no questions that day I would go through code reviews and explain what changes we needed to make and why.",[22,104534,104535],{},"I really enjoy mentoring and teaching so these are some of my fondest memories over the past year. At the end of the day, I learned that I didn’t like consulting and working for 2 companies, it’s not for everyone. I enjoyed my time with Briebug, my coworkers, and the core values the company stands for.",[26,104537,104538],{"id":37106},"Spring Developer Advocate 🥑",[22,104540,104541],{},[653,104542],{"alt":104543,"src":104544},"mia-baker-ctRgcY-lY8I-unsplash.jpg","/images/blog/2022/01/24/mia-baker-ctRgcY-lY8I-unsplash.jpg",[22,104546,104547,104548,104553],{},"Now that you know a little bit of my history I want to take some time to tell you why this is a ",[646,104549,104550],{},[4534,104551,104552],{},"dream job"," for me. I have always had a passion for the art of Software Development. For as long as I can remember though what has brought me the most joy throughout my career is helping others.",[22,104555,104556],{},"I come from a time when there was no StackOverflow or YouTube. I remember struggling with something and thinking to myself, someone else is having a hard time with this too so I am going to blog about it and hopefully save someone the stress of figuring this out.",[22,104558,104559],{},"During this time I found my superpower. Has anyone ever asked you “What is your superpower”? I used to get asked this and I would have no answer but I do now. My superpower is the ability to break down complex problems into easy-to-understand byte-sized chunks, also referred to as problem-solving. I’m good at that and then explaining the problem and solution to others.",[22,104561,104562],{},"I think if you take the experience I have of being in this industry for 22 years, Course Development, Curriculum Development, Writing, YouTube, and my ability to connect with others it’s a surprise I didn’t end up in Advocacy sooner.",[636,104564,38104],{"id":38103},[22,104566,104567],{},"I’m ecstatic to join such a world-class organization and I’m not taking this opportunity lightly. In this role, I will get to help customers and developers by informing them of solutions to problems. I really hope to be an inspiration to you the way so many have been for me over the years.",[22,104569,104570],{},"I’m so thankful to my boss Tasha Isenberg for giving me this opportunity. Everyone I talk to her raves about her as a person and a Boss and I can already see why. I’m so excited to join such an amazing team of advocates who are all some of the best developers and teachers in the industry.",[22,104572,104573],{},"I’m excited to call Josh Long a coworker, he is someone I have looked up to for a long time and I’m eager to learn from him. I’m excited to work with Nate Schutta who is one of the easiest people In the world to have a conversation with. He also did something very important for me, he introduced me to Ted Lasso.",[104354,104575],{"src":104576,"width":104577,"height":104578,"frameBorder":1130,"className":104579,"allowFullScreen":13},"https://giphy.com/embed/ZYKLgA1pB1WSPQASCs",480,270,[104580],"giphy-embed",[22,104582,104583],{},[677,104584,104586],{"href":104585},"https://giphy.com/gifs/AppleTV-ted-lasso-tedlasso-ZYKLgA1pB1WSPQASCs","via GIPHY",[22,104588,104589,104590,104593,104594,104599],{},"The one thing I am really looking forward to as the world continues to open back up is to get back to in-person conferences. I hope to be speaking at a few more this year and I already have my eyes on ",[646,104591,104592],{},"SpringOne",". If you see me at a conference or a ",[677,104595,104598],{"href":104596,"rel":104597},"https://tanzu.vmware.com/developer/tv/springone-tour/",[681],"SpringOne Tour stop"," please say hello 👋🏻 and let’s talk about any of the problems you’re facing.",[26,104601,104602],{"id":37169},"Thank you 🙏",[22,104604,104605],{},"There are so many people that I would like to thank for being there for me throughout my career. I have certainly worked my butt off over the years but I have also been incredibly lucky to be surrounded by some really great people. In no particular order I want to thank the following people:",[915,104607,104608,104611,104614,104617,104620,104623,104626,104629,104632,104635,104638,104641,104644,104647,104650,104653,104656,104659,104662],{},[37,104609,104610],{},"My Wife & Kids ❤️",[37,104612,104613],{},"Tasha Isenberg",[37,104615,104616],{},"Dana Hawthorne",[37,104618,104619],{},"Ken Kousen",[37,104621,104622],{},"Nate Schutta",[37,104624,104625],{},"Josh Long",[37,104627,104628],{},"Jesse Sanders",[37,104630,104631],{},"John Kim",[37,104633,104634],{},"Andrew Veliath",[37,104636,104637],{},"David Winrich",[37,104639,104640],{},"Jason Delmore",[37,104642,104643],{},"Sam Farmer",[37,104645,104646],{},"Lance Staples",[37,104648,104649],{},"Phil Rodopolous",[37,104651,104652],{},"Todd Sharp",[37,104654,104655],{},"Raymond Camden",[37,104657,104658],{},"Tracy Ash",[37,104660,104661],{},"Sharat Chander",[37,104663,104664],{},"Julien Dubois",[26,104666,1499],{"id":1498},[22,104668,104669,104670,2755],{},"If you can’t tell I am beyond excited about the opportunity that lies ahead of me. I’m going to find a good way for you to reach out and let me know what content you want me to create around Spring. Until I have something in place please feel free to reach out to me on ",[677,104671,86336],{"href":93581,"rel":104672},[681],{"title":57,"searchDepth":76,"depth":76,"links":104674},[104675,104681,104685,104688,104689],{"id":104313,"depth":76,"text":104314,"children":104676},[104677,104678,104679,104680],{"id":104326,"depth":82,"text":104327},{"id":104374,"depth":82,"text":104375},{"id":104448,"depth":82,"text":104449},{"id":104473,"depth":82,"text":104474},{"id":104483,"depth":76,"text":104484,"children":104682},[104683,104684],{"id":104490,"depth":82,"text":104491},{"id":104511,"depth":82,"text":104512},{"id":37106,"depth":76,"text":104538,"children":104686},[104687],{"id":38103,"depth":82,"text":38104},{"id":37169,"depth":76,"text":104602},{"id":1498,"depth":76,"text":1499},{"_id":104691,"path":104692,"title":104693,"description":104694,"meta":104695,"body":104700},"content/blog/2022/01/01/happy-new-year-2022.md","/blog/2022/01/01/happy-new-year-2022","Happy New Year 2022","Happy New Year! I want to take a few minutes and reflect on the year that was 2021 and look ahead to 2022.",{"slug":104696,"date":104697,"published":13,"tags":104698,"author":17,"cover":104699,"excerpt":-1},"happy-new-year-2022","2022-01-01T11:00:00.000Z",[33475],"./kelly-sikkema-PXl_S152jNM-unsplash.jpg",{"type":19,"value":104701,"toc":104957},[104702,104704,104708,104710,104718,104721,104734,104740,104744,104747,104752,104767,104771,104807,104811,104814,104818,104823,104838,104841,104858,104861,104864,104870,104873,104887,104894,104897,104901,104904,104907,104916,104918,104925,104927,104934,104938,104944,104952,104954],[22,104703,104694],{},[26,104705,104707],{"id":104706},"_2021","2021",[636,104709,104512],{"id":104511},[22,104711,104712,104713,104717],{},"I started a new job this year at ",[677,104714,104716],{"href":104518,"rel":104715},[681],"Briebug Software"," as an Enterprise Architect. We are a consultancy and I spent this year working for a single client who happens to be a Fortune 50 company. The size of the company and the volume at which the applications run presented all sorts of new challenges for me. I got to work on a bunch of really cool projects running Spring Boot & Spring Cloud. Towards the end of the year, I was promoted to Principal Architect and can’t say enough good things about Briebug!",[636,104719,104592],{"id":104720},"springone",[22,104722,104723,104724,104728,104729,2755],{},"This year I was lucky to be selected as one of the speakers at SpringOne. I have attended a few Spring Conferences before and it is easily one of my favorites so this was a big deal and an honor for me. ",[677,104725,104727],{"href":104458,"rel":104726},[681],"My Presentation"," was on Full Stack Java Development with Spring Boot and Vue. I thought I did a really good job of preparing for this talk but nothing really prepares you for Zoom dropping off in the middle of your presentation. Thankfully I was alerted that it happened and I was able to log back in and finish the talk. I got some really good feedback on my talk and appreciate anyone who joined us live or watched ",[677,104730,104733],{"href":104731,"rel":104732},"https://springone.io/2021/sessions/full-stack-development-with-spring-boot-vuejs",[681],"the replay",[22,104735,104736],{},[653,104737],{"alt":104738,"src":104739},"Spring One","/images/blog/2022/01/01/spring-one.png",[636,104741,104743],{"id":104742},"creating-content","Creating Content",[22,104745,104746],{},"I love creating content and thankfully I was able to get back to it this year. Here are a few of my favorite pieces of content that I worked on this year.",[22,104748,104749],{},[646,104750,104751],{},"Blog",[915,104753,104754,104760],{},[37,104755,104756],{},[677,104757,104759],{"href":101085,"rel":104758},[681],"Full Stack Java Development with Spring Boot and VueJS",[37,104761,104762],{},[677,104763,104766],{"href":104764,"rel":104765},"https://www.danvega.dev/blog/2021/11/15/macbook-pro-m1-max-review/",[681],"14” MacBook Pro M1 Max Review",[22,104768,104769],{},[646,104770,37949],{},[915,104772,104773,104779,104786,104793,104800],{},[37,104774,104775],{},[677,104776,104778],{"href":86005,"rel":104777},[681],"Create your first Spring application (Without Spring Boot)",[37,104780,104781],{},[677,104782,104785],{"href":104783,"rel":104784},"https://youtu.be/97C3fQqzj-I",[681],"Github Copilot for Java Developers",[37,104787,104788],{},[677,104789,104792],{"href":104790,"rel":104791},"https://youtu.be/nGqVYiwu8uo",[681],"Log4J Vulnerability for Spring Boot Applications",[37,104794,104795],{},[677,104796,104799],{"href":104797,"rel":104798},"https://youtu.be/tdOoKKXlDCQ",[681],"Getting Started with Nuxt 3",[37,104801,104802],{},[677,104803,104806],{"href":104804,"rel":104805},"https://youtu.be/ZddzOO_ovz8",[681],"Java is now FREE for everyone (YouTube Short)",[26,104808,104810],{"id":104809},"_2022","2022",[22,104812,104813],{},"Now that I have 2 small kids at home I’m hesitant to put specific goals out in the world because I honestly don’t know If I can complete them. With that said here are some aspirations.",[636,104815,104817],{"id":104816},"move-my-website-over-to-nuxt-3","Move my website over to Nuxt 3",[22,104819,104820],{},[653,104821],{"alt":8474,"src":104822},"/images/blog/2022/01/01/danvega-dev-homepage.png",[22,104824,104825,104826,104831,104832,104837],{},"I’m really happy with my ",[677,104827,104830],{"href":104828,"rel":104829},"https://www.danvega.dev/",[681],"current website",". Every time I post a new article I head over to the site to make sure it looks ok and I remember just how proud I am of it. The current version of my website is written in a framework called ",[677,104833,104836],{"href":104834,"rel":104835},"https://gridsome.org/",[681],"Gridsome",", which is a static site generator for VueJS. I have been really happy with Gridsome but there are a few limitations that are stopping me from doing some of the things I want to do. That and the project hasn’t been moving forward that much over the last couple of years.",[22,104839,104840],{},"Enter Nuxt 3 which just went into beta and brings a whole bunch of exciting features. There are too many to mention here but these are my favorites so far",[915,104842,104843,104846,104849,104852,104855],{},[37,104844,104845],{},"Vue 3 Support",[37,104847,104848],{},"TypeScript Support",[37,104850,104851],{},"Vite - My favorite build tool",[37,104853,104854],{},"Nitro - Nuxt 3 Server",[37,104856,104857],{},"FAST!",[22,104859,104860],{},"Nuxt 3 is still in beta and while I love what they are doing there are still some things missing that are going to prevent me from moving over right now. The biggest one of all is static site generation which is coming soon 🤞🏻 Lucky for me I am in no rush to move over which gives me a great opportunity to spend some time learning Nuxt 3.",[636,104862,104863],{"id":2230},"Content",[22,104865,104866],{},[653,104867],{"alt":104868,"src":104869},"content creator","/images/blog/2022/01/01/onur-binay-O2-EZNGZIyk-unsplash.jpg",[22,104871,104872],{},"I was happy to be back at turning out content last year but after looking at my total numbers I know I can do better. I’m setting some lofty goals in this department but I think they are doable.",[915,104874,104875,104878,104881,104884],{},[37,104876,104877],{},"Blog: 1 post per month for a total of 12 posts",[37,104879,104880],{},"YouTube: 1 video per week for a total of 52 videos",[37,104882,104883],{},"Live Streaming: 2x a month for a total of 24 live streams",[37,104885,104886],{},"Courses: 2 New Courses",[22,104888,104889,104890,104893],{},"As you can see YouTube is where I want to focus this year. I’m really proud that I have been able to grow my channel to almost 14k subscribers and I have done so without being consistent. I looked back at my stats and I only published ",[646,104891,104892],{},"11 videos"," in 2021. That is less than 1 per month and I know I can do better than that.",[22,104895,104896],{},"You might be thinking that moving from 11 to 52 videos in a year is a lofty goal and I would completely agree. I think the one thing I have on my side is that I can create YouTube shorts much quicker than normal tutorials. Are you a fan of consuming shorts on YouTube? If so can you let me know what types of videos you want to see or some of your favorite creators that are creating shorts?",[636,104898,104900],{"id":104899},"read-more-books","Read more books",[22,104902,104903],{},"Whenever a read a new book I always end up asking myself, why don’t I do this more often. I read a few books towards the end of the year and it reminded me what I was missing. I am going to set a goal of reading 1 book per month for a total of 12 books in 2022.",[636,104905,38131],{"id":104906},"conferences",[22,104908,104909,104910,104915],{},"I’m really hoping that we return to somewhat of a normal conference season where we are all hanging out in person. I would love to speak at 4+ conferences this year and to do that I need to submit to a bunch of conferences. I need to get better and finding the conferences I want to speak at writing good abstracts. I keep all of my call for papers in a ",[677,104911,104914],{"href":104912,"rel":104913},"https://github.com/danvega/call-for-papers",[681],"Github repo"," if you’re interested in reading them. If you know of any good places to find lists of upcoming conferences I would love to hear about them.",[636,104917,37673],{"id":33895},[22,104919,104920,104921,2755],{},"I’m really happy that I got back to producing my newsletter on a weekly basis. I would like to continue that in 2022. I’m constantly looking at making small tweaks to my format and that is something I will continue to do. If you’re a subscriber and have a suggestions please send them my way. If you’re not a subscriber, ",[677,104922,104924],{"href":38005,"rel":104923},[681],"what are you waiting for",[636,104926,89804],{"id":89803},[22,104928,104929,104933],{},[677,104930,89804],{"href":104931,"rel":104932},"https://developer.oracle.com/javachampions/",[681]," are leaders in the Java community who are experts at what they do. I happen to know a few Java Champions and I look up to them and aspire to be like them. I feel like I do a lot for the Java community but I could be doing so much more. I am going to figure out more ways to contribute to the community who has given me so much so that one day I might join this list.",[636,104935,104937],{"id":104936},"charity","Charity",[22,104939,104940],{},[653,104941],{"alt":104942,"src":104943},"Toys for Shots","/images/blog/2022/01/01/toysforshots.png",[22,104945,104946,104947,104951],{},"Due to the pandemic the non profit organization I run has been unable to throw our big event, ",[677,104948,104942],{"href":104949,"rel":104950},"https://toysforshots.com/",[681],". This is something I hope we can back to this year. With that I would like to move our website over to Nuxt 3. I also want to build in some stripe integration so that we can take donations online which is one feature we are lacking. CLE Cares, is the name of our non-profit and we need to get a site setup for that. A lot of work to do here but it’s something I enjoy working on so I look forward to it.",[26,104953,1499],{"id":1498},[22,104955,104956],{},"I know it sounds silly but I look forward to the new year after the long holiday season. I’m excited for what this year will bring. I’m looking forward to doing more of what I love to do, learning and creating content. Thank you to all of you who continue to support me and I hope you have a happy and healthy 2022.",{"title":57,"searchDepth":76,"depth":76,"links":104958},[104959,104964,104973],{"id":104706,"depth":76,"text":104707,"children":104960},[104961,104962,104963],{"id":104511,"depth":82,"text":104512},{"id":104720,"depth":82,"text":104592},{"id":104742,"depth":82,"text":104743},{"id":104809,"depth":76,"text":104810,"children":104965},[104966,104967,104968,104969,104970,104971,104972],{"id":104816,"depth":82,"text":104817},{"id":2230,"depth":82,"text":104863},{"id":104899,"depth":82,"text":104900},{"id":104906,"depth":82,"text":38131},{"id":33895,"depth":82,"text":37673},{"id":89803,"depth":82,"text":89804},{"id":104936,"depth":82,"text":104937},{"id":1498,"depth":76,"text":1499},{"_id":104975,"path":104976,"title":104977,"description":104978,"meta":104979,"body":104985},"content/blog/2021/11/15/macbook-pro-m1-max-review.md","/blog/2021/11/15/macbook-pro-m1-max-review","14\" MacBook Pro M1 Max Review for developers and content creators","I have been using the new MacBook Pro for about a week and wanted to give you my initial thoughts from a developer & content creator perspective.",{"slug":104980,"date":104981,"published":13,"tags":104982,"author":17,"cover":104984,"excerpt":-1},"macbook-pro-m1-max-review","2021-11-15T21:00:00.000Z",[104983],"Apple","./macbook-pro-review-thumbnail.png",{"type":19,"value":104986,"toc":105258},[104987,104990,104999,105004,105007,105011,105014,105040,105043,105129,105132,105135,105142,105147,105150,105155,105159,105162,105165,105169,105172,105178,105181,105184,105190,105200,105204,105207,105212,105215,105218,105221,105225,105228,105235,105239,105244,105247,105250,105253,105255],[22,104988,104989],{},"I have been using the new MacBook Pro for about a week and wanted to give you my initial thoughts from a developer & content creator perspective. If you're looking for an in-depth review full of Geekbench benchmarks you aren't going to find them here. Instead, I will share with you what I like or don't like about it some day-to-day workflows I am using it for.",[30796,104991,104994,104995,104998],{"className":104992},[104993],"tldr","\n📖 ",[646,104996,104997],{},"TLDR;"," If you are not the patient type and want me to get right to the point, here it is. This is by far the best laptop I have ever used and it has me excited for the future of Apple Silicon.\n",[22,105000,105001],{},[653,105002],{"alt":105003,"src":105003},"https://images.unsplash.com/photo-1635310568932-47fd9c961c26?ixlib=rb-1.2.1&q=85&fm=jpg&crop=entropy&cs=srgb",[22,105005,105006],{},"If you are new around here I have been a software developer for over 20 years now. I have used everything from custom-built desktops to the laptop I am talking about today. I have used Linux, Windows, and macOS and for whatever reason, I have just really enjoyed my time on the mac. I also create content in the form of articles like the one you're reading now, videos for my YouTube channel, and courses that are consumed by students worldwide.",[26,105008,105010],{"id":105009},"_14-macbook-pro-m1-max","14\" MacBook Pro M1 Max",[22,105012,105013],{},"This is the custom configuration I ended up ordering:",[915,105015,105016,105019,105022,105025,105028,105031,105034,105037],{},[37,105017,105018],{},"Apple M1 Max with 10-core CPU, 24-core GPU, 16-core Neural Engine",[37,105020,105021],{},"32GB unified memory",[37,105023,105024],{},"1TB SSD storage",[37,105026,105027],{},"96w USB-C Power Adapter",[37,105029,105030],{},"14-inch Liquid Retina XDR display",[37,105032,105033],{},"Three Thunderbolt 4 ports, HDMI port, SDXC card slot, MagSafe 3 port",[37,105035,105036],{},"Backlit Magic Keyboard with Touch ID - US English",[37,105038,105039],{},"Accessory Kit",[22,105041,105042],{},"When Apple announced that both the 14\" & 16\" would be able to be configured with pretty much the same specs It was an easy decision for me to choose the 14\" model. My laptop is usually on my desk where it is hooked up to 2 external monitors so the screen size wasn't important. If you're curious about the monitors or other peripherals found on my desk here are a few:",[30796,105044,105047,105051,105123],{"className":105045},[105046],"promo",[636,105048,105050],{"id":105049},"whats-on-my-desk","What's on my desk:",[915,105052,105053,105060,105067,105074,105081,105088,105095,105102,105109,105116],{},[37,105054,105055],{},[677,105056,105059],{"href":105057,"rel":105058},"https://amzn.to/3C8IqQy",[681],"LG 4k Display",[37,105061,105062],{},[677,105063,105066],{"href":105064,"rel":105065},"https://amzn.to/3HcTobs",[681],"Dell UltraSharp U3415W 34-Inch Curved LED-Lit Monitor",[37,105068,105069],{},[677,105070,105073],{"href":105071,"rel":105072},"https://amzn.to/3quWxh0",[681],"Apple Magic Keyboard",[37,105075,105076],{},[677,105077,105080],{"href":105078,"rel":105079},"https://amzn.to/3F2gLCy",[681],"Logitech MX Master 3 Mouse",[37,105082,105083],{},[677,105084,105087],{"href":105085,"rel":105086},"https://amzn.to/3HdoZto",[681],"Audio-Technica AT2020 USB Microphone",[37,105089,105090],{},[677,105091,105094],{"href":105092,"rel":105093},"https://amzn.to/3Dk78P2",[681],"Sony A6100 DSLR Camera",[37,105096,105097],{},[677,105098,105101],{"href":105099,"rel":105100},"https://amzn.to/3c4fpea",[681],"Elgato Cam Link 4k",[37,105103,105104],{},[677,105105,105108],{"href":105106,"rel":105107},"https://amzn.to/3HdpnIm",[681],"Elgato Key Light * 2",[37,105110,105111],{},[677,105112,105115],{"href":105113,"rel":105114},"https://amzn.to/3qvw3vO",[681],"Apple AirPods Pro",[37,105117,105118],{},[677,105119,105122],{"href":105120,"rel":105121},"https://amzn.to/3F5Utjp",[681],"Wyze Wireless Noise Cancelling Headphones",[22,105124,105125],{},[105126,105127,105128],"small",{},"* Each of these are affiliate links at no extra cost to you but they do help support this website. Thank You 🙏",[22,105130,105131],{},"When I am not sitting at my desk I am using my standing desk or I am on the move and I appreciate the portability. When I am on the go whether it's doing some work at a coffee shop or traveling I like the smaller form factor.",[22,105133,105134],{},"Those were my initial thoughts when ordering the laptop and I'm super happy I went with the 14\". It's a really great size and it's got a little weight to it which, in a pro machine I appreciate. The XDR display is nothing short of amazing. Speaking of the display I heard a lot of people complaining about the notch and I can honestly say that after a day or two I haven't noticed it at all. When you move an app into full-screen it's totally gone and when you're not in full-screen it's not a distraction.",[22,105136,105137,105138,105141],{},"The keyboard is new and so far it's been a great experience. Usually, I prefer using my external keyboard (Apple Magic Keyboard) but I actually prefer typing on this one. The keys are smooth and have made for a really good typing experience. I was never a big fan of the touch bar so I was excited to see the physical function keys return. There is ",[59,105139,105140],{},"Fn"," key on the lower left-hand side of the keyboard that also serves as an emoji picker which I have found to be really handy.",[22,105143,105144],{},[653,105145],{"alt":105146,"src":105146},"https://images.unsplash.com/photo-1635310478752-cad8a1f77d9c?ixlib=rb-1.2.1&q=85&fm=jpg&crop=entropy&cs=srgb",[22,105148,105149],{},"I was also really happy to see the return of the ports. This laptop comes with 3 Thunderbolt 4 ports, an HDMI port, an SDXC card slot, and a MagSafe 3 port. When I'm presenting it's nice to have quick access to an HDMI port without having to use one of the many dongles I have to carry in my bag. When I am shooting a video it's nice to have quick access to an SD card reader. I think one of the knocks I heard most on the M1 laptops from a year ago was that they only had 2 Thunderbolt ports, having that extra one makes a big difference. The theme here is doing as much as we can without having to carry all of those connectors.",[22,105151,105152],{},[653,105153],{"alt":105154,"src":105154},"https://images.unsplash.com/photo-1635310567899-949f415d0e90?ixlib=rb-1.2.1&q=85&fm=jpg&crop=entropy&cs=srgb",[26,105156,105158],{"id":105157},"macbook-pro-m1-max-performance","MacBook Pro M1 Max Performance",[22,105160,105161],{},"Everything I have mentioned before is great but the real star of this laptop is Apple Silicon and the performance. I heard this was similar on the previous M1 laptops but the first thing you notice is how fast the laptop starts up when you open it up, almost instantly.",[22,105163,105164],{},"From day-to-day use, everything just feels smooth, fast, and snappy. The other thing I have noticed is that the fans either are not kicking on or I am just not hearing them. This isn't the case with my 2015 & 2019 MacBooks which during the wintertime double as a space heater.",[636,105166,105168],{"id":105167},"developer-productivity","Developer Productivity",[22,105170,105171],{},"I think the biggest thing I have noticed from a software developer standpoint is the speed at which everything moves. I find myself waiting around less for things to happen. It's particularly more responsive when I fire up my IDE IntelliJ and the project has to get indexed or when I have to build something. I tweeted this out recently but I pulled down the Spring Pet Clinic project and I was able to run it in 1.95 seconds.",[22,105173,105174],{},[677,105175,105176],{"href":105176,"rel":105177},"https://twitter.com/therealdanvega/status/1458979393557123089",[681],[22,105179,105180],{},"The other thing I have found is that I am less worried about running resource-intensive applications like Chrome while I am coding. In the past, I would try and limit the number of tabs or extensions running but it's not something I am paying attention to now.",[22,105182,105183],{},"I think the build times are going to be the biggest time savers when it comes to developer productivity. I came across this tweet and thought it was right on the money. Laptops are cheaper than developers!",[22,105185,105186],{},[677,105187,105188],{"href":105188,"rel":105189},"https://twitter.com/softwarejameson/status/1455971162060697613",[681],[30796,105191,105194,105195],{"className":105192},[105046,105193],"tip-jar","\n Support this website ",[677,105196,105199],{"href":105197,"target":105198},"https://danvega.ck.page/products/content-creator-tip-jar","_blank","Buy Me Coffee",[636,105201,105203],{"id":105202},"content-creator-productivity","Content Creator Productivity",[22,105205,105206],{},"When it comes to creating content this machine is a dream come true. The biggest improvement for me is when I am both recording and editing videos. In my previous setups whenever I would record my screen or try and live stream the fans would go into overdrive and the machine would get sluggish making it hard to multitask.",[22,105208,105209],{},[653,105210],{"alt":105211,"src":105211},"https://images.unsplash.com/photo-1607968565043-36af90dde238?ixlib=rb-1.2.1&q=85&fm=jpg&crop=entropy&cs=srgb",[22,105213,105214],{},"With this machine, I am able to run my recording software (Ecamm Live) and the fans are not in my way and everything still feels snappy.",[22,105216,105217],{},"Editing videos is where I feel like I am seeing the biggest performance gains and getting back time in my day. In previous setups when I would drag footage onto my timeline It would take up to 10 seconds to render and scrubbing through footage would be choppy. Now, everything is just instant and snappy.",[22,105219,105220],{},"Finally, when it comes to exporting I have found videos that were taking me 15 min to export now take me 4-5 minutes. During the course of a day or a week, these things really start to add up!",[26,105222,105224],{"id":105223},"developer-setup","Developer Setup",[22,105226,105227],{},"I originally wanted to create videos around each step of my setup process and quickly realized that would take way too long. I wanted to get it set up and start playing with it as soon as I could. What I have been doing over the past few years is documenting my step-by-step setup. I like doing this because it might help others and you might come across a new tool or application that can help you out. You can find the link below and as always, feedback is welcome!",[22,105229,105230],{},[677,105231,105234],{"href":105232,"rel":105233},"https://github.com/danvega/new-macbook-setup/tree/master/2021",[681],"new-macbook-setup/2021 at master · danvega/new-macbook-setup",[26,105236,105238],{"id":105237},"macbook-2021-price","MacBook 2021 Price",[22,105240,105241],{},[653,105242],{"alt":105243,"src":105243},"https://images.unsplash.com/photo-1459257831348-f0cdd359235f?ixlib=rb-1.2.1&q=85&fm=jpg&crop=entropy&cs=srgb",[22,105245,105246],{},"If there is a con it's the price of these new machines, they aren't cheap. I feel pretty fortunate to even be in a position to buy one because for most of my tech life I have never been able to buy the latest tech. The only reason I am in this position is that I have a business where and it's almost the end of the year which means I am looking for tax write-offs.",[22,105248,105249],{},"I haven't actually used the MacBook Air or the MacBook M1 from 2020 but from everything I have seen and heard they are amazing machines. In fact, if you aren't creating content I might recommend going that route if the price is an issue.",[22,105251,105252],{},"That said if you are live streaming, editing video, and producing content I think it's worth every penny. I also think there is a way that you can talk your boss into buying you one if you're building larger software projects.",[26,105254,1499],{"id":1498},[22,105256,105257],{},"I'm usually a pretty patient person but I don't like waiting for my computer to perform builds or exports videos. While we haven't reached the ultimate goal of instant gratification It is incredible what this machine can do. I think the biggest thing for me is that this is only the 2nd iteration of this chip. I am excited about the future of Apple Silicon and can't wait to see what they do next!",{"title":57,"searchDepth":76,"depth":76,"links":105259},[105260,105263,105267,105268,105269],{"id":105009,"depth":76,"text":105010,"children":105261},[105262],{"id":105049,"depth":82,"text":105050},{"id":105157,"depth":76,"text":105158,"children":105264},[105265,105266],{"id":105167,"depth":82,"text":105168},{"id":105202,"depth":82,"text":105203},{"id":105223,"depth":76,"text":105224},{"id":105237,"depth":76,"text":105238},{"id":1498,"depth":76,"text":1499},{"_id":105271,"path":105272,"title":105273,"description":105274,"meta":105275,"body":105280},"content/blog/2021/11/06/github-copilot-java-developers.md","/blog/2021/11/06/github-copilot-java-developers","GitHub Copilot for Java Develpers","Earlier this year GitHub launched Copilot, an AI pair-programmer. With GitHub Copilot, get suggestions for whole lines or entire functions right inside your editor.",{"slug":105276,"date":105277,"published":13,"tags":105278,"author":17,"cover":105279,"excerpt":-1},"github-copilot-java-developers","2021-11-08T10:00:00.000Z",[56,11002],"./github_copilot_thumbnail.png",{"type":19,"value":105281,"toc":105321},[105282,105285,105288,105301,105304,105310,105312,105315,105318],[22,105283,105284],{},"Earlier this year GitHub launched Copilot, an AI pair-programmer. With GitHub Copilot, get suggestions for whole lines or entire functions right inside your editor. I'm not sure if it supported Java when it was first launched but at the time of writing this, the technical preview supports Python, JavaScript, TypeScript, Ruby, Java & Go.",[22,105286,105287],{},"When it was released back in June of 2021 they only had support for Visual Studio Code. At Github Universe 2021 they announced support for NeoVim and Jetbrains. This means that if you're running any flavor of the JetBrains IDE like IntelliJ, GoLand, or Webstorm you can use Github Copilot.",[22,105289,105290,105291,105296,105297,105300],{},"In this tutorial, I am focused on Github Copilot for Java Developers. I am using IntelliJ Ultimate Edition but this should work fine in the community edition. The first thing you need to do is to visit ",[677,105292,105295],{"href":105293,"rel":105294},"https://copilot.github.com/",[681],"https://copilot.github.com"," and sign up for the technical preview. Next, open up IntelliJ and go to ",[59,105298,105299],{},"Preferences > Plugins",", search for Github Copilot and install the plugin.",[22,105302,105303],{},"At this point, you're ready to use GitHub Copilot. In the remainder of the video tutorial, I walk through different ways GitHub Copilot was able to make suggestions for lines of code and even entire methods. If you want to grab the final code you can head over to the repository below.",[22,105305,105306],{},[677,105307,105308],{"href":105308,"rel":105309},"https://github.com/danvega/youtube/tree/main/java/github-copilot",[681],[26,105311,1499],{"id":1498},[22,105313,105314],{},"As I said throughout the video I am not sure how this is going to work into my day-to-day workflow. Since the video was made I have been using it a little bit more and I can say that it's not replacing me, it's actually assisting me in writing code faster.",[26,105316,105317],{"id":1338},"Update",[22,105319,105320],{},"I have been using Github Copilot with IntelliJ Ultimate Edition for a while now and I'm very happy with the results. It really does a great job of predicting what you need to write next and I use it for Java, Spring, JavaScript and even Markdown. I find myself turning it off for presentations now because it's the only thing viewers want to talk about and it takes away from the demo.",{"title":57,"searchDepth":76,"depth":76,"links":105322},[105323,105324],{"id":1498,"depth":76,"text":1499},{"id":1338,"depth":76,"text":105317},{"_id":105326,"path":105327,"title":105328,"description":105329,"meta":105330,"body":105337},"content/blog/2021/08/30/spring-one-2021.md","/blog/2021/08/30/spring-one-2021","Spring One 2021 Presentation","I'm so honored to be speaking at this year's SpringOne Conference. This article will give you a look ahead to the presentation by explaining what you can expect and the resources from my talk.",{"slug":105331,"date":105332,"published":13,"tags":105333,"author":17,"cover":105336,"excerpt":-1},"spring-one-2021","2021-08-30T19:45:43.204Z",[105334,11002,105335],"conference","vue","./spring-one-2021-cover.png",{"type":19,"value":105338,"toc":105414},[105339,105347,105351,105354,105359,105364,105369,105373,105376,105387,105390,105396,105398,105401,105407,105411],[22,105340,105341,105342,105346],{},"This year ",[677,105343,104592],{"href":105344,"rel":105345},"https://springone.io/",[681]," takes place September 1-2, 2021, and is free to virtually attend. As much as I want to get back to speaking in person I am just so thankful to have been selected to speak at this conference. I am looking forward to sharing the virtual stage with so many of the best speakers in our community.",[26,105348,105350],{"id":105349},"full-stack-java-development-with-spring-boot-and-vue","Full Stack Java Development with Spring Boot and Vue",[22,105352,105353],{},"I am going to be presenting on two of my favorite frameworks, Spring Boot and VueJS. I have a bit of a confession to make, I am a lazy programmer and both of these frameworks are enablers. They both make all of the hard stuff easy and let me focus on the business requirements of the project. Here is the presentation abstract to give you a bit more context on the presentation.",[29685,105355,105356],{},[22,105357,105358],{},"Are you interested in building full-stack web applications with Spring Boot and Vue 3? If so, this session is going to give you everything you need to get started. You will learn some of the different approaches to building full-stack applications and the pros and cons to each.",[29685,105360,105361],{},[22,105362,105363],{},"First, we will look at how Vue is a progressive framework and will allow you to incrementally adopt it in your Java applications as your needs grow. Next, we will look at how to build a monolith application where you end up with a single deployable JAR. Finally, if your frontend and backend teams are separate, we will look at a solution for you.",[29685,105365,105366],{},[22,105367,105368],{},"When you leave this session, you’ll have everything you need to start building full-stack web applications with Spring Boot and VueJS.",[636,105370,105372],{"id":105371},"what-will-you-learn","What will you learn?",[22,105374,105375],{},"As I said in the abstract I want to focus on three different approaches to using Spring Boot and VueJS together. What approach you choose will depend on the project's requirements and the makeup of your team. You will learn what to use each of these as well as some pros/cons to each.",[34,105377,105378,105381,105384],{},[37,105379,105380],{},"Progressive Vue - Incrementally adopting Vue to enhance an existing page",[37,105382,105383],{},"Monolith - The Spring Boot project contains the Vue frontend and you will package both of them into a single deployable JAR.",[37,105385,105386],{},"Single Page Application (SPA) - The frontend and backend applications are built and deployed independently.",[22,105388,105389],{},"To illustrate these approaches we will be building out functionality for an Ice Cream store. If you don't learn anything you either weren't paying attention or completely consumed with the idea of getting ice cream.",[22,105391,105392],{},[653,105393],{"alt":105394,"src":105395},"Ice Cream Store","/images/blog/2021/08/30/ice-cream-store.png",[636,105397,4098],{"id":4097},[22,105399,105400],{},"If you would like to get access to the source code for any of the demos I walked through or the slide deck for the presentation please visit the following Github Repository.",[22,105402,105403],{},[677,105404,105405],{"href":105405,"rel":105406},"https://github.com/danvega/spring-one-2021",[681],[636,105408,105410],{"id":105409},"feedback","Feedback",[22,105412,105413],{},"I love putting presentations together but without your feedback, they can sometimes leave me feeling incomplete. If you have any feedback (positive or negative) please reach out to me and let me know. I hope to give this talk again so I would love to know what I can do to improve it for next time.",{"title":57,"searchDepth":76,"depth":76,"links":105415},[105416],{"id":105349,"depth":76,"text":105350,"children":105417},[105418,105419,105420],{"id":105371,"depth":82,"text":105372},{"id":4097,"depth":82,"text":4098},{"id":105409,"depth":82,"text":105410},{"_id":105422,"path":105423,"title":105424,"description":105425,"meta":105426,"body":105431},"content/blog/2021/01/22/full-stack-java.md","/blog/2021/01/22/full-stack-java","Full Stack Java development with Spring Boot and VueJS","In this tutorial, you are going to learn how to build a full-stack application that uses Vue for the frontend and Spring Boot for the backend.",{"slug":105427,"date":105428,"published":13,"tags":105429,"author":17,"cover":105430,"excerpt":-1},"full-stack-java","2021-01-22T08:00:00.000Z",[56,105335],"./full-stack-java-spring-vue.png",{"type":19,"value":105432,"toc":106887},[105433,105440,105442,105468,105471,105474,105479,105482,105485,105488,105494,105497,105500,105503,105506,105510,105513,105518,105521,105524,105538,105540,105543,105549,105555,105561,105564,105625,105628,105693,105697,105700,105783,105790,105796,105798,105819,105824,105830,105843,105846,105850,105861,105959,105970,105974,105979,106108,106112,106123,106305,106312,106318,106321,106324,106330,106339,106369,106390,106600,106616,106721,106724,106736,106742,106837,106843,106849,106852,106875,106877,106880,106884],[22,105434,105435,105436,2755],{},"In this tutorial, you will learn how to build a full-stack application that uses Vue for the frontend and Spring Boot for the backend. As always you can find the code associated with this over on ",[677,105437,50830],{"href":105438,"rel":105439},"https://github.com/danvega/full-stack-java-vue",[681],[26,105441,48219],{"id":48218},[915,105443,105444,105447,105450,105466],{},[37,105445,105446],{},"Design decisions",[37,105448,105449],{},"Monolithic architecture",[37,105451,105452,105453],{},"Creating the project\n",[915,105454,105455,105458,105460,105463],{},[37,105456,105457],{},"Requirements",[37,105459,2925],{},[37,105461,105462],{},"Vue",[37,105464,105465],{},"Build plugins",[37,105467,1499],{},[26,105469,105446],{"id":105470},"design-decisions",[22,105472,105473],{},"Creating a brand new application from scratch is exciting, isn't it? The possibilities are endless but the freedom to create whatever you want comes with some future implications. I know your first instinct is to jump right into the deep end and start writing code but before you do, you should have a clear idea of what you're trying to build.",[29685,105475,105476],{},[22,105477,105478],{},"\"IF YOU FAIL TO PLAN, YOU ARE PLANNING TO FAIL\"",[22,105480,105481],{},"Without fully understanding the problems you are trying to solve for I can't recommend the absolute best solution but I will give you a general approach. One of the first questions I try to answer is \"What is the structure of the development team on this project?\". This shouldn't be the driving force for all of your decisions but it is an important one.",[22,105483,105484],{},"If you have full-stack developers that will be working on both sides of the application a monolithic approach could make sense. If you have separate teams working on the frontend and backend splitting these projects into two independent projects might make sense. There are pros and cons to each and these should really be evaluated on a per-project basis.",[26,105486,105449],{"id":105487},"monolithic-architecture",[22,105489,105490],{},[653,105491],{"alt":105492,"src":105493},"Monolith","/images/blog/2021/01/22/trent-erwin-zvKMpmvLlSo-unsplash.jpg",[22,105495,105496],{},"In this article we are going to focus on building a Monolithic application that uses Vue on the fronted and Spring Boot on the backend. A lot of developers I come across are familiar with both of these technologies but aren't quite sure how to configure them in a single project.",[22,105498,105499],{},"Having a single deployable asset simplifies the deployment process which I know a lot of developers don't like to deal with. If you're a single developer or a team of full-stack developers this is a great option. There are some tricks to getting this all into a single project but that is exactly what you will learn in this tutorial.",[22,105501,105502],{},"The biggest advantage to this approach is that you end up with a single artifact to deploy. If you don't have a team dedicated to the DevOps side of development this will help cut down some of the complexities of moving to production.",[22,105504,105505],{},"This also means that if you want to change something small on the frontend you will to redeploy the entire application. If you have a large team there could be a lot of paths crossing which could end up causing lots of merge conflicts. I don't know about you but the only thing worse than real life conflicts is dealing with merge conflicts 😳",[26,105507,105509],{"id":105508},"creating-the-project","Creating the project",[22,105511,105512],{},"You will start by creating a Spring Boot project that will act as the foundation for your project. You can then generate a new Vue project in the Spring Boot project and with some configuration you will be able to put all of the puzzle pieces together.",[29685,105514,105515],{},[22,105516,105517],{},"This could be done using any front end technology (Angular,React,Vue,Svelte,etc..)",[636,105519,105457],{"id":105520},"requirements",[22,105522,105523],{},"I am going to make some assumptions about you before we get started. You should be familiar and have some experience with the following:",[915,105525,105526,105528,105530,105532,105535],{},[37,105527,15],{},[37,105529,94504],{},[37,105531,2925],{},[37,105533,105534],{},"JavaScript",[37,105536,105537],{},"VueJS",[636,105539,2925],{"id":57650},[22,105541,105542],{},"My IDE of choice is IntelliJ Ultimate Edition. If you're using the same you can create the project right in the IDE:",[22,105544,105545],{},[653,105546],{"alt":105547,"src":105548},"Spring Initializr IntelliJ","/images/blog/2021/01/22/intelli_new_spring_project.png",[22,105550,105551,105552],{},"If you're not you can do the same from ",[677,105553,1744],{"href":1744,"rel":105554},[681],[22,105556,105557],{},[653,105558],{"alt":105559,"src":105560},"Spring Initializr Web","/images/blog/2021/01/22/start_spring_io.png",[22,105562,105563],{},"Start by creating a new Spring Boot project with the following properties:",[915,105565,105566,105572,105578,105584,105590,105596,105602,105608,105613],{},[37,105567,105568,105571],{},[646,105569,105570],{},"Group:"," dev.danvega",[37,105573,105574,105577],{},[646,105575,105576],{},"Artifact:"," fsjava",[37,105579,105580,105583],{},[646,105581,105582],{},"Type:"," Maven",[37,105585,105586,105589],{},[646,105587,105588],{},"Spring Boot:"," 2.4.2 (or latest)",[37,105591,105592,105595],{},[646,105593,105594],{},"Language:"," Java",[37,105597,105598,105601],{},[646,105599,105600],{},"Packaging:"," Jar",[37,105603,105604,105607],{},[646,105605,105606],{},"Java Version:"," 11",[37,105609,105610,105601],{},[646,105611,105612],{},"Package:",[37,105614,105615,105618],{},[646,105616,105617],{},"Dependencies:",[915,105619,105620,105623],{},[37,105621,105622],{},"Spring Boot DevTools",[37,105624,1756],{},[22,105626,105627],{},"The first thing I like to do with any new project is to make sure it runs as expected. Open up the main application class (I renamed mine to Application.java) and run it to make sure",[52,105629,105631],{"className":54,"code":105630,"language":56,"meta":57,"style":57},"@SpringBootApplication\npublic class Application {\n\n public static void main(String[] args) {\n SpringApplication.run(Application.class, args);\n }\n\n}\n",[59,105632,105633,105639,105649,105653,105673,105681,105685,105689],{"__ignoreMap":57},[62,105634,105635,105637],{"class":64,"line":65},[62,105636,942],{"class":72},[62,105638,2079],{"class":68},[62,105640,105641,105643,105645,105647],{"class":64,"line":76},[62,105642,116],{"class":68},[62,105644,119],{"class":68},[62,105646,2088],{"class":122},[62,105648,126],{"class":72},[62,105650,105651],{"class":64,"line":82},[62,105652,79],{"emptyLinePlaceholder":13},[62,105654,105655,105657,105659,105661,105663,105665,105667,105669,105671],{"class":64,"line":89},[62,105656,194],{"class":68},[62,105658,2101],{"class":68},[62,105660,200],{"class":68},[62,105662,2106],{"class":122},[62,105664,2109],{"class":72},[62,105666,973],{"class":68},[62,105668,2114],{"class":72},[62,105670,2117],{"class":889},[62,105672,768],{"class":72},[62,105674,105675,105677,105679],{"class":64,"line":95},[62,105676,2124],{"class":72},[62,105678,2127],{"class":122},[62,105680,2130],{"class":72},[62,105682,105683],{"class":64,"line":101},[62,105684,223],{"class":72},[62,105686,105687],{"class":64,"line":107},[62,105688,79],{"emptyLinePlaceholder":13},[62,105690,105691],{"class":64,"line":113},[62,105692,379],{"class":72},[28831,105694,105696],{"id":105695},"message-controller","Message Controller",[22,105698,105699],{},"The first class you are going to create is a simple controller that contains a single mapping. This will be a public REST endpoint that you can call from your client application.",[52,105701,105703],{"className":54,"code":105702,"language":56,"meta":57,"style":57},"@RestController\n@RequestMapping(\"/api/messages\")\npublic class MessageController {\n\n @GetMapping(\"/hello\")\n public String hello() {\n return \"Full Stack Java with Spring Boot & VueJS!\";\n }\n\n}\n",[59,105704,105705,105711,105724,105735,105739,105752,105762,105771,105775,105779],{"__ignoreMap":57},[62,105706,105707,105709],{"class":64,"line":65},[62,105708,942],{"class":72},[62,105710,2342],{"class":68},[62,105712,105713,105715,105717,105719,105722],{"class":64,"line":76},[62,105714,942],{"class":72},[62,105716,10592],{"class":68},[62,105718,2109],{"class":72},[62,105720,105721],{"class":1675},"\"/api/messages\"",[62,105723,2212],{"class":72},[62,105725,105726,105728,105730,105733],{"class":64,"line":82},[62,105727,116],{"class":68},[62,105729,119],{"class":68},[62,105731,105732],{"class":122}," MessageController",[62,105734,126],{"class":72},[62,105736,105737],{"class":64,"line":89},[62,105738,79],{"emptyLinePlaceholder":13},[62,105740,105741,105743,105745,105747,105750],{"class":64,"line":95},[62,105742,2143],{"class":72},[62,105744,2548],{"class":68},[62,105746,2109],{"class":72},[62,105748,105749],{"class":1675},"\"/hello\"",[62,105751,2212],{"class":72},[62,105753,105754,105756,105758,105760],{"class":64,"line":101},[62,105755,194],{"class":68},[62,105757,2469],{"class":72},[62,105759,82397],{"class":122},[62,105761,206],{"class":72},[62,105763,105764,105766,105769],{"class":64,"line":107},[62,105765,360],{"class":68},[62,105767,105768],{"class":1675}," \"Full Stack Java with Spring Boot & VueJS!\"",[62,105770,153],{"class":72},[62,105772,105773],{"class":64,"line":113},[62,105774,223],{"class":72},[62,105776,105777],{"class":64,"line":129},[62,105778,79],{"emptyLinePlaceholder":13},[62,105780,105781],{"class":64,"line":134},[62,105782,379],{"class":72},[22,105784,105785,105786],{},"With DevTools your application should have restarted after this change, if it hasn't please re-run the application. Open up a browser and navigate to ",[677,105787,105788],{"href":105788,"rel":105789},"http://localhost:8080/api/messages/hello",[681],[22,105791,105792],{},[653,105793],{"alt":105794,"src":105795},"Hello Endpoint","/images/blog/2021/01/22/hello_endpoint.png",[636,105797,105462],{"id":105335},[22,105799,105800,105801,105806,105807,105810,105811,105814,105815,105818],{},"Next, you will create the frontend application using the ",[677,105802,105805],{"href":105803,"rel":105804},"https://cli.vuejs.org/",[681],"Vue CLI",". If you're in IntelliJ you can open up the terminal or open up what ever shell you use. If you have the Vue CLI installed you can navigate to the ",[59,105808,105809],{},"/src"," folder and run the command ",[59,105812,105813],{},"vue create frontend"," which will generate a project into ",[59,105816,105817],{},"/src/frontend",". For this project I am generating a Vue 3 application but this will also work with v2.",[22,105820,105821],{},[653,105822],{"alt":105805,"src":105823},"/images/blog/2021/01/22/vue_cli.png",[22,105825,105826,105827,105829],{},"Once the application has been created navigate to ",[59,105828,105817],{}," and run the application.",[52,105831,105833],{"className":1663,"code":105832,"language":1665,"meta":57,"style":57},"npm run serve\n",[59,105834,105835],{"__ignoreMap":57},[62,105836,105837,105839,105841],{"class":64,"line":65},[62,105838,32645],{"class":122},[62,105840,1716],{"class":1675},[62,105842,49787],{"class":1675},[22,105844,105845],{},"If your Spring Boot application isn't running the client app will start on port 8080, if it is it will find the next available port. At this point you should be able to run both of the applications independently.",[636,105847,105849],{"id":105848},"vue-configuration","Vue Configuration",[22,105851,105852,105853,105856,105857,105860],{},"Next let's add some configuration so to our Vue application that will solve a couple of problems. Create a new file in the root of the ",[59,105854,105855],{},"/frontend"," folder named ",[59,105858,105859],{},"vue.config.js"," that contains the following:",[52,105862,105866],{"className":105863,"code":105864,"language":105865,"meta":57,"style":57},"language-js shiki shiki-themes github-light github-dark github-light","// vue.config.js\nmodule.exports = {\n // https://cli.vuejs.org/config/#devserver-proxy\n devServer: {\n port: 3000,\n proxy: {\n '/api': {\n target: 'http://localhost:8080',\n ws: true,\n changeOrigin: true\n }\n }\n }\n}\n","js",[59,105867,105868,105873,105885,105890,105895,105905,105910,105917,105927,105936,105943,105947,105951,105955],{"__ignoreMap":57},[62,105869,105870],{"class":64,"line":65},[62,105871,105872],{"class":85},"// vue.config.js\n",[62,105874,105875,105877,105879,105881,105883],{"class":64,"line":76},[62,105876,32813],{"class":149},[62,105878,2755],{"class":72},[62,105880,32818],{"class":149},[62,105882,2556],{"class":68},[62,105884,126],{"class":72},[62,105886,105887],{"class":64,"line":82},[62,105888,105889],{"class":85}," // https://cli.vuejs.org/config/#devserver-proxy\n",[62,105891,105892],{"class":64,"line":89},[62,105893,105894],{"class":72}," devServer: {\n",[62,105896,105897,105900,105903],{"class":64,"line":95},[62,105898,105899],{"class":72}," port: ",[62,105901,105902],{"class":149},"3000",[62,105904,3338],{"class":72},[62,105906,105907],{"class":64,"line":101},[62,105908,105909],{"class":72}," proxy: {\n",[62,105911,105912,105915],{"class":64,"line":107},[62,105913,105914],{"class":1675}," '/api'",[62,105916,3688],{"class":72},[62,105918,105919,105922,105925],{"class":64,"line":113},[62,105920,105921],{"class":72}," target: ",[62,105923,105924],{"class":1675},"'http://localhost:8080'",[62,105926,3338],{"class":72},[62,105928,105929,105932,105934],{"class":64,"line":129},[62,105930,105931],{"class":72}," ws: ",[62,105933,21775],{"class":149},[62,105935,3338],{"class":72},[62,105937,105938,105941],{"class":64,"line":134},[62,105939,105940],{"class":72}," changeOrigin: ",[62,105942,51914],{"class":149},[62,105944,105945],{"class":64,"line":156},[62,105946,861],{"class":72},[62,105948,105949],{"class":64,"line":161},[62,105950,533],{"class":72},[62,105952,105953],{"class":64,"line":167},[62,105954,223],{"class":72},[62,105956,105957],{"class":64,"line":173},[62,105958,379],{"class":72},[22,105960,105961,105962,105965,105966,105969],{},"This will set the port to 3000 so we don't collide with the Spring Boot application. Next you're setting a proxy so that any request that starts with ",[59,105963,105964],{},"/api"," will be forwarded to ",[677,105967,16942],{"href":16942,"rel":105968},[681],". This means that you don't have to worry about setting up different environment variables for development and production.",[28831,105971,105973],{"id":105972},"appvue","App.vue",[22,105975,105976,105978],{},[59,105977,105973],{}," is the main component that is loaded in your Vue application. You can start by cleaning this up and removing some of the default styles.",[52,105980,105983],{"className":105981,"code":105982,"language":105335,"meta":57,"style":57},"language-vue shiki shiki-themes github-light github-dark github-light","\u003Ctemplate>\n \u003CHelloWorld />\n\u003C/template>\n\n\u003Cscript>\nimport HelloWorld from './components/HelloWorld.vue'\n\nexport default {\n name: 'App',\n components: {\n HelloWorld\n }\n}\n\u003C/script>\n\n\u003Cstyle>\n\n\u003C/style>\n",[59,105984,105985,105994,106003,106011,106015,106023,106035,106039,106048,106058,106063,106068,106072,106076,106084,106088,106096,106100],{"__ignoreMap":57},[62,105986,105987,105989,105992],{"class":64,"line":65},[62,105988,760],{"class":72},[62,105990,105991],{"class":1780},"template",[62,105993,1784],{"class":72},[62,105995,105996,105998,106001],{"class":64,"line":76},[62,105997,33056],{"class":72},[62,105999,106000],{"class":1780},"HelloWorld",[62,106002,67133],{"class":72},[62,106004,106005,106007,106009],{"class":64,"line":82},[62,106006,1818],{"class":72},[62,106008,105991],{"class":1780},[62,106010,1784],{"class":72},[62,106012,106013],{"class":64,"line":89},[62,106014,79],{"emptyLinePlaceholder":13},[62,106016,106017,106019,106021],{"class":64,"line":95},[62,106018,760],{"class":72},[62,106020,15846],{"class":1780},[62,106022,1784],{"class":72},[62,106024,106025,106027,106030,106032],{"class":64,"line":101},[62,106026,27875],{"class":68},[62,106028,106029],{"class":72}," HelloWorld ",[62,106031,3507],{"class":68},[62,106033,106034],{"class":1675}," './components/HelloWorld.vue'\n",[62,106036,106037],{"class":64,"line":107},[62,106038,79],{"emptyLinePlaceholder":13},[62,106040,106041,106043,106046],{"class":64,"line":113},[62,106042,14767],{"class":68},[62,106044,106045],{"class":68}," default",[62,106047,126],{"class":72},[62,106049,106050,106053,106056],{"class":64,"line":129},[62,106051,106052],{"class":72}," name: ",[62,106054,106055],{"class":1675},"'App'",[62,106057,3338],{"class":72},[62,106059,106060],{"class":64,"line":134},[62,106061,106062],{"class":72}," components: {\n",[62,106064,106065],{"class":64,"line":156},[62,106066,106067],{"class":72}," HelloWorld\n",[62,106069,106070],{"class":64,"line":161},[62,106071,3731],{"class":72},[62,106073,106074],{"class":64,"line":167},[62,106075,379],{"class":72},[62,106077,106078,106080,106082],{"class":64,"line":173},[62,106079,1818],{"class":72},[62,106081,15846],{"class":1780},[62,106083,1784],{"class":72},[62,106085,106086],{"class":64,"line":179},[62,106087,79],{"emptyLinePlaceholder":13},[62,106089,106090,106092,106094],{"class":64,"line":185},[62,106091,760],{"class":72},[62,106093,1527],{"class":1780},[62,106095,1784],{"class":72},[62,106097,106098],{"class":64,"line":191},[62,106099,79],{"emptyLinePlaceholder":13},[62,106101,106102,106104,106106],{"class":64,"line":209},[62,106103,1818],{"class":72},[62,106105,1527],{"class":1780},[62,106107,1784],{"class":72},[636,106109,106111],{"id":106110},"helloworld-component","HelloWorld Component",[22,106113,106114,106115,106118,106119,106122],{},"Last step on the frontend is to remove the boiler-plate code from the ",[59,106116,106117],{},"HelloWorld.vue"," component. In this component you can make a fetch request to your backend API to retrieve the message that is being exposed on your hello endpoint. I am doing this in the ",[59,106120,106121],{},"mounted()"," lifecycle hook and I am using the Fetch API.",[52,106124,106126],{"className":15773,"code":106125,"language":15775,"meta":57,"style":57},"\u003Ctemplate>\n \u003Ch1>{{ msg }}\u003C/h1>\n\u003C/template>\n\n\u003Cscript>\nexport default {\n name: 'HelloWorld',\n data() {\n return {\n msg: ''\n }\n },\n mounted() {\n fetch(\"/api/messages/hello\")\n .then((response) => response.text())\n .then((data) => {\n this.msg = data;\n });\n }\n}\n\u003C/script>\n",[59,106127,106128,106136,106149,106157,106161,106169,106177,106186,106193,106199,106207,106211,106215,106222,106234,106254,106271,106284,106289,106293,106297],{"__ignoreMap":57},[62,106129,106130,106132,106134],{"class":64,"line":65},[62,106131,760],{"class":72},[62,106133,105991],{"class":1780},[62,106135,1784],{"class":72},[62,106137,106138,106140,106142,106145,106147],{"class":64,"line":76},[62,106139,33056],{"class":72},[62,106141,4168],{"class":1780},[62,106143,106144],{"class":72},">{{ msg }}\u003C/",[62,106146,4168],{"class":1780},[62,106148,1784],{"class":72},[62,106150,106151,106153,106155],{"class":64,"line":82},[62,106152,1818],{"class":72},[62,106154,105991],{"class":1780},[62,106156,1784],{"class":72},[62,106158,106159],{"class":64,"line":89},[62,106160,79],{"emptyLinePlaceholder":13},[62,106162,106163,106165,106167],{"class":64,"line":95},[62,106164,760],{"class":72},[62,106166,15846],{"class":1780},[62,106168,1784],{"class":72},[62,106170,106171,106173,106175],{"class":64,"line":101},[62,106172,14767],{"class":68},[62,106174,106045],{"class":68},[62,106176,126],{"class":72},[62,106178,106179,106181,106184],{"class":64,"line":107},[62,106180,106052],{"class":72},[62,106182,106183],{"class":1675},"'HelloWorld'",[62,106185,3338],{"class":72},[62,106187,106188,106191],{"class":64,"line":113},[62,106189,106190],{"class":122}," data",[62,106192,206],{"class":72},[62,106194,106195,106197],{"class":64,"line":129},[62,106196,2599],{"class":68},[62,106198,126],{"class":72},[62,106200,106201,106204],{"class":64,"line":134},[62,106202,106203],{"class":72}," msg: ",[62,106205,106206],{"class":1675},"''\n",[62,106208,106209],{"class":64,"line":156},[62,106210,223],{"class":72},[62,106212,106213],{"class":64,"line":161},[62,106214,32848],{"class":72},[62,106216,106217,106220],{"class":64,"line":167},[62,106218,106219],{"class":122}," mounted",[62,106221,206],{"class":72},[62,106223,106224,106227,106229,106232],{"class":64,"line":173},[62,106225,106226],{"class":122}," fetch",[62,106228,2109],{"class":72},[62,106230,106231],{"class":1675},"\"/api/messages/hello\"",[62,106233,2212],{"class":72},[62,106235,106236,106238,106240,106242,106244,106246,106248,106250,106252],{"class":64,"line":179},[62,106237,42872],{"class":72},[62,106239,36912],{"class":122},[62,106241,85590],{"class":72},[62,106243,13389],{"class":889},[62,106245,5024],{"class":72},[62,106247,21525],{"class":68},[62,106249,12760],{"class":72},[62,106251,1727],{"class":122},[62,106253,4460],{"class":72},[62,106255,106256,106258,106260,106262,106265,106267,106269],{"class":64,"line":185},[62,106257,42872],{"class":72},[62,106259,36912],{"class":122},[62,106261,85590],{"class":72},[62,106263,106264],{"class":889},"data",[62,106266,5024],{"class":72},[62,106268,21525],{"class":68},[62,106270,126],{"class":72},[62,106272,106273,106276,106279,106281],{"class":64,"line":191},[62,106274,106275],{"class":149}," this",[62,106277,106278],{"class":72},".msg ",[62,106280,146],{"class":68},[62,106282,106283],{"class":72}," data;\n",[62,106285,106286],{"class":64,"line":209},[62,106287,106288],{"class":72}," });\n",[62,106290,106291],{"class":64,"line":220},[62,106292,3731],{"class":72},[62,106294,106295],{"class":64,"line":226},[62,106296,379],{"class":72},[62,106298,106299,106301,106303],{"class":64,"line":231},[62,106300,1818],{"class":72},[62,106302,15846],{"class":1780},[62,106304,1784],{"class":72},[22,106306,106307,106308,106311],{},"If you run the Vue application using ",[59,106309,106310],{},"npm run serve"," you should see the message from your backend API being displayed.",[22,106313,106314],{},[653,106315],{"alt":106316,"src":106317},"Vue Hello World","/images/blog/2021/01/22/vue_hello_world.png",[22,106319,106320],{},"My development workflow consists of running these as separate applications. I usually start up a new shell in the background for running the Vue application. This gives me one less distraction in IntelliJ and I appreciate the larger iTerm window to view anything going wrong with the client.",[636,106322,105465],{"id":106323},"build-plugins",[22,106325,106326,106327,24909],{},"When it comes to production the goal of our Monolithic application is to have a single deployable artifact. To accomplish this you will need to build a production version of our VueJS application and copy that to your ",[59,106328,106329],{},"/target/classes/static",[22,106331,106332,106333,106335,106336,106338],{},"With ",[59,106334,20654],{}," in the static folder this is the file that will be served when you hit the root. Finally as part of that build process Spring Boot will build a runnable JAR. Open up your ",[59,106337,1765],{}," and make sure you have the following properties set:",[52,106340,106342],{"className":1769,"code":106341,"language":1771,"meta":57,"style":57},"\u003Cproperties>\n \u003Cjava.version>11\u003C/java.version>\n \u003Cnode.version>v10.15.0\u003C/node.version>\n \u003Cnpm.version>6.14.3\u003C/npm.version>\n\u003C/properties>\n",[59,106343,106344,106349,106354,106359,106364],{"__ignoreMap":57},[62,106345,106346],{"class":64,"line":65},[62,106347,106348],{},"\u003Cproperties>\n",[62,106350,106351],{"class":64,"line":76},[62,106352,106353],{}," \u003Cjava.version>11\u003C/java.version>\n",[62,106355,106356],{"class":64,"line":82},[62,106357,106358],{}," \u003Cnode.version>v10.15.0\u003C/node.version>\n",[62,106360,106361],{"class":64,"line":89},[62,106362,106363],{}," \u003Cnpm.version>6.14.3\u003C/npm.version>\n",[62,106365,106366],{"class":64,"line":95},[62,106367,106368],{},"\u003C/properties>\n",[22,106370,106371,106372,106375,106376,106379,106380,19931,106383,106386,106387,24909],{},"Next, locate the ",[59,106373,106374],{},"\u003Cbuild>"," section and add the ",[677,106377,33321],{"href":33319,"rel":106378},[681],". This plugin downloads/installs Node and NPM locally for your project, and runs ",[59,106381,106382],{},"npm install",[59,106384,106385],{},"npm build"," to build your Vue application. The output for this build is stored in the ",[59,106388,106389],{},"/src/frontend/dist",[52,106391,106393],{"className":1769,"code":106392,"language":1771,"meta":57,"style":57},"\u003Cplugin>\n \u003CgroupId>com.github.eirslett\u003C/groupId>\n \u003CartifactId>frontend-maven-plugin\u003C/artifactId>\n \u003Cversion>1.7.6\u003C/version>\n\n \u003Cexecutions>\n \u003Cexecution>\n \u003Cid>Install node and npm\u003C/id>\n \u003Cgoals>\n \u003Cgoal>install-node-and-npm\u003C/goal>\n \u003C/goals>\n \u003Cphase>generate-resources\u003C/phase>\n \u003Cconfiguration>\n \u003CnodeVersion>${node.version}\u003C/nodeVersion>\n \u003CnpmVersion>${npm.version}\u003C/npmVersion>\n \u003C/configuration>\n \u003C/execution>\n\n \u003Cexecution>\n \u003Cid>npm install\u003C/id>\n \u003Cgoals>\n \u003Cgoal>npm\u003C/goal>\n \u003C/goals>\n \u003Cphase>generate-resources\u003C/phase>\n \u003Cconfiguration>\n \u003Carguments>install\u003C/arguments>\n \u003C/configuration>\n \u003C/execution>\n\n \u003Cexecution>\n \u003Cid>npm build\u003C/id>\n \u003Cgoals>\n \u003Cgoal>npm\u003C/goal>\n \u003C/goals>\n \u003Cphase>generate-resources\u003C/phase>\n \u003Cconfiguration>\n \u003Carguments>run build\u003C/arguments>\n \u003C/configuration>\n \u003C/execution>\n \u003C/executions>\n \u003Cconfiguration>\n \u003CnodeVersion>${node.version}\u003C/nodeVersion>\n \u003CworkingDirectory>src/frontend\u003C/workingDirectory>\n \u003C/configuration>\n\n\u003C/plugin>\n",[59,106394,106395,106399,106404,106409,106414,106418,106423,106427,106432,106437,106442,106447,106452,106457,106462,106467,106472,106476,106480,106484,106489,106493,106498,106502,106506,106510,106515,106519,106523,106527,106531,106536,106540,106544,106548,106552,106556,106561,106565,106569,106574,106578,106583,106588,106592,106596],{"__ignoreMap":57},[62,106396,106397],{"class":64,"line":65},[62,106398,82312],{},[62,106400,106401],{"class":64,"line":76},[62,106402,106403],{}," \u003CgroupId>com.github.eirslett\u003C/groupId>\n",[62,106405,106406],{"class":64,"line":82},[62,106407,106408],{}," \u003CartifactId>frontend-maven-plugin\u003C/artifactId>\n",[62,106410,106411],{"class":64,"line":89},[62,106412,106413],{}," \u003Cversion>1.7.6\u003C/version>\n",[62,106415,106416],{"class":64,"line":95},[62,106417,79],{"emptyLinePlaceholder":13},[62,106419,106420],{"class":64,"line":101},[62,106421,106422],{}," \u003Cexecutions>\n",[62,106424,106425],{"class":64,"line":107},[62,106426,78391],{},[62,106428,106429],{"class":64,"line":113},[62,106430,106431],{}," \u003Cid>Install node and npm\u003C/id>\n",[62,106433,106434],{"class":64,"line":129},[62,106435,106436],{}," \u003Cgoals>\n",[62,106438,106439],{"class":64,"line":134},[62,106440,106441],{}," \u003Cgoal>install-node-and-npm\u003C/goal>\n",[62,106443,106444],{"class":64,"line":156},[62,106445,106446],{}," \u003C/goals>\n",[62,106448,106449],{"class":64,"line":161},[62,106450,106451],{}," \u003Cphase>generate-resources\u003C/phase>\n",[62,106453,106454],{"class":64,"line":167},[62,106455,106456],{}," \u003Cconfiguration>\n",[62,106458,106459],{"class":64,"line":173},[62,106460,106461],{}," \u003CnodeVersion>${node.version}\u003C/nodeVersion>\n",[62,106463,106464],{"class":64,"line":179},[62,106465,106466],{}," \u003CnpmVersion>${npm.version}\u003C/npmVersion>\n",[62,106468,106469],{"class":64,"line":185},[62,106470,106471],{}," \u003C/configuration>\n",[62,106473,106474],{"class":64,"line":191},[62,106475,78416],{},[62,106477,106478],{"class":64,"line":209},[62,106479,79],{"emptyLinePlaceholder":13},[62,106481,106482],{"class":64,"line":220},[62,106483,78391],{},[62,106485,106486],{"class":64,"line":226},[62,106487,106488],{}," \u003Cid>npm install\u003C/id>\n",[62,106490,106491],{"class":64,"line":231},[62,106492,106436],{},[62,106494,106495],{"class":64,"line":236},[62,106496,106497],{}," \u003Cgoal>npm\u003C/goal>\n",[62,106499,106500],{"class":64,"line":242},[62,106501,106446],{},[62,106503,106504],{"class":64,"line":247},[62,106505,106451],{},[62,106507,106508],{"class":64,"line":252},[62,106509,106456],{},[62,106511,106512],{"class":64,"line":257},[62,106513,106514],{}," \u003Carguments>install\u003C/arguments>\n",[62,106516,106517],{"class":64,"line":271},[62,106518,106471],{},[62,106520,106521],{"class":64,"line":281},[62,106522,78416],{},[62,106524,106525],{"class":64,"line":286},[62,106526,79],{"emptyLinePlaceholder":13},[62,106528,106529],{"class":64,"line":291},[62,106530,78391],{},[62,106532,106533],{"class":64,"line":296},[62,106534,106535],{}," \u003Cid>npm build\u003C/id>\n",[62,106537,106538],{"class":64,"line":302},[62,106539,106436],{},[62,106541,106542],{"class":64,"line":308},[62,106543,106497],{},[62,106545,106546],{"class":64,"line":314},[62,106547,106446],{},[62,106549,106550],{"class":64,"line":320},[62,106551,106451],{},[62,106553,106554],{"class":64,"line":326},[62,106555,106456],{},[62,106557,106558],{"class":64,"line":338},[62,106559,106560],{}," \u003Carguments>run build\u003C/arguments>\n",[62,106562,106563],{"class":64,"line":343},[62,106564,106471],{},[62,106566,106567],{"class":64,"line":357},[62,106568,78416],{},[62,106570,106571],{"class":64,"line":366},[62,106572,106573],{}," \u003C/executions>\n",[62,106575,106576],{"class":64,"line":371},[62,106577,82327],{},[62,106579,106580],{"class":64,"line":376},[62,106581,106582],{}," \u003CnodeVersion>${node.version}\u003C/nodeVersion>\n",[62,106584,106585],{"class":64,"line":16333},[62,106586,106587],{}," \u003CworkingDirectory>src/frontend\u003C/workingDirectory>\n",[62,106589,106590],{"class":64,"line":16349},[62,106591,82347],{},[62,106593,106594],{"class":64,"line":16365},[62,106595,79],{"emptyLinePlaceholder":13},[62,106597,106598],{"class":64,"line":16381},[62,106599,82352],{},[22,106601,106602,106603,106608,106609,106612,106613,24909],{},"Next, add the ",[677,106604,106607],{"href":106605,"rel":106606},"https://maven.apache.org/plugins/maven-resources-plugin/",[681],"maven-resources-plugin"," which handles the copying of project resources to the output directory. You will need to copy everything from the ",[59,106610,106611],{},"src/frontend/dist"," directory to the ",[59,106614,106615],{},"target/classes/static",[52,106617,106619],{"className":1769,"code":106618,"language":1771,"meta":57,"style":57}," \u003Cplugin>\n \u003CgroupId>org.apache.maven.plugins\u003C/groupId>\n \u003CartifactId>maven-resources-plugin\u003C/artifactId>\n \u003Cexecutions>\n \u003Cexecution>\n \u003Cid>Copy Vue frontend into Spring Boot target static folder\u003C/id>\n \u003Cphase>process-resources\u003C/phase>\n \u003Cgoals>\n \u003Cgoal>copy-resources\u003C/goal>\n \u003C/goals>\n \u003Cconfiguration>\n \u003CoutputDirectory>target/classes/static\u003C/outputDirectory>\n \u003Cresources>\n \u003Cresource>\n \u003Cdirectory>src/frontend/dist\u003C/directory>\n \u003Cfiltering>true\u003C/filtering>\n \u003C/resource>\n \u003C/resources>\n \u003C/configuration>\n \u003C/execution>\n \u003C/executions>\n\u003C/plugin>\n",[59,106620,106621,106626,106630,106635,106639,106643,106648,106653,106657,106662,106666,106670,106675,106680,106685,106690,106695,106700,106705,106709,106713,106717],{"__ignoreMap":57},[62,106622,106623],{"class":64,"line":65},[62,106624,106625],{}," \u003Cplugin>\n",[62,106627,106628],{"class":64,"line":76},[62,106629,82317],{},[62,106631,106632],{"class":64,"line":82},[62,106633,106634],{}," \u003CartifactId>maven-resources-plugin\u003C/artifactId>\n",[62,106636,106637],{"class":64,"line":89},[62,106638,106422],{},[62,106640,106641],{"class":64,"line":95},[62,106642,78391],{},[62,106644,106645],{"class":64,"line":101},[62,106646,106647],{}," \u003Cid>Copy Vue frontend into Spring Boot target static folder\u003C/id>\n",[62,106649,106650],{"class":64,"line":107},[62,106651,106652],{}," \u003Cphase>process-resources\u003C/phase>\n",[62,106654,106655],{"class":64,"line":113},[62,106656,106436],{},[62,106658,106659],{"class":64,"line":129},[62,106660,106661],{}," \u003Cgoal>copy-resources\u003C/goal>\n",[62,106663,106664],{"class":64,"line":134},[62,106665,106446],{},[62,106667,106668],{"class":64,"line":156},[62,106669,106456],{},[62,106671,106672],{"class":64,"line":161},[62,106673,106674],{}," \u003CoutputDirectory>target/classes/static\u003C/outputDirectory>\n",[62,106676,106677],{"class":64,"line":167},[62,106678,106679],{}," \u003Cresources>\n",[62,106681,106682],{"class":64,"line":173},[62,106683,106684],{}," \u003Cresource>\n",[62,106686,106687],{"class":64,"line":179},[62,106688,106689],{}," \u003Cdirectory>src/frontend/dist\u003C/directory>\n",[62,106691,106692],{"class":64,"line":185},[62,106693,106694],{}," \u003Cfiltering>true\u003C/filtering>\n",[62,106696,106697],{"class":64,"line":191},[62,106698,106699],{}," \u003C/resource>\n",[62,106701,106702],{"class":64,"line":209},[62,106703,106704],{}," \u003C/resources>\n",[62,106706,106707],{"class":64,"line":220},[62,106708,106471],{},[62,106710,106711],{"class":64,"line":226},[62,106712,78416],{},[62,106714,106715],{"class":64,"line":231},[62,106716,106573],{},[62,106718,106719],{"class":64,"line":236},[62,106720,82352],{},[22,106722,106723],{},"And that is all of the configuration you need. From the command-line you can now package the application using Maven:",[52,106725,106726],{"className":1663,"code":3632,"language":1665,"meta":57,"style":57},[59,106727,106728],{"__ignoreMap":57},[62,106729,106730,106732,106734],{"class":64,"line":65},[62,106731,3639],{"class":122},[62,106733,3642],{"class":1675},[62,106735,3645],{"class":1675},[22,106737,106738,106739,74739],{},"When that is complete you should see something that looks like this and it will end up producing a new JAR ",[59,106740,106741],{},"fsjava-0.0.1-SNAPSHOT.jar",[52,106743,106745],{"className":1663,"code":106744,"language":1665,"meta":57,"style":57},"[INFO]\n[INFO] Results:\n[INFO]\n[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0\n[INFO]\n[INFO]\n[INFO] --- maven-jar-plugin:3.2.0:jar (default-jar) @ fsjava ---\n[INFO] Building jar: /Users/vega/dev/boot/full-stack-java-vue/target/fsjava-0.0.1-SNAPSHOT.jar\n[INFO]\n[INFO] --- spring-boot-maven-plugin:2.4.2:repackage (repackage) @ fsjava ---\n[INFO] Replacing main artifact with repackaged archive\n[INFO] ------------------------------------------------------------------------\n[INFO] BUILD SUCCESS\n[INFO] ------------------------------------------------------------------------\n[INFO] Total time: 20.083 s\n[INFO] Finished at: 2021-01-22T13:24:38-05:00\n[INFO] ------------------------------------------------------------------------\n",[59,106746,106747,106752,106757,106761,106766,106770,106774,106785,106790,106794,106804,106809,106814,106819,106823,106828,106833],{"__ignoreMap":57},[62,106748,106749],{"class":64,"line":65},[62,106750,106751],{"class":72},"[INFO]\n",[62,106753,106754],{"class":64,"line":76},[62,106755,106756],{"class":72},"[INFO] Results:\n",[62,106758,106759],{"class":64,"line":82},[62,106760,106751],{"class":72},[62,106762,106763],{"class":64,"line":89},[62,106764,106765],{"class":72},"[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0\n",[62,106767,106768],{"class":64,"line":95},[62,106769,106751],{"class":72},[62,106771,106772],{"class":64,"line":101},[62,106773,106751],{"class":72},[62,106775,106776,106779,106782],{"class":64,"line":107},[62,106777,106778],{"class":72},"[INFO] --- maven-jar-plugin:3.2.0:jar (",[62,106780,106781],{"class":122},"default-jar",[62,106783,106784],{"class":72},") @ fsjava ---\n",[62,106786,106787],{"class":64,"line":113},[62,106788,106789],{"class":72},"[INFO] Building jar: /Users/vega/dev/boot/full-stack-java-vue/target/fsjava-0.0.1-SNAPSHOT.jar\n",[62,106791,106792],{"class":64,"line":129},[62,106793,106751],{"class":72},[62,106795,106796,106799,106802],{"class":64,"line":134},[62,106797,106798],{"class":72},"[INFO] --- spring-boot-maven-plugin:2.4.2:repackage (",[62,106800,106801],{"class":122},"repackage",[62,106803,106784],{"class":72},[62,106805,106806],{"class":64,"line":156},[62,106807,106808],{"class":72},"[INFO] Replacing main artifact with repackaged archive\n",[62,106810,106811],{"class":64,"line":161},[62,106812,106813],{"class":72},"[INFO] ------------------------------------------------------------------------\n",[62,106815,106816],{"class":64,"line":167},[62,106817,106818],{"class":72},"[INFO] BUILD SUCCESS\n",[62,106820,106821],{"class":64,"line":173},[62,106822,106813],{"class":72},[62,106824,106825],{"class":64,"line":179},[62,106826,106827],{"class":72},"[INFO] Total time: 20.083 s\n",[62,106829,106830],{"class":64,"line":185},[62,106831,106832],{"class":72},"[INFO] Finished at: 2021-01-22T13:24:38-05:00\n",[62,106834,106835],{"class":64,"line":191},[62,106836,106813],{"class":72},[22,106838,106839,106840],{},"You can run this jar from the command line ",[59,106841,106842],{},"java -jar target/fsjava-0.0.1-SNAPSHOT.jar",[22,106844,106845],{},[653,106846],{"alt":106847,"src":106848},"Java JAR","/images/blog/2021/01/22/java_jar.png",[22,106850,106851],{},"With this single artifact you can push this into a production environment and you are ready to go. A few of my favorite options for doing this are:",[915,106853,106854,106861,106868],{},[37,106855,106856],{},[677,106857,106860],{"href":106858,"rel":106859},"https://www.heroku.com/",[681],"Heroku",[37,106862,106863],{},[677,106864,106867],{"href":106865,"rel":106866},"https://aws.amazon.com/elasticbeanstalk/",[681],"AWS Beanstalk",[37,106869,106870],{},[677,106871,106874],{"href":106872,"rel":106873},"https://azure.microsoft.com/en-us/services/spring-cloud/",[681],"Azure Spring",[26,106876,1499],{"id":1498},[22,106878,106879],{},"I wanted to keep the application simple in this article so we could focus on the plumbing of the project. That said if you're interested in seeing more please let me know. Spring Boot and Vue are two technologies I really enjoy using and I hope this helps others put the two together. As always friends...",[22,106881,36004,106882,82545],{},[36006,106883],{},[1527,106885,106886],{},"html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .spoOn, html code.shiki .spoOn{--shiki-default:#E36209;--shiki-dark:#FFAB70;--shiki-sepia:#E36209}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}html pre.shiki code .s4-Ip, html code.shiki .s4-Ip{--shiki-default:#6A737D;--shiki-dark:#6A737D;--shiki-sepia:#6A737D}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}html pre.shiki code .suaqH, html code.shiki .suaqH{--shiki-default:#22863A;--shiki-dark:#85E89D;--shiki-sepia:#22863A}",{"title":57,"searchDepth":76,"depth":76,"links":106888},[106889,106890,106891,106892,106900],{"id":48218,"depth":76,"text":48219},{"id":105470,"depth":76,"text":105446},{"id":105487,"depth":76,"text":105449},{"id":105508,"depth":76,"text":105509,"children":106893},[106894,106895,106896,106897,106898,106899],{"id":105520,"depth":82,"text":105457},{"id":57650,"depth":82,"text":2925},{"id":105335,"depth":82,"text":105462},{"id":105848,"depth":82,"text":105849},{"id":106110,"depth":82,"text":106111},{"id":106323,"depth":82,"text":105465},{"id":1498,"depth":76,"text":1499},{"_id":106902,"path":106903,"title":106904,"description":106905,"meta":106906,"body":106911},"content/blog/2021/01/08/network-throttling.md","/blog/2021/01/08/network-throttling","How to force a delay in a web app","In this tutorial, I will show you can force a delay in a web application for testing purposes.",{"slug":106907,"date":106908,"published":13,"tags":106909,"author":17,"cover":106910,"excerpt":-1},"network-throttling","2021-01-08T09:30:00.000Z",[32793,105335,56],"./network-throttling-cover.png",{"type":19,"value":106912,"toc":107884},[106913,106916,106922,106925,106936,106939,106943,106946,106951,106954,106960,106963,106967,106984,107363,107366,107433,107440,107472,107475,107534,107543,107622,107625,107628,107638,107726,107731,107745,107748,107876,107878,107881],[22,106914,106915],{},"So you have this amazing loading animation, and you want to test it out but you have a problem. When you're working on your local machine there isn't much latency so you never see your animated art.",[22,106917,106918],{},[653,106919],{"alt":106920,"src":106921},"Fancy Animation","/images/blog/2021/01/08/bean-eater-200px_small.jpeg",[22,106923,106924],{},"While it's good that you want to test this out this is also an opportunity to test our application for visitors that might be on a slower connection. We have to remember that not everyone is lucky enough to have access to hi-speed internet like you and I do. There are many ways that you can test this out but we are going to cover the following in this tutorial.",[915,106926,106927,106930,106933],{},[37,106928,106929],{},"Chrome DevTools (Bandwidth Throttling)",[37,106931,106932],{},"Client Side (Force Delay)",[37,106934,106935],{},"Server Side (Force Delay)",[22,106937,106938],{},"The code samples in this article will use Vue on the frontend and Java on the backend but the concepts are the same regardless of languages or frameworks.",[26,106940,106942],{"id":106941},"chrome-devtools","Chrome DevTools",[22,106944,106945],{},"As I mentioned in the intro it is important to test your applications on a slower connection. One way to accomplish this is by using the Chrome DevTools to throttle the network speed.",[29685,106947,106948],{},[22,106949,106950],{},"Network throttling is an intentional slowing down of internet speed. In web performance, network throttling, or network condition emulation, it is used to emulate low bandwidth conditions experienced by likely a large segment of a site's target user base",[22,106952,106953],{},"To do this open up DevTools and go to the Network tab. There is a dropdown on the first line that defaults to \"Online\". Change to what speed you want to imitate and reload the page to simulate that connection.",[22,106955,106956],{},[653,106957],{"alt":106958,"src":106959},"Chrome DevTools Network Throttling","/images/blog/2021/01/08/chrome-devtools-network-throttling.png",[22,106961,106962],{},"This is a great first step but this will throttle the entire connection and I often don't have the patience for this during development.",[26,106964,106966],{"id":106965},"client-side-vue","Client-Side (Vue)",[22,106968,106969,106970,106975,106976,106979,106980,106983],{},"The next solution I want to cover is building in a delay to the client-side code. In the following example, I am using a ",[677,106971,106974],{"href":106972,"rel":106973},"https://icanhazdadjoke.com/",[681],"public API"," to return a random dad joke. When the component is mounted the ",[59,106977,106978],{},"loadJoke()"," method is called and a ",[59,106981,106982],{},"fetch()"," request is made to the API. We use state within the component to display the loading animation until the fetch request has been completed and we have a joke to display.",[52,106985,106987],{"className":105981,"code":106986,"language":105335,"meta":57,"style":57},"\u003Ctemplate>\n \u003Cdiv class=\"dadjoke\">\n \u003Ch1>Random Dad Jokes\u003C/h1>\n \u003Cp v-if=\"loading\">\u003Cimg src=\"./assets/bean-eater-200px.gif\" />\u003C/p>\n \u003Cp v-else>{{ joke }}\u003C/p>\n \u003C/div>\n\u003C/template>\n\n\u003Cscript>\nexport default {\n name: \"App\",\n data() {\n return {\n joke: \"\",\n loading: true,\n };\n },\n methods: {\n loadJoke() {\n fetch(\"https://icanhazdadjoke.com/\", {\n headers: {\n Accept: \"application/json\",\n },\n })\n .then((response) => {\n return response.json();\n })\n .then((data) => {\n if (data.status === 200) {\n this.joke = data.joke;\n this.loading = false;\n }\n })\n .catch((err) => console.log(err));\n },\n },\n mounted() {\n this.loadJoke();\n },\n};\n\u003C/script>\n",[59,106988,106989,106997,107012,107025,107057,107073,107081,107089,107093,107101,107109,107118,107124,107130,107139,107148,107152,107156,107161,107168,107181,107186,107195,107200,107205,107221,107232,107236,107252,107266,107278,107291,107295,107299,107321,107325,107329,107335,107346,107350,107355],{"__ignoreMap":57},[62,106990,106991,106993,106995],{"class":64,"line":65},[62,106992,760],{"class":72},[62,106994,105991],{"class":1780},[62,106996,1784],{"class":72},[62,106998,106999,107001,107003,107005,107007,107010],{"class":64,"line":76},[62,107000,33056],{"class":72},[62,107002,15944],{"class":1780},[62,107004,119],{"class":122},[62,107006,146],{"class":72},[62,107008,107009],{"class":1675},"\"dadjoke\"",[62,107011,1784],{"class":72},[62,107013,107014,107016,107018,107021,107023],{"class":64,"line":82},[62,107015,1789],{"class":72},[62,107017,4168],{"class":1780},[62,107019,107020],{"class":72},">Random Dad Jokes\u003C/",[62,107022,4168],{"class":1780},[62,107024,1784],{"class":72},[62,107026,107027,107029,107031,107034,107036,107039,107041,107043,107045,107047,107050,107053,107055],{"class":64,"line":89},[62,107028,1789],{"class":72},[62,107030,22],{"class":1780},[62,107032,107033],{"class":122}," v-if",[62,107035,146],{"class":72},[62,107037,107038],{"class":1675},"\"loading\"",[62,107040,68460],{"class":72},[62,107042,653],{"class":1780},[62,107044,15849],{"class":122},[62,107046,146],{"class":72},[62,107048,107049],{"class":1675},"\"./assets/bean-eater-200px.gif\"",[62,107051,107052],{"class":72}," />\u003C/",[62,107054,22],{"class":1780},[62,107056,1784],{"class":72},[62,107058,107059,107061,107063,107066,107069,107071],{"class":64,"line":95},[62,107060,1789],{"class":72},[62,107062,22],{"class":1780},[62,107064,107065],{"class":122}," v-else",[62,107067,107068],{"class":72},">{{ joke }}\u003C/",[62,107070,22],{"class":1780},[62,107072,1784],{"class":72},[62,107074,107075,107077,107079],{"class":64,"line":101},[62,107076,33187],{"class":72},[62,107078,15944],{"class":1780},[62,107080,1784],{"class":72},[62,107082,107083,107085,107087],{"class":64,"line":107},[62,107084,1818],{"class":72},[62,107086,105991],{"class":1780},[62,107088,1784],{"class":72},[62,107090,107091],{"class":64,"line":113},[62,107092,79],{"emptyLinePlaceholder":13},[62,107094,107095,107097,107099],{"class":64,"line":129},[62,107096,760],{"class":72},[62,107098,15846],{"class":1780},[62,107100,1784],{"class":72},[62,107102,107103,107105,107107],{"class":64,"line":134},[62,107104,14767],{"class":68},[62,107106,106045],{"class":68},[62,107108,126],{"class":72},[62,107110,107111,107113,107116],{"class":64,"line":156},[62,107112,106052],{"class":72},[62,107114,107115],{"class":1675},"\"App\"",[62,107117,3338],{"class":72},[62,107119,107120,107122],{"class":64,"line":161},[62,107121,106190],{"class":122},[62,107123,206],{"class":72},[62,107125,107126,107128],{"class":64,"line":167},[62,107127,2599],{"class":68},[62,107129,126],{"class":72},[62,107131,107132,107135,107137],{"class":64,"line":173},[62,107133,107134],{"class":72}," joke: ",[62,107136,25895],{"class":1675},[62,107138,3338],{"class":72},[62,107140,107141,107144,107146],{"class":64,"line":179},[62,107142,107143],{"class":72}," loading: ",[62,107145,21775],{"class":149},[62,107147,3338],{"class":72},[62,107149,107150],{"class":64,"line":185},[62,107151,40087],{"class":72},[62,107153,107154],{"class":64,"line":191},[62,107155,32848],{"class":72},[62,107157,107158],{"class":64,"line":209},[62,107159,107160],{"class":72}," methods: {\n",[62,107162,107163,107166],{"class":64,"line":220},[62,107164,107165],{"class":122}," loadJoke",[62,107167,206],{"class":72},[62,107169,107170,107173,107175,107178],{"class":64,"line":226},[62,107171,107172],{"class":122}," fetch",[62,107174,2109],{"class":72},[62,107176,107177],{"class":1675},"\"https://icanhazdadjoke.com/\"",[62,107179,107180],{"class":72},", {\n",[62,107182,107183],{"class":64,"line":231},[62,107184,107185],{"class":72}," headers: {\n",[62,107187,107188,107191,107193],{"class":64,"line":236},[62,107189,107190],{"class":72}," Accept: ",[62,107192,18356],{"class":1675},[62,107194,3338],{"class":72},[62,107196,107197],{"class":64,"line":242},[62,107198,107199],{"class":72}," },\n",[62,107201,107202],{"class":64,"line":247},[62,107203,107204],{"class":72}," })\n",[62,107206,107207,107209,107211,107213,107215,107217,107219],{"class":64,"line":252},[62,107208,3862],{"class":72},[62,107210,36912],{"class":122},[62,107212,85590],{"class":72},[62,107214,13389],{"class":889},[62,107216,5024],{"class":72},[62,107218,21525],{"class":68},[62,107220,126],{"class":72},[62,107222,107223,107226,107228,107230],{"class":64,"line":257},[62,107224,107225],{"class":68}," return",[62,107227,12760],{"class":72},[62,107229,3673],{"class":122},[62,107231,822],{"class":72},[62,107233,107234],{"class":64,"line":271},[62,107235,85702],{"class":72},[62,107237,107238,107240,107242,107244,107246,107248,107250],{"class":64,"line":281},[62,107239,3862],{"class":72},[62,107241,36912],{"class":122},[62,107243,85590],{"class":72},[62,107245,106264],{"class":889},[62,107247,5024],{"class":72},[62,107249,21525],{"class":68},[62,107251,126],{"class":72},[62,107253,107254,107257,107260,107262,107264],{"class":64,"line":286},[62,107255,107256],{"class":68}," if",[62,107258,107259],{"class":72}," (data.status ",[62,107261,21072],{"class":68},[62,107263,6821],{"class":149},[62,107265,768],{"class":72},[62,107267,107268,107270,107273,107275],{"class":64,"line":291},[62,107269,9947],{"class":149},[62,107271,107272],{"class":72},".joke ",[62,107274,146],{"class":68},[62,107276,107277],{"class":72}," data.joke;\n",[62,107279,107280,107282,107285,107287,107289],{"class":64,"line":296},[62,107281,9947],{"class":149},[62,107283,107284],{"class":72},".loading ",[62,107286,146],{"class":68},[62,107288,1165],{"class":149},[62,107290,153],{"class":72},[62,107292,107293],{"class":64,"line":302},[62,107294,51018],{"class":72},[62,107296,107297],{"class":64,"line":308},[62,107298,85702],{"class":72},[62,107300,107301,107303,107305,107307,107310,107312,107314,107316,107318],{"class":64,"line":314},[62,107302,3862],{"class":72},[62,107304,883],{"class":122},[62,107306,85590],{"class":72},[62,107308,107309],{"class":889},"err",[62,107311,5024],{"class":72},[62,107313,21525],{"class":68},[62,107315,58268],{"class":72},[62,107317,58271],{"class":122},[62,107319,107320],{"class":72},"(err));\n",[62,107322,107323],{"class":64,"line":320},[62,107324,50124],{"class":72},[62,107326,107327],{"class":64,"line":326},[62,107328,32848],{"class":72},[62,107330,107331,107333],{"class":64,"line":338},[62,107332,106219],{"class":122},[62,107334,206],{"class":72},[62,107336,107337,107339,107341,107344],{"class":64,"line":343},[62,107338,39124],{"class":149},[62,107340,2755],{"class":72},[62,107342,107343],{"class":122},"loadJoke",[62,107345,822],{"class":72},[62,107347,107348],{"class":64,"line":357},[62,107349,32848],{"class":72},[62,107351,107352],{"class":64,"line":366},[62,107353,107354],{"class":72},"};\n",[62,107356,107357,107359,107361],{"class":64,"line":371},[62,107358,1818],{"class":72},[62,107360,15846],{"class":1780},[62,107362,1784],{"class":72},[22,107364,107365],{},"Even in a CodeSandbox environment, this is pretty fast and you don't see the loading animation.",[52,107367,107369],{"className":15773,"code":107368,"language":15775,"meta":57,"style":57},"\u003Ciframe src=\"https://codesandbox.io/embed/angry-fast-3te0x?fontsize=14&module=%2Fsrc%2FApp.vue&theme=dark\"\n style=\"width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;\"\n title=\"angry-fast-3te0x\"\n allow=\"accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking\"\n sandbox=\"allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts\"\n >\u003C/iframe>\n",[59,107370,107371,107384,107394,107404,107414,107424],{"__ignoreMap":57},[62,107372,107373,107375,107377,107379,107381],{"class":64,"line":65},[62,107374,760],{"class":72},[62,107376,104354],{"class":1780},[62,107378,15849],{"class":122},[62,107380,146],{"class":72},[62,107382,107383],{"class":1675},"\"https://codesandbox.io/embed/angry-fast-3te0x?fontsize=14&module=%2Fsrc%2FApp.vue&theme=dark\"\n",[62,107385,107386,107389,107391],{"class":64,"line":76},[62,107387,107388],{"class":122}," style",[62,107390,146],{"class":72},[62,107392,107393],{"class":1675},"\"width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;\"\n",[62,107395,107396,107399,107401],{"class":64,"line":82},[62,107397,107398],{"class":122}," title",[62,107400,146],{"class":72},[62,107402,107403],{"class":1675},"\"angry-fast-3te0x\"\n",[62,107405,107406,107409,107411],{"class":64,"line":89},[62,107407,107408],{"class":122}," allow",[62,107410,146],{"class":72},[62,107412,107413],{"class":1675},"\"accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking\"\n",[62,107415,107416,107419,107421],{"class":64,"line":95},[62,107417,107418],{"class":122}," sandbox",[62,107420,146],{"class":72},[62,107422,107423],{"class":1675},"\"allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts\"\n",[62,107425,107426,107429,107431],{"class":64,"line":101},[62,107427,107428],{"class":72}," >\u003C/",[62,107430,104354],{"class":1780},[62,107432,1784],{"class":72},[22,107434,107435,107436,107439],{},"To force a delay you can use the ",[59,107437,107438],{},"setTimeout()"," function in JavaScript to set a timer which executes a function or specified piece of code once the timer expires. The first argument is the function or code you want to execute and the second argument is the delay in milliseconds. In this example, I am forcing a three-second delay.",[52,107441,107443],{"className":105863,"code":107442,"language":105865,"meta":57,"style":57},"mounted() {\n setTimeout(this.loadJoke, 3000);\n}\n",[59,107444,107445,107452,107468],{"__ignoreMap":57},[62,107446,107447,107450],{"class":64,"line":65},[62,107448,107449],{"class":122},"mounted",[62,107451,206],{"class":72},[62,107453,107454,107457,107459,107461,107464,107466],{"class":64,"line":76},[62,107455,107456],{"class":122}," setTimeout",[62,107458,2109],{"class":72},[62,107460,1295],{"class":149},[62,107462,107463],{"class":72},".loadJoke, ",[62,107465,105902],{"class":149},[62,107467,1133],{"class":72},[62,107469,107470],{"class":64,"line":82},[62,107471,379],{"class":72},[22,107473,107474],{},"Now when we run our example we can have a three second delay and we can clearly see our loading animation.",[52,107476,107478],{"className":15773,"code":107477,"language":15775,"meta":57,"style":57},"\u003Ciframe src=\"https://codesandbox.io/embed/cocky-benz-o2x09?fontsize=14&module=%2Fsrc%2FApp.vue&theme=dark\"\n style=\"width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;\"\n title=\"cocky-benz-o2x09\"\n allow=\"accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking\"\n sandbox=\"allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts\"\n >\u003C/iframe>\n",[59,107479,107480,107493,107501,107510,107518,107526],{"__ignoreMap":57},[62,107481,107482,107484,107486,107488,107490],{"class":64,"line":65},[62,107483,760],{"class":72},[62,107485,104354],{"class":1780},[62,107487,15849],{"class":122},[62,107489,146],{"class":72},[62,107491,107492],{"class":1675},"\"https://codesandbox.io/embed/cocky-benz-o2x09?fontsize=14&module=%2Fsrc%2FApp.vue&theme=dark\"\n",[62,107494,107495,107497,107499],{"class":64,"line":76},[62,107496,107388],{"class":122},[62,107498,146],{"class":72},[62,107500,107393],{"class":1675},[62,107502,107503,107505,107507],{"class":64,"line":82},[62,107504,107398],{"class":122},[62,107506,146],{"class":72},[62,107508,107509],{"class":1675},"\"cocky-benz-o2x09\"\n",[62,107511,107512,107514,107516],{"class":64,"line":89},[62,107513,107408],{"class":122},[62,107515,146],{"class":72},[62,107517,107413],{"class":1675},[62,107519,107520,107522,107524],{"class":64,"line":95},[62,107521,107418],{"class":122},[62,107523,146],{"class":72},[62,107525,107423],{"class":1675},[62,107527,107528,107530,107532],{"class":64,"line":101},[62,107529,107428],{"class":72},[62,107531,104354],{"class":1780},[62,107533,1784],{"class":72},[22,107535,107536,107537,107542],{},"The one downside to this approach is that you might forget to remove code like this and you don't want this getting checked in and pushed into production. One solution to this problem is to use environment variables to make sure you are in development mode when forcing a delay. In the following example, I am using ",[677,107538,107541],{"href":107539,"rel":107540},"https://github.com/vitejs/vite",[681],"Vite"," to get the environment and to set a custom delay.",[52,107544,107546],{"className":105863,"code":107545,"language":105865,"meta":57,"style":57},"mounted() {\n if (import.meta.env.DEV) {\n setTimeout(this.loadJoke, import.meta.env.VITE_FETCH_DELAY);\n } else {\n this.loadJoke;\n }\n}\n",[59,107547,107548,107554,107574,107598,107607,107614,107618],{"__ignoreMap":57},[62,107549,107550,107552],{"class":64,"line":65},[62,107551,107449],{"class":122},[62,107553,206],{"class":72},[62,107555,107556,107559,107561,107563,107565,107567,107570,107572],{"class":64,"line":76},[62,107557,107558],{"class":68}," if",[62,107560,744],{"class":72},[62,107562,27875],{"class":68},[62,107564,2755],{"class":72},[62,107566,20701],{"class":149},[62,107568,107569],{"class":72},".env.",[62,107571,37657],{"class":149},[62,107573,768],{"class":72},[62,107575,107576,107579,107581,107583,107585,107587,107589,107591,107593,107596],{"class":64,"line":82},[62,107577,107578],{"class":122}," setTimeout",[62,107580,2109],{"class":72},[62,107582,1295],{"class":149},[62,107584,107463],{"class":72},[62,107586,27875],{"class":68},[62,107588,2755],{"class":72},[62,107590,20701],{"class":149},[62,107592,107569],{"class":72},[62,107594,107595],{"class":149},"VITE_FETCH_DELAY",[62,107597,1133],{"class":72},[62,107599,107600,107603,107605],{"class":64,"line":89},[62,107601,107602],{"class":72}," } ",[62,107604,12783],{"class":68},[62,107606,126],{"class":72},[62,107608,107609,107611],{"class":64,"line":95},[62,107610,39124],{"class":149},[62,107612,107613],{"class":72},".loadJoke;\n",[62,107615,107616],{"class":64,"line":101},[62,107617,3731],{"class":72},[62,107619,107620],{"class":64,"line":107},[62,107621,379],{"class":72},[26,107623,107624],{"id":61187},"Server-Side",[22,107626,107627],{},"The next solution I want to look at is building in a delay to the server-side code. This wouldn't work for the previous example because we didn't have control over that API. If you are the one working on the server-side code though you can force a delay there.",[22,107629,107630,107631,107634,107635,22822],{},"In the following example, I am using Java but you can accomplish this in whatever language you're using. I have a Spring Boot Controller that returns a random joke but before that happens we force a delay of three seconds using the static ",[59,107632,107633],{},"sleep()"," method in the ",[59,107636,107637],{},"Thread",[52,107639,107641],{"className":54,"code":107640,"language":56,"meta":57,"style":57},"@RestController\n@RequestMapping(\"/api/jokes\")\npublic class JokeController {\n\n @GetMapping\n public String random() throws InterruptedException {\n Thread.sleep(3000);\n return \"What do you call a pig that knows karate? A pork chop!\";\n }\n}\n",[59,107642,107643,107649,107662,107673,107677,107683,107697,107709,107718,107722],{"__ignoreMap":57},[62,107644,107645,107647],{"class":64,"line":65},[62,107646,942],{"class":72},[62,107648,2342],{"class":68},[62,107650,107651,107653,107655,107657,107660],{"class":64,"line":76},[62,107652,942],{"class":72},[62,107654,10592],{"class":68},[62,107656,2109],{"class":72},[62,107658,107659],{"class":1675},"\"/api/jokes\"",[62,107661,2212],{"class":72},[62,107663,107664,107666,107668,107671],{"class":64,"line":82},[62,107665,116],{"class":68},[62,107667,119],{"class":68},[62,107669,107670],{"class":122}," JokeController",[62,107672,126],{"class":72},[62,107674,107675],{"class":64,"line":89},[62,107676,79],{"emptyLinePlaceholder":13},[62,107678,107679,107681],{"class":64,"line":95},[62,107680,2143],{"class":72},[62,107682,47319],{"class":68},[62,107684,107685,107687,107689,107691,107693,107695],{"class":64,"line":101},[62,107686,194],{"class":68},[62,107688,2469],{"class":72},[62,107690,41143],{"class":122},[62,107692,5398],{"class":72},[62,107694,11501],{"class":68},[62,107696,48433],{"class":72},[62,107698,107699,107701,107703,107705,107707],{"class":64,"line":107},[62,107700,48438],{"class":72},[62,107702,847],{"class":122},[62,107704,2109],{"class":72},[62,107706,105902],{"class":149},[62,107708,1133],{"class":72},[62,107710,107711,107713,107716],{"class":64,"line":113},[62,107712,360],{"class":68},[62,107714,107715],{"class":1675}," \"What do you call a pig that knows karate? A pork chop!\"",[62,107717,153],{"class":72},[62,107719,107720],{"class":64,"line":129},[62,107721,223],{"class":72},[62,107723,107724],{"class":64,"line":134},[62,107725,379],{"class":72},[22,107727,107728,107729,1266],{},"As with the client-side solution you should be careful about checking this in and letting this go into production. You could set an active profile in ",[59,107730,1265],{},[52,107732,107734],{"className":54,"code":107733,"language":56,"meta":57,"style":57},"spring.profiles.active=dev\n",[59,107735,107736],{"__ignoreMap":57},[62,107737,107738,107740,107742],{"class":64,"line":65},[62,107739,15094],{"class":72},[62,107741,146],{"class":68},[62,107743,107744],{"class":72},"dev\n",[22,107746,107747],{},"Back in your controller, you can get that value and as along as it's not a production environment you can force your delay.",[52,107749,107751],{"className":54,"code":107750,"language":56,"meta":57,"style":57},"@RestController\n@RequestMapping(\"/api/jokes\")\npublic class JokeController {\n\n @Value(\"${spring.profiles.active}\")\n private String ENV_MODE;\n\n @GetMapping\n public String random() throws InterruptedException {\n if( ENV_MODE != \"prod\" ) {\n Thread.sleep(3000);\n }\n return \"What do you call a pig that knows karate? A pork chop!\";\n }\n}\n",[59,107752,107753,107759,107771,107781,107785,107798,107805,107809,107815,107829,107843,107856,107860,107868,107872],{"__ignoreMap":57},[62,107754,107755,107757],{"class":64,"line":65},[62,107756,942],{"class":72},[62,107758,2342],{"class":68},[62,107760,107761,107763,107765,107767,107769],{"class":64,"line":76},[62,107762,942],{"class":72},[62,107764,10592],{"class":68},[62,107766,2109],{"class":72},[62,107768,107659],{"class":1675},[62,107770,2212],{"class":72},[62,107772,107773,107775,107777,107779],{"class":64,"line":82},[62,107774,116],{"class":68},[62,107776,119],{"class":68},[62,107778,107670],{"class":122},[62,107780,126],{"class":72},[62,107782,107783],{"class":64,"line":89},[62,107784,79],{"emptyLinePlaceholder":13},[62,107786,107787,107789,107791,107793,107796],{"class":64,"line":95},[62,107788,2143],{"class":72},[62,107790,14632],{"class":68},[62,107792,2109],{"class":72},[62,107794,107795],{"class":1675},"\"${spring.profiles.active}\"",[62,107797,2212],{"class":72},[62,107799,107800,107802],{"class":64,"line":101},[62,107801,137],{"class":68},[62,107803,107804],{"class":72}," String ENV_MODE;\n",[62,107806,107807],{"class":64,"line":107},[62,107808,79],{"emptyLinePlaceholder":13},[62,107810,107811,107813],{"class":64,"line":113},[62,107812,2143],{"class":72},[62,107814,47319],{"class":68},[62,107816,107817,107819,107821,107823,107825,107827],{"class":64,"line":129},[62,107818,194],{"class":68},[62,107820,2469],{"class":72},[62,107822,41143],{"class":122},[62,107824,5398],{"class":72},[62,107826,11501],{"class":68},[62,107828,48433],{"class":72},[62,107830,107831,107833,107836,107838,107841],{"class":64,"line":134},[62,107832,12741],{"class":68},[62,107834,107835],{"class":72},"( ENV_MODE ",[62,107837,13321],{"class":68},[62,107839,107840],{"class":1675}," \"prod\"",[62,107842,73082],{"class":72},[62,107844,107845,107848,107850,107852,107854],{"class":64,"line":156},[62,107846,107847],{"class":72}," Thread.",[62,107849,847],{"class":122},[62,107851,2109],{"class":72},[62,107853,105902],{"class":149},[62,107855,1133],{"class":72},[62,107857,107858],{"class":64,"line":161},[62,107859,533],{"class":72},[62,107861,107862,107864,107866],{"class":64,"line":167},[62,107863,360],{"class":68},[62,107865,107715],{"class":1675},[62,107867,153],{"class":72},[62,107869,107870],{"class":64,"line":173},[62,107871,223],{"class":72},[62,107873,107874],{"class":64,"line":179},[62,107875,379],{"class":72},[26,107877,1499],{"id":1498},[22,107879,107880],{},"This question came to me from one of our students and I thought it was a good one. Just remember while you might want to test one component or feature for latency it is important to test the entire application with a slower connection to ensure each visitor has a good experience.",[1527,107882,107883],{},"html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .suaqH, html code.shiki .suaqH{--shiki-default:#22863A;--shiki-dark:#85E89D;--shiki-sepia:#22863A}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}html pre.shiki code .spoOn, html code.shiki .spoOn{--shiki-default:#E36209;--shiki-dark:#FFAB70;--shiki-sepia:#E36209}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}",{"title":57,"searchDepth":76,"depth":76,"links":107885},[107886,107887,107888,107889],{"id":106941,"depth":76,"text":106942},{"id":106965,"depth":76,"text":106966},{"id":61187,"depth":76,"text":107624},{"id":1498,"depth":76,"text":1499},{"_id":107891,"path":107892,"title":107893,"description":107894,"meta":107895,"body":107900},"content/blog/2021/01/07/happy-new-year-2021.md","/blog/2021/01/07/happy-new-year-2021","New Year, New Opportunities and Goals","In this article, I have some exciting personal news to share and I want to take a look ahead at the new year.",{"slug":107896,"date":107897,"published":13,"tags":107898,"author":17,"cover":107899,"excerpt":-1},"happy-new-year-2021","2021-01-07T11:30:00.000Z",[20701],"isaac-smith-YwrdbQw0oco-unsplash.jpg",{"type":19,"value":107901,"toc":108180},[107902,107905,107909,107924,107927,107933,107936,107942,107946,107951,107964,107967,107970,107973,107981,107987,107999,108003,108006,108012,108016,108025,108034,108037,108039,108048,108051,108074,108078,108086,108090,108093,108097,108100,108114,108117,108126,108129,108134,108137,108143,108147,108150,108152,108155,108157,108160,108162,108165,108167,108170,108172,108175,108177],[22,107903,107904],{},"I think we can all agree that we would like to put 2020 in the rear-view mirror and begin to look forward to the new year. In this post, I am going to share some exciting personal news, what I'm looking forward to learning, and some goals I want to set for myself this year.",[26,107906,107908],{"id":107907},"goodbye-tech-elevator","Goodbye Tech Elevator",[22,107910,107911,107912,107917,107918,107923],{},"Leaving an employer is never easy and it's especially hard when you love the company you work for. ",[677,107913,107916],{"href":107914,"rel":107915},"https://www.techelevator.com/",[681],"Tech Elevator's"," mission is near and dear to my heart and I will continue to be an advocate for them. If you're looking into ",[677,107919,107922],{"href":107920,"rel":107921},"https://www.switchup.org/rankings/best-coding-bootcamps",[681],"coding bootcamps"," I honestly think they are one of the best in the country and you should check them out.",[22,107925,107926],{},"I was a curriculum developer for 2.5 years and love everything about the content the curriculum team was able to produce. As a team, we refined our process over that time and I went from someone who was a content creator to someone who understands what a professional curriculum looks like.",[22,107928,107929,107930],{},"I owe a lot of this to my boss, co-founder of Tech Elevator, David Wintrich. Not only did I learn a lot from him but he was one of the best people I have ever had the privilege to work for. As I am writing this it's clear to me how much I am going to miss him. ",[646,107931,107932],{},"#MapleHeights",[22,107934,107935],{},"I haven't been in the office since the first week of March and it's starting to hit me that I won't be going back. I love the energy that the students and my coworkers brought to that space every single day. I'm going to miss the morning coffee chats with so many wonderful people. There are so many great people at Tech Elevator and I hope that I left a good impression on them and that we all stay in touch. 💔",[22,107937,107938],{},[653,107939],{"alt":107940,"src":107941},"Tech Elevator Cleveland","/images/blog/2021/01/07/techelevator.jpg",[26,107943,107945],{"id":107944},"hello-briebug-software","Hello, Briebug Software",[29685,107947,107948],{},[22,107949,107950],{},"“Life is a circle. The end of one journey is the beginning of the next.”\n― Joseph M. Marshall III",[22,107952,107953,107954,107958,107959,2755],{},"I am excited to announce that I have joined ",[677,107955,107957],{"href":104518,"rel":107956},[681],"Briebug software"," as an Enterprise Java Architect. I didn't know much about Briebug going into this process but I was referred to by my friend ",[677,107960,107963],{"href":107961,"rel":107962},"https://twitter.com/JordanPowell88",[681],"Jordan Powell",[22,107965,107966],{},"Jordan had nothing but great things to say about the company culture and how he thought I would be a great fit. After talking to a few employees at Briebug I was beyond excited about this opportunity.",[22,107968,107969],{},"Java was the first programming language I learned and I have spent most of my career on the JVM. This opportunity gives me the chance to work on some exciting projects solving some of the most challenging problems our clients have.",[22,107971,107972],{},"As much as I love solving problems through code it isn't what drives me. I really love to learn new things and teach what I have learned to others. This position gives me the chance to work with some very smart people and I feed off of that. It also gives me a chance to teach and mentor other developers as a team lead and I think I'm looking forward to that the most.",[22,107974,107975,107976,2755],{},"If that wasn't exciting enough, we are hiring! We are looking to fill many roles so if you're interested in working with me please check out our ",[677,107977,107980],{"href":107978,"rel":107979},"https://briebug.com/careers",[681],"careers page",[22,107982,107983],{},[653,107984],{"alt":107985,"src":107986},"Code Editor","/images/blog/2021/01/07/christopher-gower-m_HRfLhgABo-unsplash.jpg",[22,107988,107989,107990,107994,107995,2755],{},"What this means for you is that you can expect a lot more content on this blog and on my ",[677,107991,37949],{"href":107992,"rel":107993},"https://www.danvega.dev/youtube",[681]," channel around the projects I will be working on day to day. If you have specific items you would like to see me cover please leave a comment below 👇🏻 or reach out to me on ",[677,107996,86336],{"href":107997,"rel":107998},"https://www.danvega.dev/twitter",[681],[26,108000,108002],{"id":108001},"learning","Learning",[22,108004,108005],{},"I don't know about you but I keep a long list of subjects that I am interested in learning. This could be something brand new or brushing up on an existing skill. This list is always changing and given my new role I am going to focus a lot on Enterprise Architecture, Cloud and leadership skills.",[22,108007,108008],{},[653,108009],{"alt":108010,"src":108011},"Bookshelf","/images/blog/2021/01/07/pickawood-YbLitAY8bPA-unsplash.jpg",[636,108013,108015],{"id":108014},"software-architecture","Software Architecture",[22,108017,108018,108019,108024],{},"As an architect, I am looking forward to obsessing over finding the best solutions to our customer's problems. I am going to start by re-reading ",[677,108020,108023],{"href":108021,"rel":108022},"https://amzn.to/3niCGfq",[681],"\"Clean Architecture\" by Robert C Martin",". This book does a great job by covering a lot of the fundamentals of architecture and design principles.",[22,108026,108027,108028,108033],{},"I also picked up a new book ",[677,108029,108032],{"href":108030,"rel":108031},"https://amzn.to/3ousa6x",[681],"Fundamentals of Software Architecture"," that I am looking forward to reading. Finally, I plan on spending some time take a deeper dive into software architecture patterns like microservices and event-driven architecture.",[22,108035,108036],{},"If there is anything you would like to see me cover please let me know below 👇🏻.",[636,108038,47077],{"id":78619},[22,108040,108041,108042,108047],{},"I am in awe of all the capabilities that Amazon Web Services (AWS) provides. In my ",[677,108043,108046],{"href":108044,"rel":108045},"https://www.udemy.com/course/spring-boot-2/?referralCode=ECB6B9F8EF104672AF4A",[681],"Getting Started with Spring Boot 2"," course I used AWS to launch the application that we built into production.",[22,108049,108050],{},"I spent some time over the break to better understand what services are available and when to reach for them. I have just begun to scratch the surface of what AWS can do and here are a few services that I am excited to learn more about.",[915,108052,108053,108060,108067],{},[37,108054,108055],{},[677,108056,108059],{"href":108057,"rel":108058},"https://docs.amplify.aws/",[681],"AWS Amplify",[37,108061,108062],{},[677,108063,108066],{"href":108064,"rel":108065},"https://aws.amazon.com/appsync/",[681],"AWS AppSync",[37,108068,108069],{},[677,108070,108073],{"href":108071,"rel":108072},"https://aws.amazon.com/lambda/",[681],"AWS Lambda",[636,108075,108077],{"id":108076},"azure","Azure",[22,108079,108080,108081,108085],{},"I haven't had the opportunity to use Microsoft's cloud services yet and I am going to take advantage of them this year so I am looking forward to diving right in. I am especially excited to check out ",[677,108082,108084],{"href":106872,"rel":108083},[681],"Azure Spring Cloud"," which is a fully managed Spring Cloud service, jointly built and operated with VMware.",[636,108087,108089],{"id":108088},"leadership-skills","Leadership Skills",[22,108091,108092],{},"On the non-technical front, being a good leader is important to me. My goals here are to build trust and respect with my team and I found the perfect book. The Art of leadership by Michael Lopp looks like a fantastic read. Michael spent time at Netscape, Apple, and Slack and he shares a series of small but compelling practices to help you build leadership skills.",[636,108094,108096],{"id":108095},"just-for-fun","Just for fun",[22,108098,108099],{},"There are a few things on my list that If I ever found myself sitting around with nothing to do (unlikely) that I would love to learn.",[915,108101,108102,108108],{},[37,108103,108104,108107],{},[646,108105,108106],{},"Go",": I have already started learning Go but I need to find more time for it and a few real-world applications to give me a kick start.",[37,108109,108110,108113],{},[646,108111,108112],{},"Mobile Development"," (Swift / Flutter): I don't know why but I have always wanted to build a mobile application. I don't have any ideas for an app at the moment but when I do I think it would be so cool to publish an application to an app store 🤷♂️",[26,108115,108116],{"id":33475},"Goals",[22,108118,108119,108120,108125],{},"When the new year starts I usually like to set some goals. I have talked about this before but one of my favorite books on this subject is ",[677,108121,108124],{"href":108122,"rel":108123},"https://amzn.to/3ni9Goj",[681],"Your Best Year Ever by Michael Hyatt",". I learned how to make specific goals and how to achieve them. I'm throwing a lot of that knowledge out the door this year.",[22,108127,108128],{},"The pandemic has been a real challenge with two young daughters at home. I'm also starting a new job that is going to require my full focus. This doesn't leave a lot of time to work towards some of these goals. With that said there are things I want to do or at the very least work towards this year. That is why I am not going to put any specific time measurement on my goals and they very well could go into next year.",[22,108130,108131],{},[653,108132],{"alt":108116,"src":108133},"/images/blog/2021/01/07/danielle-macinnes-IuLgi9PWETU-unsplash.jpg",[636,108135,108136],{"id":36995},"Java Champion",[22,108138,108139,108142],{},[677,108140,89804],{"href":104931,"rel":108141},[681]," are leaders in the Java community who are experts at what they do. I happen to know a few Java Champions and I look up to them and aspire to be like them. I feel like I do a lot for the Java community but I could be doing so much more. I am going to figure out more ways to contribute to the community who has given me so much so that one day I might join this list.",[636,108144,108146],{"id":108145},"cloud-certifications","Cloud Certifications",[22,108148,108149],{},"I would really like to get a few cloud certifications from AWS and build from there. I am going to start with the Cloud Practitioner, Solutions Architect Associate, and Solutions Architect Professional.",[636,108151,31404],{"id":37521},[22,108153,108154],{},"My new job is going to afford me the opportunity to give back to the community and create content. This will be through my blog, YouTube channel and live streaming. This is one of those things that I can't put a number on yet but when I do I just want to be consistent. Consistently provided value for you is my goal, not burnout.",[636,108156,99601],{"id":8585},[22,108158,108159],{},"Career aspiration for me has always been to write a book. I'm not naive and realize that this isn't going to happen this year but It's always in the back of my head. As a way to kickstart this, I have a few ideas of e-books that I could create. This would be a shorter more manageable goal and something I could finish this year.",[636,108161,53436],{"id":57582},[22,108163,108164],{},"I miss creating courses. The whole process from idea to curriculum design to production. Some of my most popular courses are 12-15 hours long and I just don't have the time for that these days. That doesn't mean that I couldn't create a smaller course in the range of 2-4 hours. I think If I aligned this with my ideas for an e-book I could use the content across both mediums and package them up.",[636,108166,37673],{"id":33895},[22,108168,108169],{},"I stopped releasing my weekly newsletter this year because, you know 2020. I need to come up with a plan where this is a manageable task that I can stick to each week. The format and cadence is also something I need to examine.",[636,108171,38131],{"id":104906},[22,108173,108174],{},"I miss conferences, a lot. I miss learning and talking to all of you. A goal of mine this is year is to speak at or at the very least attend a conference later this year if everything is safe.",[26,108176,1499],{"id":1498},[22,108178,108179],{},"Leaving your comfort zone is never easy but the biggest gains come from the things that scare you the most. I'm looking forward to some sense of normalcy returning in 2021 and taking this new challenge on. What are you looking to learn in 2021? Do you have any resources or books you want to share? Please share below.",{"title":57,"searchDepth":76,"depth":76,"links":108181},[108182,108183,108184,108191,108200],{"id":107907,"depth":76,"text":107908},{"id":107944,"depth":76,"text":107945},{"id":108001,"depth":76,"text":108002,"children":108185},[108186,108187,108188,108189,108190],{"id":108014,"depth":82,"text":108015},{"id":78619,"depth":82,"text":47077},{"id":108076,"depth":82,"text":108077},{"id":108088,"depth":82,"text":108089},{"id":108095,"depth":82,"text":108096},{"id":33475,"depth":76,"text":108116,"children":108192},[108193,108194,108195,108196,108197,108198,108199],{"id":36995,"depth":82,"text":108136},{"id":108145,"depth":82,"text":108146},{"id":37521,"depth":82,"text":31404},{"id":8585,"depth":82,"text":99601},{"id":57582,"depth":82,"text":53436},{"id":33895,"depth":82,"text":37673},{"id":104906,"depth":82,"text":38131},{"id":1498,"depth":76,"text":1499},{"_id":108202,"path":108203,"title":108204,"description":108205,"meta":108206,"body":108211},"content/blog/2020/12/16/reassign-standard-in-out.md","/blog/2020/12/16/reassign-standard-in-out","How to test standard in and out in Java","In this tutorial I will show you how you can test standard in and out in Java.",{"slug":108207,"date":108208,"published":13,"tags":108209,"author":17,"cover":108210,"excerpt":-1},"reassign-standard-in-out","2020-12-16T15:17:39.619Z",[56],"./testing-standard-in-out-java-cover.png",{"type":19,"value":108212,"toc":109255},[108213,108216,108219,108223,108229,108245,108317,108337,108429,108435,108468,108660,108663,108667,108670,108684,108691,108753,108777,108863,108884,109020,109026,109239,109241,109248,109252],[22,108214,108215],{},"When you have been writing software for as long as I have you see a lot of the same problems over and over and it can be a bit repetitive at times. That is why when I run across a problem I haven't had to solve for before I get pretty excited.",[22,108217,108218],{},"Recently I was working on a project where I had to write unit tests for an exercise where students were learning the basics of File I/O in Java. The basis of the exercise was that they would read some information in from the command line and then write the responses to the console in a particular format. Pretty standard I/O but writing tests for this is not something that I have had to do before.",[26,108220,108222],{"id":108221},"java-standard-in-and-standard-out","Java Standard In and Standard Out",[22,108224,108225,108226,22831],{},"Before we dive into the tests let's take a look at the application. Ask the user for their first name, last name and email address and save each response. All of the work for this application will be done in the ",[59,108227,108228],{},"main()",[22,108230,3521,108231,108234,108235,87561,108238,108241,108242,108244],{},[59,108232,108233],{},"Scanner"," class is used to parse input from a number of different sources, one of which can be an ",[59,108236,108237],{},"Input Stream",[59,108239,108240],{},"System"," class has a static instance variable ",[59,108243,75535],{}," that represents the standard input stream (input from the keyboard).",[52,108246,108248],{"className":54,"code":108247,"language":56,"meta":57,"style":57},"public class Application {\n\n public static void main(String[] args) {\n\n Scanner scanner = new Scanner(System.in);\n\n }\n\n}\n",[59,108249,108250,108260,108264,108284,108288,108301,108305,108309,108313],{"__ignoreMap":57},[62,108251,108252,108254,108256,108258],{"class":64,"line":65},[62,108253,116],{"class":68},[62,108255,119],{"class":68},[62,108257,2088],{"class":122},[62,108259,126],{"class":72},[62,108261,108262],{"class":64,"line":76},[62,108263,79],{"emptyLinePlaceholder":13},[62,108265,108266,108268,108270,108272,108274,108276,108278,108280,108282],{"class":64,"line":82},[62,108267,194],{"class":68},[62,108269,2101],{"class":68},[62,108271,200],{"class":68},[62,108273,2106],{"class":122},[62,108275,2109],{"class":72},[62,108277,973],{"class":68},[62,108279,2114],{"class":72},[62,108281,2117],{"class":889},[62,108283,768],{"class":72},[62,108285,108286],{"class":64,"line":89},[62,108287,79],{"emptyLinePlaceholder":13},[62,108289,108290,108293,108295,108297,108299],{"class":64,"line":95},[62,108291,108292],{"class":72}," Scanner scanner ",[62,108294,146],{"class":68},[62,108296,466],{"class":68},[62,108298,63000],{"class":122},[62,108300,63003],{"class":72},[62,108302,108303],{"class":64,"line":101},[62,108304,79],{"emptyLinePlaceholder":13},[62,108306,108307],{"class":64,"line":107},[62,108308,223],{"class":72},[62,108310,108311],{"class":64,"line":113},[62,108312,79],{"emptyLinePlaceholder":13},[62,108314,108315],{"class":64,"line":129},[62,108316,379],{"class":72},[22,108318,108319,108320,108323,108324,108326,108327,108330,108331,108333,108334,108336],{},"You can ask the user a question to standard out (console) using ",[59,108321,108322],{},"System.out",". Now that you have an instance of a ",[59,108325,108233],{}," there are methods for obtaining user input of different types. The ",[59,108328,108329],{},"nextLine()"," method of the ",[59,108332,108233],{}," class will return a ",[59,108335,973],{},". We can use this same approach for all three required inputs because we expect to get back Strings for all of them.",[52,108338,108340],{"className":54,"code":108339,"language":56,"meta":57,"style":57},"System.out.println(\"What is your first name?\");\nString firstName = scanner.nextLine();\n\nSystem.out.println(\"What is your last name?\");\nString lastName = scanner.nextLine();\n\nSystem.out.println(\"What is your email address?\");\nString email = scanner.nextLine();\n",[59,108341,108342,108355,108369,108373,108386,108399,108403,108416],{"__ignoreMap":57},[62,108343,108344,108346,108348,108350,108353],{"class":64,"line":65},[62,108345,18448],{"class":72},[62,108347,2244],{"class":122},[62,108349,2109],{"class":72},[62,108351,108352],{"class":1675},"\"What is your first name?\"",[62,108354,1133],{"class":72},[62,108356,108357,108360,108362,108365,108367],{"class":64,"line":76},[62,108358,108359],{"class":72},"String firstName ",[62,108361,146],{"class":68},[62,108363,108364],{"class":72}," scanner.",[62,108366,63120],{"class":122},[62,108368,822],{"class":72},[62,108370,108371],{"class":64,"line":82},[62,108372,79],{"emptyLinePlaceholder":13},[62,108374,108375,108377,108379,108381,108384],{"class":64,"line":89},[62,108376,18448],{"class":72},[62,108378,2244],{"class":122},[62,108380,2109],{"class":72},[62,108382,108383],{"class":1675},"\"What is your last name?\"",[62,108385,1133],{"class":72},[62,108387,108388,108391,108393,108395,108397],{"class":64,"line":95},[62,108389,108390],{"class":72},"String lastName ",[62,108392,146],{"class":68},[62,108394,108364],{"class":72},[62,108396,63120],{"class":122},[62,108398,822],{"class":72},[62,108400,108401],{"class":64,"line":101},[62,108402,79],{"emptyLinePlaceholder":13},[62,108404,108405,108407,108409,108411,108414],{"class":64,"line":107},[62,108406,18448],{"class":72},[62,108408,2244],{"class":122},[62,108410,2109],{"class":72},[62,108412,108413],{"class":1675},"\"What is your email address?\"",[62,108415,1133],{"class":72},[62,108417,108418,108421,108423,108425,108427],{"class":64,"line":113},[62,108419,108420],{"class":72},"String email ",[62,108422,146],{"class":68},[62,108424,108364],{"class":72},[62,108426,63120],{"class":122},[62,108428,822],{"class":72},[22,108430,108431,108432,2755],{},"Finally, print the information that was obtained from the user in the format of ",[4534,108433,108434],{},"First Name, Last Name, Email Address",[52,108436,108438],{"className":54,"code":108437,"language":56,"meta":57,"style":57},"System.out.println(firstName + \",\" + lastName + \",\" + email);\n",[59,108439,108440],{"__ignoreMap":57},[62,108441,108442,108444,108446,108449,108451,108454,108456,108459,108461,108463,108465],{"class":64,"line":65},[62,108443,18448],{"class":72},[62,108445,2244],{"class":122},[62,108447,108448],{"class":72},"(firstName ",[62,108450,1148],{"class":68},[62,108452,108453],{"class":1675}," \",\"",[62,108455,4507],{"class":68},[62,108457,108458],{"class":72}," lastName ",[62,108460,1148],{"class":68},[62,108462,108453],{"class":1675},[62,108464,4507],{"class":68},[62,108466,108467],{"class":72}," email);\n",[52,108469,108471],{"className":54,"code":108470,"language":56,"meta":57,"style":57},"public class Application {\n\n public static void main(String[] args) {\n\n Scanner scanner = new Scanner(System.in);\n\n // Ask for user input\n System.out.println(\"What is your first name?\");\n String firstName = scanner.nextLine();\n\n System.out.println(\"What is your last name?\");\n String lastName = scanner.nextLine();\n\n System.out.println(\"What is your email address?\");\n String email = scanner.nextLine();\n\n // print to the console in the format of firstname,lastname,email\n System.out.println(firstName + \",\" + lastName + \",\" + email);\n }\n\n}\n",[59,108472,108473,108483,108487,108507,108511,108523,108527,108532,108544,108557,108561,108573,108586,108590,108602,108615,108619,108624,108648,108652,108656],{"__ignoreMap":57},[62,108474,108475,108477,108479,108481],{"class":64,"line":65},[62,108476,116],{"class":68},[62,108478,119],{"class":68},[62,108480,2088],{"class":122},[62,108482,126],{"class":72},[62,108484,108485],{"class":64,"line":76},[62,108486,79],{"emptyLinePlaceholder":13},[62,108488,108489,108491,108493,108495,108497,108499,108501,108503,108505],{"class":64,"line":82},[62,108490,194],{"class":68},[62,108492,2101],{"class":68},[62,108494,200],{"class":68},[62,108496,2106],{"class":122},[62,108498,2109],{"class":72},[62,108500,973],{"class":68},[62,108502,2114],{"class":72},[62,108504,2117],{"class":889},[62,108506,768],{"class":72},[62,108508,108509],{"class":64,"line":89},[62,108510,79],{"emptyLinePlaceholder":13},[62,108512,108513,108515,108517,108519,108521],{"class":64,"line":95},[62,108514,108292],{"class":72},[62,108516,146],{"class":68},[62,108518,466],{"class":68},[62,108520,63000],{"class":122},[62,108522,63003],{"class":72},[62,108524,108525],{"class":64,"line":101},[62,108526,79],{"emptyLinePlaceholder":13},[62,108528,108529],{"class":64,"line":107},[62,108530,108531],{"class":85}," // Ask for user input\n",[62,108533,108534,108536,108538,108540,108542],{"class":64,"line":113},[62,108535,27297],{"class":72},[62,108537,2244],{"class":122},[62,108539,2109],{"class":72},[62,108541,108352],{"class":1675},[62,108543,1133],{"class":72},[62,108545,108546,108549,108551,108553,108555],{"class":64,"line":129},[62,108547,108548],{"class":72}," String firstName ",[62,108550,146],{"class":68},[62,108552,108364],{"class":72},[62,108554,63120],{"class":122},[62,108556,822],{"class":72},[62,108558,108559],{"class":64,"line":134},[62,108560,79],{"emptyLinePlaceholder":13},[62,108562,108563,108565,108567,108569,108571],{"class":64,"line":156},[62,108564,27297],{"class":72},[62,108566,2244],{"class":122},[62,108568,2109],{"class":72},[62,108570,108383],{"class":1675},[62,108572,1133],{"class":72},[62,108574,108575,108578,108580,108582,108584],{"class":64,"line":161},[62,108576,108577],{"class":72}," String lastName ",[62,108579,146],{"class":68},[62,108581,108364],{"class":72},[62,108583,63120],{"class":122},[62,108585,822],{"class":72},[62,108587,108588],{"class":64,"line":167},[62,108589,79],{"emptyLinePlaceholder":13},[62,108591,108592,108594,108596,108598,108600],{"class":64,"line":173},[62,108593,27297],{"class":72},[62,108595,2244],{"class":122},[62,108597,2109],{"class":72},[62,108599,108413],{"class":1675},[62,108601,1133],{"class":72},[62,108603,108604,108607,108609,108611,108613],{"class":64,"line":179},[62,108605,108606],{"class":72}," String email ",[62,108608,146],{"class":68},[62,108610,108364],{"class":72},[62,108612,63120],{"class":122},[62,108614,822],{"class":72},[62,108616,108617],{"class":64,"line":185},[62,108618,79],{"emptyLinePlaceholder":13},[62,108620,108621],{"class":64,"line":191},[62,108622,108623],{"class":85}," // print to the console in the format of firstname,lastname,email\n",[62,108625,108626,108628,108630,108632,108634,108636,108638,108640,108642,108644,108646],{"class":64,"line":209},[62,108627,27297],{"class":72},[62,108629,2244],{"class":122},[62,108631,108448],{"class":72},[62,108633,1148],{"class":68},[62,108635,108453],{"class":1675},[62,108637,4507],{"class":68},[62,108639,108458],{"class":72},[62,108641,1148],{"class":68},[62,108643,108453],{"class":1675},[62,108645,4507],{"class":68},[62,108647,108467],{"class":72},[62,108649,108650],{"class":64,"line":220},[62,108651,223],{"class":72},[62,108653,108654],{"class":64,"line":226},[62,108655,79],{"emptyLinePlaceholder":13},[62,108657,108658],{"class":64,"line":231},[62,108659,379],{"class":72},[22,108661,108662],{},"As I said earlier, from an application point pretty standard stuff.",[26,108664,108666],{"id":108665},"writing-tests-for-standard-in-and-out","Writing tests for standard in and out",[22,108668,108669],{},"While the application is fairly basic the first thing I had to ask myself was how I am I going to emulate input from the keyboard. How was I going to read from standard out? What happens in the next exercise when I ask them to print the contents of that string to a file?",[22,108671,108672,108673,108675,108676,108683],{},"If you weren't aware you can reassign standard in and standard out and this is something that is fairly common in most programming languages. If you look in the ",[59,108674,108240],{}," class you will find a method ",[677,108677,108680],{"href":108678,"rel":108679},"https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/System.html#setIn(java.io.InputStream)",[681],[59,108681,108682],{},"public static void setIn(InputStream in)"," which will allow you to set the new standard input stream.",[22,108685,108686,108687,108690],{},"I start off by creating a test where when I provide valid user input and if everything goes well I should see the input in the correct format sent to standard out. I'll start by creating a string with responses to the three questions. Make sure to use ",[59,108688,108689],{},"System.lineSeparator()"," and not to hard cord a new line feed.",[52,108692,108694],{"className":54,"code":108693,"language":56,"meta":57,"style":57},"@Test\npublic void validUserInput_ShouldResultInExpectedOutput() {\n String userInput = String.format(\"Dan%sVega%sdanvega@gmail.com\",\n System.lineSeparator(),\n System.lineSeparator());\n}\n",[59,108695,108696,108702,108713,108731,108741,108749],{"__ignoreMap":57},[62,108697,108698,108700],{"class":64,"line":65},[62,108699,942],{"class":72},[62,108701,11705],{"class":68},[62,108703,108704,108706,108708,108711],{"class":64,"line":76},[62,108705,116],{"class":68},[62,108707,200],{"class":68},[62,108709,108710],{"class":122}," validUserInput_ShouldResultInExpectedOutput",[62,108712,206],{"class":72},[62,108714,108715,108718,108720,108722,108724,108726,108729],{"class":64,"line":82},[62,108716,108717],{"class":72}," String userInput ",[62,108719,146],{"class":68},[62,108721,41152],{"class":72},[62,108723,61567],{"class":122},[62,108725,2109],{"class":72},[62,108727,108728],{"class":1675},"\"Dan%sVega%sdanvega@gmail.com\"",[62,108730,3338],{"class":72},[62,108732,108733,108736,108739],{"class":64,"line":89},[62,108734,108735],{"class":72}," System.",[62,108737,108738],{"class":122},"lineSeparator",[62,108740,4651],{"class":72},[62,108742,108743,108745,108747],{"class":64,"line":95},[62,108744,108735],{"class":72},[62,108746,108738],{"class":122},[62,108748,1091],{"class":72},[62,108750,108751],{"class":64,"line":101},[62,108752,379],{"class":72},[22,108754,108755,108756,108759,108760,108763,108764,108766,108767,108770,108771,108773,108774,2755],{},"Next you need to create something that is of type ",[59,108757,108758],{},"InputStream"," so you can reassign standard in. There is a class ",[59,108761,108762],{},"ByteArrayInputStream"," that will work and one of the available constructors will take a byte array. You can use the ",[59,108765,973],{}," class's ",[59,108768,108769],{},"getBytes()"," method to turn the ",[59,108772,973],{}," into a ",[59,108775,108776],{},"byte[]",[52,108778,108780],{"className":54,"code":108779,"language":56,"meta":57,"style":57},"@Test\npublic void validUserInput_ShouldResultInExpectedOutput() {\n String userInput = String.format(\"Dan%sVega%sdanvega@gmail.com\",\n System.lineSeparator(),\n System.lineSeparator());\n ByteArrayInputStream bais = new ByteArrayInputStream(userInput.getBytes());\n System.setIn(bais);\n}\n",[59,108781,108782,108788,108798,108814,108822,108830,108848,108859],{"__ignoreMap":57},[62,108783,108784,108786],{"class":64,"line":65},[62,108785,942],{"class":72},[62,108787,11705],{"class":68},[62,108789,108790,108792,108794,108796],{"class":64,"line":76},[62,108791,116],{"class":68},[62,108793,200],{"class":68},[62,108795,108710],{"class":122},[62,108797,206],{"class":72},[62,108799,108800,108802,108804,108806,108808,108810,108812],{"class":64,"line":82},[62,108801,108717],{"class":72},[62,108803,146],{"class":68},[62,108805,41152],{"class":72},[62,108807,61567],{"class":122},[62,108809,2109],{"class":72},[62,108811,108728],{"class":1675},[62,108813,3338],{"class":72},[62,108815,108816,108818,108820],{"class":64,"line":89},[62,108817,108735],{"class":72},[62,108819,108738],{"class":122},[62,108821,4651],{"class":72},[62,108823,108824,108826,108828],{"class":64,"line":95},[62,108825,108735],{"class":72},[62,108827,108738],{"class":122},[62,108829,1091],{"class":72},[62,108831,108832,108835,108837,108839,108841,108844,108846],{"class":64,"line":101},[62,108833,108834],{"class":72}," ByteArrayInputStream bais ",[62,108836,146],{"class":68},[62,108838,466],{"class":68},[62,108840,14010],{"class":122},[62,108842,108843],{"class":72},"(userInput.",[62,108845,14393],{"class":122},[62,108847,1091],{"class":72},[62,108849,108850,108853,108856],{"class":64,"line":107},[62,108851,108852],{"class":72}," System.",[62,108854,108855],{"class":122},"setIn",[62,108857,108858],{"class":72},"(bais);\n",[62,108860,108861],{"class":64,"line":113},[62,108862,379],{"class":72},[22,108864,108865,108866,108868,108869,108876,108877,108880,108881,2755],{},"With standard in reassigned you will need to the same for standard out. Create a string that represents what the expected result is. The ",[59,108867,108240],{}," class has a method ",[677,108870,108873],{"href":108871,"rel":108872},"https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/System.html#setOut(java.io.PrintStream)",[681],[59,108874,108875],{},"public static void setOut(PrintStream out)"," that will allow you to reassign the standard output stream. You can create an instance of ",[59,108878,108879],{},"PrintStream"," using a ",[59,108882,108883],{},"ByteArrayOutputStream",[52,108885,108887],{"className":54,"code":108886,"language":56,"meta":57,"style":57},"@Test\npublic void validUserInput_ShouldResultInExpectedOutput() {\n String userInput = String.format(\"Dan%sVega%sdanvega@gmail.com\",\n System.lineSeparator(),\n System.lineSeparator());\n ByteArrayInputStream bais = new ByteArrayInputStream(userInput.getBytes());\n System.setIn(bais);\n\n String expected = \"Dan,Vega,danvega@gmail.com\";\n ByteArrayOutputStream baos = new ByteArrayOutputStream();\n PrintStream printStream = new PrintStream(baos);\n System.setOut(printStream);\n}\n",[59,108888,108889,108895,108905,108921,108929,108937,108953,108961,108965,108977,108991,109006,109016],{"__ignoreMap":57},[62,108890,108891,108893],{"class":64,"line":65},[62,108892,942],{"class":72},[62,108894,11705],{"class":68},[62,108896,108897,108899,108901,108903],{"class":64,"line":76},[62,108898,116],{"class":68},[62,108900,200],{"class":68},[62,108902,108710],{"class":122},[62,108904,206],{"class":72},[62,108906,108907,108909,108911,108913,108915,108917,108919],{"class":64,"line":82},[62,108908,108717],{"class":72},[62,108910,146],{"class":68},[62,108912,41152],{"class":72},[62,108914,61567],{"class":122},[62,108916,2109],{"class":72},[62,108918,108728],{"class":1675},[62,108920,3338],{"class":72},[62,108922,108923,108925,108927],{"class":64,"line":89},[62,108924,108735],{"class":72},[62,108926,108738],{"class":122},[62,108928,4651],{"class":72},[62,108930,108931,108933,108935],{"class":64,"line":95},[62,108932,108735],{"class":72},[62,108934,108738],{"class":122},[62,108936,1091],{"class":72},[62,108938,108939,108941,108943,108945,108947,108949,108951],{"class":64,"line":101},[62,108940,108834],{"class":72},[62,108942,146],{"class":68},[62,108944,466],{"class":68},[62,108946,14010],{"class":122},[62,108948,108843],{"class":72},[62,108950,14393],{"class":122},[62,108952,1091],{"class":72},[62,108954,108955,108957,108959],{"class":64,"line":107},[62,108956,108852],{"class":72},[62,108958,108855],{"class":122},[62,108960,108858],{"class":72},[62,108962,108963],{"class":64,"line":113},[62,108964,79],{"emptyLinePlaceholder":13},[62,108966,108967,108970,108972,108975],{"class":64,"line":129},[62,108968,108969],{"class":72}," String expected ",[62,108971,146],{"class":68},[62,108973,108974],{"class":1675}," \"Dan,Vega,danvega@gmail.com\"",[62,108976,153],{"class":72},[62,108978,108979,108982,108984,108986,108989],{"class":64,"line":134},[62,108980,108981],{"class":72}," ByteArrayOutputStream baos ",[62,108983,146],{"class":68},[62,108985,466],{"class":68},[62,108987,108988],{"class":122}," ByteArrayOutputStream",[62,108990,822],{"class":72},[62,108992,108993,108996,108998,109000,109003],{"class":64,"line":156},[62,108994,108995],{"class":72}," PrintStream printStream ",[62,108997,146],{"class":68},[62,108999,466],{"class":68},[62,109001,109002],{"class":122}," PrintStream",[62,109004,109005],{"class":72},"(baos);\n",[62,109007,109008,109010,109013],{"class":64,"line":161},[62,109009,108852],{"class":72},[62,109011,109012],{"class":122},"setOut",[62,109014,109015],{"class":72},"(printStream);\n",[62,109017,109018],{"class":64,"line":167},[62,109019,379],{"class":72},[22,109021,109022,109023,109025],{},"Next you can call the applications main method. If the student completed the exercise they would have printed out a few things to standard out. The last one would have been the users first name, last name, email address. You can use the ",[59,109024,108883],{}," to return an array of strings which will be each of the lines printed out to the console with the last one being the one you are interested in. Finally, your assertion can test if the expected output matches the actual output.",[52,109027,109029],{"className":54,"code":109028,"language":56,"meta":57,"style":57},"@Test\npublic void validUserInput_ShouldResultInExpectedOutput() {\n String userInput = String.format(\"Dan%sVega%sdanvega@gmail.com\",\n System.lineSeparator(),\n System.lineSeparator());\n ByteArrayInputStream bais = new ByteArrayInputStream(userInput.getBytes());\n System.setIn(bais);\n\n String expected = \"Dan,Vega,danvega@gmail.com\";\n ByteArrayOutputStream baos = new ByteArrayOutputStream();\n PrintStream printStream = new PrintStream(baos);\n System.setOut(printStream);\n\n Application.main(null); // call the main method\n\n String[] lines = baos.toString().split(System.lineSeparator());\n String actual = lines[lines.length-1];\n\n // checkout output\n Assert.assertEquals(expected,actual);\n}\n",[59,109030,109031,109037,109047,109063,109071,109079,109095,109103,109107,109117,109129,109141,109149,109153,109170,109174,109199,109216,109220,109225,109235],{"__ignoreMap":57},[62,109032,109033,109035],{"class":64,"line":65},[62,109034,942],{"class":72},[62,109036,11705],{"class":68},[62,109038,109039,109041,109043,109045],{"class":64,"line":76},[62,109040,116],{"class":68},[62,109042,200],{"class":68},[62,109044,108710],{"class":122},[62,109046,206],{"class":72},[62,109048,109049,109051,109053,109055,109057,109059,109061],{"class":64,"line":82},[62,109050,108717],{"class":72},[62,109052,146],{"class":68},[62,109054,41152],{"class":72},[62,109056,61567],{"class":122},[62,109058,2109],{"class":72},[62,109060,108728],{"class":1675},[62,109062,3338],{"class":72},[62,109064,109065,109067,109069],{"class":64,"line":89},[62,109066,108735],{"class":72},[62,109068,108738],{"class":122},[62,109070,4651],{"class":72},[62,109072,109073,109075,109077],{"class":64,"line":95},[62,109074,108735],{"class":72},[62,109076,108738],{"class":122},[62,109078,1091],{"class":72},[62,109080,109081,109083,109085,109087,109089,109091,109093],{"class":64,"line":101},[62,109082,108834],{"class":72},[62,109084,146],{"class":68},[62,109086,466],{"class":68},[62,109088,14010],{"class":122},[62,109090,108843],{"class":72},[62,109092,14393],{"class":122},[62,109094,1091],{"class":72},[62,109096,109097,109099,109101],{"class":64,"line":107},[62,109098,108852],{"class":72},[62,109100,108855],{"class":122},[62,109102,108858],{"class":72},[62,109104,109105],{"class":64,"line":113},[62,109106,79],{"emptyLinePlaceholder":13},[62,109108,109109,109111,109113,109115],{"class":64,"line":129},[62,109110,108969],{"class":72},[62,109112,146],{"class":68},[62,109114,108974],{"class":1675},[62,109116,153],{"class":72},[62,109118,109119,109121,109123,109125,109127],{"class":64,"line":134},[62,109120,108981],{"class":72},[62,109122,146],{"class":68},[62,109124,466],{"class":68},[62,109126,108988],{"class":122},[62,109128,822],{"class":72},[62,109130,109131,109133,109135,109137,109139],{"class":64,"line":156},[62,109132,108995],{"class":72},[62,109134,146],{"class":68},[62,109136,466],{"class":68},[62,109138,109002],{"class":122},[62,109140,109005],{"class":72},[62,109142,109143,109145,109147],{"class":64,"line":161},[62,109144,108852],{"class":72},[62,109146,109012],{"class":122},[62,109148,109015],{"class":72},[62,109150,109151],{"class":64,"line":167},[62,109152,79],{"emptyLinePlaceholder":13},[62,109154,109155,109158,109160,109162,109164,109167],{"class":64,"line":173},[62,109156,109157],{"class":72}," Application.",[62,109159,24813],{"class":122},[62,109161,2109],{"class":72},[62,109163,3256],{"class":149},[62,109165,109166],{"class":72},"); ",[62,109168,109169],{"class":85},"// call the main method\n",[62,109171,109172],{"class":64,"line":179},[62,109173,79],{"emptyLinePlaceholder":13},[62,109175,109176,109178,109181,109183,109186,109188,109190,109192,109195,109197],{"class":64,"line":185},[62,109177,73404],{"class":68},[62,109179,109180],{"class":72},"[] lines ",[62,109182,146],{"class":68},[62,109184,109185],{"class":72}," baos.",[62,109187,23175],{"class":122},[62,109189,3229],{"class":72},[62,109191,6894],{"class":122},[62,109193,109194],{"class":72},"(System.",[62,109196,108738],{"class":122},[62,109198,1091],{"class":72},[62,109200,109201,109204,109206,109209,109211,109213],{"class":64,"line":191},[62,109202,109203],{"class":72}," String actual ",[62,109205,146],{"class":68},[62,109207,109208],{"class":72}," lines[lines.length",[62,109210,11635],{"class":68},[62,109212,6689],{"class":149},[62,109214,109215],{"class":72},"];\n",[62,109217,109218],{"class":64,"line":209},[62,109219,79],{"emptyLinePlaceholder":13},[62,109221,109222],{"class":64,"line":220},[62,109223,109224],{"class":85}," // checkout output\n",[62,109226,109227,109229,109232],{"class":64,"line":226},[62,109228,14999],{"class":72},[62,109230,109231],{"class":122},"assertEquals",[62,109233,109234],{"class":72},"(expected,actual);\n",[62,109236,109237],{"class":64,"line":231},[62,109238,379],{"class":72},[26,109240,1499],{"id":1498},[22,109242,109243,109244],{},"If you already knew this existed this article was pointless and chances are you aren't reading this. For the rest of you I hope you found this as cool as I did. If you want the code for this article you can find it over on ",[677,109245,50830],{"href":109246,"rel":109247},"https://github.com/danvega/sys-in-out-tests",[681],[22,109249,36004,109250,82545],{},[36006,109251],{},[1527,109253,109254],{},"html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .spoOn, html code.shiki .spoOn{--shiki-default:#E36209;--shiki-dark:#FFAB70;--shiki-sepia:#E36209}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}html pre.shiki code .s4-Ip, html code.shiki .s4-Ip{--shiki-default:#6A737D;--shiki-dark:#6A737D;--shiki-sepia:#6A737D}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}",{"title":57,"searchDepth":76,"depth":76,"links":109256},[109257,109258,109259],{"id":108221,"depth":76,"text":108222},{"id":108665,"depth":76,"text":108666},{"id":1498,"depth":76,"text":1499},{"_id":109261,"path":109262,"title":109263,"description":109264,"meta":109265,"body":109271},"content/blog/2020/07/30/interactive-learning-from-oreilly.md","/blog/2020/07/30/interactive-learning-from-oreilly","Interactive Learning from Oreilly","O'Reilly has a new hands-on learning platform called Interactive Learning. In this tutorial I will tell you all about it and the tutorials I created for it.",{"slug":109266,"date":109267,"published":13,"tags":109268,"author":17,"cover":109270,"excerpt":-1},"interactive-learning-from-oreilly","2020-07-30T12:50:24.664Z",[56,11002,109269],"maven","./oreilly-interactive-learning-cover.png",{"type":19,"value":109272,"toc":109468},[109273,109288,109292,109295,109298,109301,109307,109315,109319,109322,109325,109329,109332,109369,109373,109376,109413,109417,109420,109457,109459],[22,109274,109275,109276,109281,109282,109287],{},"I'm happy to announce that a few projects I have been working on with ",[677,109277,109280],{"href":109278,"rel":109279},"https://learning.oreilly.com/",[681],"O'Reilly Media"," are now live.\nFirst off I want to thank everyone at O'Reilly for all of their help launching these projects. For me, O'Reilly has long\nbeen the gold standard for technical training materials. Their physical books line my bookshelf and I also have a\nsubscription to the ",[677,109283,109286],{"href":109284,"rel":109285},"https://learning.oreilly.com",[681],"O'Reilly Learning Platform"," which I highly recommend. When I got\napproached to collaborate with them on a new type of learning platform I was really excited.",[26,109289,109291],{"id":109290},"interactive-learning-powered-by-katacoda","Interactive Learning powered by Katacoda",[22,109293,109294],{},"Before I dive into the specific projects that I worked on it might help to have a little context on what the interactive learning\nplatform is. We all know that you can buy all the books or watch all the videos you want but if you want to truly learn\nsomething you have to learn by doing.",[22,109296,109297],{},"Interactive Learning is a short, 5-15 minutes hands-on learning experience. There is no development environment to set up-just write\nand run code as you learn, right in your browser. Each interactive learning tutorial is called a scenario and you can have\nthree to five scenarios grouped together that will make up a set.",[22,109299,109300],{},"Below is an example from one of the scenarios I built for getting started with Maven. Each step has a tutorial, a code\neditor and a terminal to run commands. This layout is customizable based on what you are teaching but this made sense\nfor this particular scenario.",[22,109302,109303],{},[653,109304],{"alt":109305,"src":109306},"Tutorial / Editor / Terminal","/images/blog/2020/07/30/tutorial_editor_terminal.png",[22,109308,109309,109310,109314],{},"If you want to learn more about Interactive Learning or browse there hundreds of scenarios head over to ",[677,109311,109312],{"href":109312,"rel":109313},"https://learning.oreilly.com/interactive",[681],".\nYou will need an O'Reilly account to access these but as someone who had an account before working on these I can tell\nyou that it is well worth the money.",[26,109316,109318],{"id":109317},"my-interactive-learning-sets","My Interactive Learning sets",[22,109320,109321],{},"Now that you know a little more about what Interactive Learning scenarios are I can tell you about the sets that I put together.\nI think my biggest challenge creating these was that it was a new system to me with its own rules and boundaries. The first\nset I put together took a long time but soon after getting used to things I was able to get into a grove.",[22,109323,109324],{},"Below are each of the sets that I put together, links to each of the scenarios and a description of the set.",[636,109326,109328],{"id":109327},"spring-boot-creating-your-first-rest-api","Spring Boot: Creating your first REST API",[22,109330,109331],{},"In this set you will how to create a new Spring Boot project that will be a REST API. If you are new to Spring Boot\nand creating REST APIs this is a perfect place to get started.",[915,109333,109334,109341,109348,109355,109362],{},[37,109335,109336],{},[677,109337,109340],{"href":109338,"rel":109339},"https://learning.oreilly.com/scenarios/spring-boot-create/9781492088547/",[681],"Spring Boot: Create a New Project",[37,109342,109343],{},[677,109344,109347],{"href":109345,"rel":109346},"https://learning.oreilly.com/scenarios/spring-boot-create/9781492088554/",[681],"Spring Boot: Create a REST Controller",[37,109349,109350],{},[677,109351,109354],{"href":109352,"rel":109353},"https://learning.oreilly.com/scenarios/spring-boot-dependency/9781492088561/",[681],"Spring Boot: Dependency Injection",[37,109356,109357],{},[677,109358,109361],{"href":109359,"rel":109360},"https://learning.oreilly.com/scenarios/spring-boot-handling/9781492088578/",[681],"Spring Boot: Handling JSON",[37,109363,109364],{},[677,109365,109368],{"href":109366,"rel":109367},"https://learning.oreilly.com/scenarios/spring-boot-handling/9781492088585/",[681],"Spring Boot: Handling Errors",[636,109370,109372],{"id":109371},"spring-boot-connecting-to-a-database","Spring Boot: Connecting to a database",[22,109374,109375],{},"In this set you will learn how to connect, populate, access and manipulate date from a database in a Spring Boot application.\nThis is perfect for anyone who is just getting started with Spring Boot and want to learn how to interact with a database.",[915,109377,109378,109385,109392,109399,109406],{},[37,109379,109380],{},[677,109381,109384],{"href":109382,"rel":109383},"https://learning.oreilly.com/scenarios/spring-boot-connect/9781492088608/",[681],"Spring Boot: Connect to a Database",[37,109386,109387],{},[677,109388,109391],{"href":109389,"rel":109390},"https://learning.oreilly.com/scenarios/spring-boot-populate/9781492088615/",[681],"Spring Boot: Populate a Database",[37,109393,109394],{},[677,109395,109398],{"href":109396,"rel":109397},"https://learning.oreilly.com/scenarios/spring-boot-accessing/9781492088622/",[681],"Spring Boot: Accessing Data with JdbcTemplate",[37,109400,109401],{},[677,109402,109405],{"href":109403,"rel":109404},"https://learning.oreilly.com/scenarios/spring-boot-manipulating/9781492088639/",[681],"Spring Boot: Manipulating Data with JdbcTemplate",[37,109407,109408],{},[677,109409,109412],{"href":109410,"rel":109411},"https://learning.oreilly.com/scenarios/spring-boot-using/9781492088646/",[681],"Spring Boot: Using the Command Line Runner Interface",[636,109414,109416],{"id":109415},"maven-first-steps","Maven: First Steps",[22,109418,109419],{},"In this set you will learn what Maven is and how to use it in Java projects. Maven is a tool for building and managing your Java-based projects\nand is the most used tool among Java developers. This set is perfect for anyone who wants to understand how to use Maven.",[915,109421,109422,109429,109436,109443,109450],{},[37,109423,109424],{},[677,109425,109428],{"href":109426,"rel":109427},"https://learning.oreilly.com/scenarios/maven-install-maven/9781492088660/",[681],"Maven: Install Maven and Create Your First Application",[37,109430,109431],{},[677,109432,109435],{"href":109433,"rel":109434},"https://learning.oreilly.com/scenarios/maven-compiling-packaging/9781492088677/",[681],"Maven: Compiling, Packaging, and Running Your Application",[37,109437,109438],{},[677,109439,109442],{"href":109440,"rel":109441},"https://learning.oreilly.com/scenarios/maven-dependency-management/9781492088684/",[681],"Maven: Dependency Management",[37,109444,109445],{},[677,109446,109449],{"href":109447,"rel":109448},"https://learning.oreilly.com/scenarios/maven-using-maven/9781492088691/",[681],"Maven: Using Maven Plugins",[37,109451,109452],{},[677,109453,109456],{"href":109454,"rel":109455},"https://learning.oreilly.com/scenarios/maven-using-maven/9781492088707/",[681],"Maven: Using Maven Archetypes",[26,109458,1499],{"id":1498},[22,109460,109461,109462,109467],{},"I had a lot of fun putting these together and now that I know what I am doing and how to approach building out each scenario\nI am excited to put together more of them. I have put together some suggestions for new scenarios and I will let all of you\nknow if I build any more. Thanks again to O'Reilly for giving me the opportunity to work on these. Having an ",[677,109463,109466],{"href":109464,"rel":109465},"https://learning.oreilly.com/search/?query=author%3A%22Dan%20Vega%22&extended_publisher_data=true&highlight=true&include_assessments=false&include_case_studies=true&include_courses=true&include_orioles=true&include_playlists=true&include_collections=true&include_notebooks=true&is_academic_institution_account=false&source=suggestion&sort=relevance&facet_json=true&page=0&include_scenarios=true&include_sandboxes=true",[681],"author page","\non their platform with some of my work is a dream come true.",{"title":57,"searchDepth":76,"depth":76,"links":109469},[109470,109471,109476],{"id":109290,"depth":76,"text":109291},{"id":109317,"depth":76,"text":109318,"children":109472},[109473,109474,109475],{"id":109327,"depth":82,"text":109328},{"id":109371,"depth":82,"text":109372},{"id":109415,"depth":82,"text":109416},{"id":1498,"depth":76,"text":1499},{"_id":109478,"path":109479,"title":109480,"description":109481,"meta":109482,"body":109487},"content/blog/2020/06/04/spring-boot-rest-service.md","/blog/2020/06/04/spring-boot-rest-service","Spring Boot REST Service: How to build a REST API in Java","In this tutorial you will learn how to build a REST API in Java using Spring Boot.",{"slug":109483,"date":109484,"published":13,"tags":109485,"author":17,"cover":109486,"excerpt":-1},"spring-boot-rest-service","2020-06-04T08:00:00.000Z",[2925],"./spring-boot-rest-service.jpeg",{"type":19,"value":109488,"toc":110067},[109489,109493,109502,109506,109509,109517,109520,109524,109527,109530,109566,109571,109574,109578,109581,109588,109613,109755,109761,109765,109771,109776,109825,109972,109978,109982,109996,110010,110014,110017,110020,110040,110046,110048,110051,110059,110062,110064],[26,109490,109492],{"id":109491},"a-comprehensive-guide-to-building-restful-web-service-with-spring-boot","A Comprehensive Guide to Building RESTful Web Service with Spring Boot",[22,109494,109495,109496,109501],{},"Today, we'll be going over a tutorial on how to build a RESTful web service using Spring Boot. This is perfect for visual learners who may prefer to start with a tutorial before diving into the actual ",[677,109497,109500],{"href":109498,"rel":109499},"https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/",[681],"Spring.io documentation",". So, let's dive into the tutorial and start building our web service.",[26,109503,109505],{"id":109504},"building-a-restful-web-service-prerequisites","Building a RESTful Web Service: Prerequisites",[22,109507,109508],{},"Before we get started, let's make sure you have the following tools installed and ready to go:",[915,109510,109511,109514],{},[37,109512,109513],{},"A favorite text editor or IDE (we'll be using IntelliJ Ultimate Edition)",[37,109515,109516],{},"JDK 1.8 or later",[22,109518,109519],{},"With those in place, we're ready to get started on building our web service!",[26,109521,109523],{"id":109522},"step-1-setting-up-the-project","Step 1: Setting Up the Project",[22,109525,109526],{},"To ensure a smooth start, we'll begin by creating our project using the Spring Initializer. This will automatically configure our build system, dependencies, and folder structure for us.",[22,109528,109529],{},"Here's what to do:",[34,109531,109532,109537,109545,109555,109563],{},[37,109533,23045,109534],{},[677,109535,2903],{"href":2901,"rel":109536},[681],[37,109538,109539,109540,976,109542,109544],{},"Select ",[59,109541,94504],{},[59,109543,15],{},", and choose the latest stable version of Spring Boot",[37,109546,109547,109548,109551,109552],{},"Set the Group to ",[59,109549,109550],{},"com.example"," and Artifact to ",[59,109553,109554],{},"rest-service",[37,109556,81336,109557,109559,109560],{},[59,109558,78714],{}," dependency and click ",[59,109561,109562],{},"Generate",[37,109564,109565],{},"Download and unzip the generated project",[22,109567,109568],{},[653,109569],{"alt":24606,"src":109570},"/images/blog/2020/06/04/start-spring-io.png",[22,109572,109573],{},"Once that's done, open the project in your favorite IDE, and you're ready for the next step.",[26,109575,109577],{"id":109576},"step-2-creating-a-model","Step 2: Creating a Model",[22,109579,109580],{},"With our project structure in place, we need to create a model class to describe the data we'll be working with. In this case, it will be a simple greeting with an ID and some content.",[22,109582,109583,109584,109587],{},"To create a ",[59,109585,109586],{},"Greeting"," class with the appropriate fields, follow these steps:",[34,109589,109590,109596,109601,109610],{},[37,109591,109592,109593,109595],{},"Navigate to the ",[59,109594,24813],{}," package in your project",[37,109597,109598,109599],{},"Create a new class named ",[59,109600,109586],{},[37,109602,22815,109603,19931,109606,109609],{},[59,109604,109605],{},"private final long id",[59,109607,109608],{},"private final String content"," as fields",[37,109611,109612],{},"Generate a constructor and getters for the fields",[52,109614,109616],{"className":54,"code":109615,"language":56,"meta":57,"style":57},"public class Greeting {\n\n private final long id;\n private final String content;\n\n public Greeting(long id, String content) {\n this.id = id;\n this.content = content;\n }\n\n public long getId() {\n return id;\n }\n\n public String getContent() {\n return content;\n }\n\n}\n",[59,109617,109618,109629,109633,109644,109652,109656,109674,109684,109694,109698,109702,109713,109719,109723,109727,109737,109743,109747,109751],{"__ignoreMap":57},[62,109619,109620,109622,109624,109627],{"class":64,"line":65},[62,109621,116],{"class":68},[62,109623,119],{"class":68},[62,109625,109626],{"class":122}," Greeting",[62,109628,126],{"class":72},[62,109630,109631],{"class":64,"line":76},[62,109632,79],{"emptyLinePlaceholder":13},[62,109634,109635,109637,109639,109642],{"class":64,"line":82},[62,109636,42793],{"class":68},[62,109638,458],{"class":68},[62,109640,109641],{"class":68}," long",[62,109643,9954],{"class":72},[62,109645,109646,109648,109650],{"class":64,"line":89},[62,109647,42793],{"class":68},[62,109649,458],{"class":68},[62,109651,93711],{"class":72},[62,109653,109654],{"class":64,"line":95},[62,109655,79],{"emptyLinePlaceholder":13},[62,109657,109658,109660,109662,109664,109666,109668,109670,109672],{"class":64,"line":101},[62,109659,26643],{"class":68},[62,109661,109626],{"class":122},[62,109663,2109],{"class":72},[62,109665,27552],{"class":68},[62,109667,20831],{"class":889},[62,109669,8624],{"class":72},[62,109671,2230],{"class":889},[62,109673,768],{"class":72},[62,109675,109676,109678,109680,109682],{"class":64,"line":107},[62,109677,39124],{"class":149},[62,109679,9802],{"class":72},[62,109681,146],{"class":68},[62,109683,9954],{"class":72},[62,109685,109686,109688,109690,109692],{"class":64,"line":113},[62,109687,39124],{"class":149},[62,109689,93788],{"class":72},[62,109691,146],{"class":68},[62,109693,14207],{"class":72},[62,109695,109696],{"class":64,"line":129},[62,109697,3731],{"class":72},[62,109699,109700],{"class":64,"line":134},[62,109701,79],{"emptyLinePlaceholder":13},[62,109703,109704,109706,109708,109711],{"class":64,"line":156},[62,109705,26643],{"class":68},[62,109707,109641],{"class":68},[62,109709,109710],{"class":122}," getId",[62,109712,206],{"class":72},[62,109714,109715,109717],{"class":64,"line":161},[62,109716,2599],{"class":68},[62,109718,9954],{"class":72},[62,109720,109721],{"class":64,"line":167},[62,109722,3731],{"class":72},[62,109724,109725],{"class":64,"line":173},[62,109726,79],{"emptyLinePlaceholder":13},[62,109728,109729,109731,109733,109735],{"class":64,"line":179},[62,109730,26643],{"class":68},[62,109732,2469],{"class":72},[62,109734,12317],{"class":122},[62,109736,206],{"class":72},[62,109738,109739,109741],{"class":64,"line":185},[62,109740,2599],{"class":68},[62,109742,14207],{"class":72},[62,109744,109745],{"class":64,"line":191},[62,109746,3731],{"class":72},[62,109748,109749],{"class":64,"line":209},[62,109750,79],{"emptyLinePlaceholder":13},[62,109752,109753],{"class":64,"line":220},[62,109754,379],{"class":72},[22,109756,109757,109758,109760],{},"And that's it for our ",[59,109759,109586],{}," class! Now, let's move on to creating the controller that will define the behavior of our web service.",[26,109762,109764],{"id":109763},"step-3-creating-a-greeting-controller","Step 3: Creating a Greeting Controller",[22,109766,109767,109768,109770],{},"In this step, we'll create our ",[59,109769,103735],{}," class, which will handle the interaction between our web service and its users.",[22,109772,109773,109774,1266],{},"Here's how to set up the ",[59,109775,103735],{},[34,109777,109778,109782,109786,109791,109800,109808],{},[37,109779,109592,109780,109595],{},[59,109781,24813],{},[37,109783,109598,109784],{},[59,109785,103735],{},[37,109787,81336,109788,109790],{},[59,109789,40266],{}," annotation at the beginning of the class",[37,109792,15404,109793,109796,109797],{},[59,109794,109795],{},"private static final String template"," with an initial value of ",[59,109798,109799],{},"Hello, %s!",[37,109801,15404,109802,109796,109805],{},[59,109803,109804],{},"private final AtomicLong counter",[59,109806,109807],{},"new AtomicLong()",[37,109809,109810,109811,109814,109815,109818,109819,109821,109822],{},"Add a ",[59,109812,109813],{},"greeting"," method with a ",[59,109816,109817],{},"@GetMapping(\"/greeting\")"," annotation and a single ",[59,109820,973],{}," parameter annotated with ",[59,109823,109824],{},"@RequestParam(value = \"name\", defaultValue = \"World\")",[52,109826,109828],{"className":54,"code":109827,"language":56,"meta":57,"style":57},"@RestController\npublic class GreetingController {\n\n private static final String template = \"Hello, %s!\";\n private final AtomicLong counter = new AtomicLong();\n\n @GetMapping(\"/greeting\")\n public Greeting greeting(@RequestParam(value = \"name\", defaultValue = \"World\") String name) {\n return new Greeting(counter.incrementAndGet(), String.format(template, name));\n }\n\n}\n\n",[59,109829,109830,109836,109846,109850,109868,109886,109890,109903,109939,109960,109964,109968],{"__ignoreMap":57},[62,109831,109832,109834],{"class":64,"line":65},[62,109833,942],{"class":72},[62,109835,2342],{"class":68},[62,109837,109838,109840,109842,109844],{"class":64,"line":76},[62,109839,116],{"class":68},[62,109841,119],{"class":68},[62,109843,103781],{"class":122},[62,109845,126],{"class":72},[62,109847,109848],{"class":64,"line":82},[62,109849,79],{"emptyLinePlaceholder":13},[62,109851,109852,109854,109856,109858,109861,109863,109866],{"class":64,"line":89},[62,109853,137],{"class":68},[62,109855,2101],{"class":68},[62,109857,458],{"class":68},[62,109859,109860],{"class":72}," String template ",[62,109862,146],{"class":68},[62,109864,109865],{"class":1675}," \"Hello, %s!\"",[62,109867,153],{"class":72},[62,109869,109870,109872,109874,109877,109879,109881,109884],{"class":64,"line":95},[62,109871,137],{"class":68},[62,109873,458],{"class":68},[62,109875,109876],{"class":72}," AtomicLong counter ",[62,109878,146],{"class":68},[62,109880,466],{"class":68},[62,109882,109883],{"class":122}," AtomicLong",[62,109885,822],{"class":72},[62,109887,109888],{"class":64,"line":101},[62,109889,79],{"emptyLinePlaceholder":13},[62,109891,109892,109894,109896,109898,109901],{"class":64,"line":107},[62,109893,2143],{"class":72},[62,109895,2548],{"class":68},[62,109897,2109],{"class":72},[62,109899,109900],{"class":1675},"\"/greeting\"",[62,109902,2212],{"class":72},[62,109904,109905,109907,109910,109912,109914,109916,109918,109920,109922,109924,109926,109928,109930,109933,109935,109937],{"class":64,"line":113},[62,109906,194],{"class":68},[62,109908,109909],{"class":72}," Greeting ",[62,109911,109813],{"class":122},[62,109913,2475],{"class":72},[62,109915,2591],{"class":68},[62,109917,2109],{"class":72},[62,109919,2553],{"class":149},[62,109921,2556],{"class":68},[62,109923,86744],{"class":1675},[62,109925,976],{"class":72},[62,109927,17908],{"class":149},[62,109929,2556],{"class":68},[62,109931,109932],{"class":1675}," \"World\"",[62,109934,17916],{"class":72},[62,109936,3107],{"class":889},[62,109938,768],{"class":72},[62,109940,109941,109943,109945,109947,109950,109952,109955,109957],{"class":64,"line":129},[62,109942,360],{"class":68},[62,109944,466],{"class":68},[62,109946,109626],{"class":122},[62,109948,109949],{"class":72},"(counter.",[62,109951,873],{"class":122},[62,109953,109954],{"class":72},"(), String.",[62,109956,61567],{"class":122},[62,109958,109959],{"class":72},"(template, name));\n",[62,109961,109962],{"class":64,"line":134},[62,109963,223],{"class":72},[62,109965,109966],{"class":64,"line":156},[62,109967,79],{"emptyLinePlaceholder":13},[62,109969,109970],{"class":64,"line":161},[62,109971,379],{"class":72},[22,109973,109974,109975,109977],{},"Now that we've set up our ",[59,109976,103735],{},", let's start the application and test our endpoint.",[26,109979,109981],{"id":109980},"step-4-testing-the-application","Step 4: Testing the Application",[22,109983,109984,109985,109987,109988,109991,109992,109995],{},"To test our application, run the ",[59,109986,24813],{}," method in your ",[59,109989,109990],{},"RestServiceApplication"," class to start the server. You can then access the ",[59,109993,109994],{},"/greeting"," endpoint using a browser or tools like Postman.",[22,109997,109998,109999,110002,110003,110005,110006,110009],{},"Once the application is up and running, navigate to ",[59,110000,110001],{},"http://localhost:8080/greeting"," and verify that you receive the expected JSON response with an ID and content. You can also test the ",[59,110004,3107],{}," parameter by appending ",[59,110007,110008],{},"?name=YourName"," to the endpoint URL.",[26,110011,110013],{"id":110012},"step-5-building-an-executable-jar","Step 5: Building an Executable JAR",[22,110015,110016],{},"Now that our web service is functional, it's time to package it as an executable JAR. This will allow us to easily deploy and run the web service on any machine with Java installed.",[22,110018,110019],{},"To package the application as a JAR, follow these steps:",[34,110021,110022,110025,110034],{},[37,110023,110024],{},"Open a terminal and navigate to your project directory",[37,110026,91325,110027,744,110030,110033],{},[59,110028,110029],{},"./mvnw package",[59,110031,110032],{},"mvnw.cmd package"," on Windows) to build the JAR file",[37,110035,110036,110037,110039],{},"Verify that a JAR file is generated in the ",[59,110038,3651],{}," directory",[22,110041,110042,110043,2755],{},"You can now run the web service using the command ",[59,110044,110045],{},"java -jar target/rest-service-0.0.1-SNAPSHOT.jar",[26,110047,32553],{"id":32552},[22,110049,110050],{},"Congratulations! You've just built a simple RESTful web service using Spring Boot!",[22,110052,110053,110054,110058],{},"This tutorial covered the basics of creating a RESTful web service, but there's plenty more to learn. The ",[677,110055,110057],{"href":86207,"rel":110056},[681],"Spring.io guides"," provide numerous tutorials and examples to help you continue building your skills and expanding your knowledge of Spring Boot and its many features.",[22,110060,110061],{},"Remember to leave a comment below if you enjoyed this tutorial, and let us know if you'd like to see more like this!",[22,110063,1525],{},[1527,110065,110066],{},"html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .spoOn, html code.shiki .spoOn{--shiki-default:#E36209;--shiki-dark:#FFAB70;--shiki-sepia:#E36209}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}",{"title":57,"searchDepth":76,"depth":76,"links":110068},[110069,110070,110071,110072,110073,110074,110075,110076],{"id":109491,"depth":76,"text":109492},{"id":109504,"depth":76,"text":109505},{"id":109522,"depth":76,"text":109523},{"id":109576,"depth":76,"text":109577},{"id":109763,"depth":76,"text":109764},{"id":109980,"depth":76,"text":109981},{"id":110012,"depth":76,"text":110013},{"id":32552,"depth":76,"text":32553},{"_id":110078,"path":110079,"title":110080,"description":110081,"meta":110082,"body":110088},"content/blog/2020/06/03/deno-standard-in-out.md","/blog/2020/06/03/deno-standard-in-out","Working with Standard Input and Output in Deno","In this tutorial I will show you how to work with standard input and output in Deno.",{"slug":110083,"date":110084,"published":13,"tags":110085,"author":17,"cover":110087,"excerpt":-1},"deno-standard-in-out","2020-06-03T20:00:00.500Z",[110086,105534],"Deno","./deno_stdin_stdout-cover.png",{"type":19,"value":110089,"toc":110965},[110090,110099,110108,110112,110123,110127,110143,110162,110165,110257,110279,110283,110289,110308,110322,110332,110355,110374,110407,110410,110414,110422,110463,110479,110529,110532,110583,110593,110620,110623,110711,110715,110718,110860,110863,110947,110949,110958,110962],[22,110091,110092,110093,110098],{},"I recently posted a ",[677,110094,110097],{"href":110095,"rel":110096},"https://www.danvega.dev/blog/2020/05/29/hello-deno/",[681],"introduction to Deno"," where I walk through how to get Deno installed and the features I think make it an exciting project. The next step for me was to start building something with Deno.",[22,110100,110101,110102,110107],{},"The first project I took a look at was a node script I used to ",[677,110103,110106],{"href":110104,"rel":110105},"https://www.danvega.dev/blog/2019/04/23/gridsome-blog-post-generator/",[681],"generate new blog posts"," in Gridsome. Before I tackle the entire script I want to use this tutorial to discuss how you can work with standard input and output in Deno.",[26,110109,110111],{"id":110110},"standard-input-standard-output","Standard Input & Standard Output",[22,110113,110114,110115,110118,110119,110122],{},"Before you write any code I think it is important for you to understand what standard in and standard out are. Standard output, sometimes abbreviated ",[59,110116,110117],{},"stdout",", refers to the standardized stream to which a program writes its output data. Standard input, sometimes abbreviated ",[59,110120,110121],{},"stdin"," is a stream from which a program reads its input data. Without getting into low level abstractions this is way to read and write data.",[26,110124,110126],{"id":110125},"deno-stdin-stdout","Deno stdin & stdout",[22,110128,110129,110130,34867,110136,110142],{},"Now that you have an understanding of what standard in & out are let's talk about them in the context of Deno. If you want to get a handle for ",[677,110131,110134],{"href":110132,"rel":110133},"https://doc.deno.land/https/github.com/denoland/deno/releases/latest/download/lib.deno.d.ts#Deno.stdin",[681],[59,110135,110121],{},[677,110137,110140],{"href":110138,"rel":110139},"https://doc.deno.land/https/github.com/denoland/deno/releases/latest/download/lib.deno.d.ts#Deno.stdout",[681],[59,110141,110117],{}," you can use the properties from the Deno Runtime API:",[52,110144,110146],{"className":1269,"code":110145,"language":1271,"meta":57,"style":57},"Deno.stdin;\nDeno.stdout;\n",[59,110147,110148,110155],{"__ignoreMap":57},[62,110149,110150,110153],{"class":64,"line":65},[62,110151,110152],{"class":72},"Deno.stdin",[62,110154,153],{"class":85},[62,110156,110157,110160],{"class":64,"line":76},[62,110158,110159],{"class":72},"Deno.stdout",[62,110161,153],{"class":85},[22,110163,110164],{},"If you take a look at the source for Deno you will see the following:",[52,110166,110169],{"className":110167,"code":110168,"language":50401,"meta":57,"style":57},"language-ts shiki shiki-themes github-light github-dark github-light","/** A handle for `stdin`. */\nexport const stdin: Reader & ReaderSync & Closer & { rid: number };\n/** A handle for `stdout`. */\nexport const stdout: Writer & WriterSync & Closer & { rid: number };\n",[59,110170,110171,110176,110217,110222],{"__ignoreMap":57},[62,110172,110173],{"class":64,"line":65},[62,110174,110175],{"class":85},"/** A handle for `stdin`. */\n",[62,110177,110178,110180,110183,110186,110188,110191,110194,110197,110199,110202,110204,110206,110209,110211,110214],{"class":64,"line":76},[62,110179,14767],{"class":68},[62,110181,110182],{"class":68}," const",[62,110184,110185],{"class":149}," stdin",[62,110187,1266],{"class":68},[62,110189,110190],{"class":122}," Reader",[62,110192,110193],{"class":68}," &",[62,110195,110196],{"class":122}," ReaderSync",[62,110198,110193],{"class":68},[62,110200,110201],{"class":122}," Closer",[62,110203,110193],{"class":68},[62,110205,21785],{"class":72},[62,110207,110208],{"class":889},"rid",[62,110210,1266],{"class":68},[62,110212,110213],{"class":149}," number",[62,110215,110216],{"class":72}," };\n",[62,110218,110219],{"class":64,"line":82},[62,110220,110221],{"class":85},"/** A handle for `stdout`. */\n",[62,110223,110224,110226,110228,110231,110233,110236,110238,110241,110243,110245,110247,110249,110251,110253,110255],{"class":64,"line":89},[62,110225,14767],{"class":68},[62,110227,110182],{"class":68},[62,110229,110230],{"class":149}," stdout",[62,110232,1266],{"class":68},[62,110234,110235],{"class":122}," Writer",[62,110237,110193],{"class":68},[62,110239,110240],{"class":122}," WriterSync",[62,110242,110193],{"class":68},[62,110244,110201],{"class":122},[62,110246,110193],{"class":68},[62,110248,21785],{"class":72},[62,110250,110208],{"class":889},[62,110252,1266],{"class":68},[62,110254,110213],{"class":149},[62,110256,110216],{"class":72},[22,110258,110259,110260,110262,110263,976,110266,19931,110269,110272,110273,110278],{},"If you're new to TypeScript that probably looks a little confusing but don't let it scare you. Deno is declaring a variable called ",[59,110261,110121],{}," that is a type that combines ",[59,110264,110265],{},"Reader",[59,110267,110268],{},"ReaderSync",[59,110270,110271],{},"Closer"," and an object with a property called rid. In TypeScript these are called ",[677,110274,110277],{"href":110275,"rel":110276},"https://www.typescriptlang.org/docs/handbook/advanced-types.html#intersection-types",[681],"Intersection Types"," and they are pretty darn cool once you start using them.",[636,110280,110282],{"id":110281},"deno-standard-output","Deno Standard Output",[22,110284,110285,110286,2755],{},"So you know about standard out and how to get a handle on it in Deno. I want to come back to the problem at hand and that is replacing a blog post generator I wrote in Node. To do so the first task will be to ask the user (me) for some information about the post. Deno is trying to stay compatible with Browser APIs so one option we have is to just use ",[59,110287,110288],{},"console.log()",[52,110290,110292],{"className":105863,"code":110291,"language":105865,"meta":57,"style":57},"console.log(\"Post Title:\");\n",[59,110293,110294],{"__ignoreMap":57},[62,110295,110296,110299,110301,110303,110306],{"class":64,"line":65},[62,110297,110298],{"class":72},"console.",[62,110300,58271],{"class":122},[62,110302,2109],{"class":72},[62,110304,110305],{"class":1675},"\"Post Title:\"",[62,110307,1133],{"class":72},[22,110309,110310,110311,110313,110314,110317,110318,110321],{},"The problem with this is you are always going to get a new line feed so the user won't be able to type a response on the same line which isn't what I want. No worries though, now that we know ",[59,110312,110117],{}," is of type ",[59,110315,110316],{},"Writer"," which exports a single method ",[59,110319,110320],{},"write()"," we can just use that.",[22,110323,110324,110325,110327,110328,110331],{},"With that said, ",[59,110326,110320],{}," doesn't take a simple string so it won't be that easy. This method will write p.byteLength bytes from p to the underlying data stream. This method returns a promise but with ",[59,110329,110330],{},"top-level-await"," support this will be easy.",[52,110333,110335],{"className":110167,"code":110334,"language":50401,"meta":57,"style":57},"write(p: Uint8Array): Promise\u003Cnumber>\n",[59,110336,110337],{"__ignoreMap":57},[62,110338,110339,110342,110345,110348,110350,110353],{"class":64,"line":65},[62,110340,110341],{"class":122},"write",[62,110343,110344],{"class":72},"(p: Uint8Array): ",[62,110346,110347],{"class":149},"Promise",[62,110349,760],{"class":68},[62,110351,110352],{"class":72},"number",[62,110354,1784],{"class":68},[22,110356,110357,110358,110365,110366,110369,110370,110373],{},"So now we just need to figure out how to pass in a simple string to the write method. It turns out there is a class in the Runtime API to help us out with this called ",[677,110359,110362],{"href":110360,"rel":110361},"https://doc.deno.land/https/github.com/denoland/deno/releases/latest/download/lib.deno.d.ts#TextEncoder",[681],[59,110363,110364],{},"TextEncoder",". This class has a method called ",[59,110367,110368],{},"encode()"," that will return a ",[59,110371,110372],{},"Uint8Array"," which is exactly what we are looking for! Putting this all together we can prompt the user for the post title:",[52,110375,110377],{"className":110167,"code":110376,"language":50401,"meta":57,"style":57},"await Deno.stdout.write(new TextEncoder().encode(\"Post Title: \"));\n",[59,110378,110379],{"__ignoreMap":57},[62,110380,110381,110384,110387,110389,110391,110393,110396,110398,110400,110402,110405],{"class":64,"line":65},[62,110382,110383],{"class":68},"await",[62,110385,110386],{"class":72}," Deno.stdout.",[62,110388,110341],{"class":122},[62,110390,2109],{"class":72},[62,110392,2426],{"class":68},[62,110394,110395],{"class":122}," TextEncoder",[62,110397,3229],{"class":72},[62,110399,97431],{"class":122},[62,110401,2109],{"class":72},[62,110403,110404],{"class":1675},"\"Post Title: \"",[62,110406,6979],{"class":72},[22,110408,110409],{},"If you were to run that it will print prompt for the post title but because there is nothing to follow the program just ends. Now we need to actually read in the response.",[636,110411,110413],{"id":110412},"deno-standard-input","Deno Standard Input",[22,110415,110416,110417,110419,110420,2755],{},"Looking back at the source for ",[59,110418,110121],{}," you can see that is of type ",[59,110421,110265],{},[52,110423,110425],{"className":110167,"code":110424,"language":50401,"meta":57,"style":57},"/** A handle for `stdin`. */\nexport const stdin: Reader & ReaderSync & Closer & { rid: number };\n",[59,110426,110427,110431],{"__ignoreMap":57},[62,110428,110429],{"class":64,"line":65},[62,110430,110175],{"class":85},[62,110432,110433,110435,110437,110439,110441,110443,110445,110447,110449,110451,110453,110455,110457,110459,110461],{"class":64,"line":76},[62,110434,14767],{"class":68},[62,110436,110182],{"class":68},[62,110438,110185],{"class":149},[62,110440,1266],{"class":68},[62,110442,110190],{"class":122},[62,110444,110193],{"class":68},[62,110446,110196],{"class":122},[62,110448,110193],{"class":68},[62,110450,110201],{"class":122},[62,110452,110193],{"class":68},[62,110454,21785],{"class":72},[62,110456,110208],{"class":889},[62,110458,1266],{"class":68},[62,110460,110213],{"class":149},[62,110462,110216],{"class":72},[22,110464,110465,110466,110468,110469,110471,110472,110474,110475,110478],{},"Reader is an interface that has a single method that takes in a ",[59,110467,110372],{},". It resolves to the number of bytes read (",[59,110470,1130],{}," \u003C ",[59,110473,71507],{}," \u003C= ",[59,110476,110477],{},"p.byteLength",") and rejects if any error encountered.",[52,110480,110482],{"className":110167,"code":110481,"language":50401,"meta":57,"style":57},"export interface Reader {\n read(p: Uint8Array): Promise\u003Cnumber | null>;\n}\n",[59,110483,110484,110494,110525],{"__ignoreMap":57},[62,110485,110486,110488,110490,110492],{"class":64,"line":65},[62,110487,14767],{"class":68},[62,110489,8531],{"class":68},[62,110491,110190],{"class":122},[62,110493,126],{"class":72},[62,110495,110496,110499,110501,110503,110505,110508,110510,110512,110514,110516,110518,110520,110522],{"class":64,"line":76},[62,110497,110498],{"class":122}," read",[62,110500,2109],{"class":72},[62,110502,22],{"class":889},[62,110504,1266],{"class":68},[62,110506,110507],{"class":122}," Uint8Array",[62,110509,2712],{"class":72},[62,110511,1266],{"class":68},[62,110513,85587],{"class":122},[62,110515,760],{"class":72},[62,110517,110352],{"class":149},[62,110519,46036],{"class":68},[62,110521,13324],{"class":149},[62,110523,110524],{"class":72},">;\n",[62,110526,110527],{"class":64,"line":82},[62,110528,379],{"class":72},[22,110530,110531],{},"The first thing you need to do is create a buffer and pass that in to the read method. Remember the read method will return a Promise with number of bytes read so we are going to wait for that result.",[52,110533,110535],{"className":110167,"code":110534,"language":50401,"meta":57,"style":57},"const buf = new Uint8Array(1024);\nconst n = \u003Cnumber>await Deno.stdin.read(buf);\n",[59,110536,110537,110558],{"__ignoreMap":57},[62,110538,110539,110542,110545,110547,110549,110551,110553,110556],{"class":64,"line":65},[62,110540,110541],{"class":68},"const",[62,110543,110544],{"class":149}," buf",[62,110546,2556],{"class":68},[62,110548,466],{"class":68},[62,110550,110507],{"class":122},[62,110552,2109],{"class":72},[62,110554,110555],{"class":149},"1024",[62,110557,1133],{"class":72},[62,110559,110560,110562,110565,110567,110569,110571,110573,110575,110578,110580],{"class":64,"line":76},[62,110561,110541],{"class":68},[62,110563,110564],{"class":149}," n",[62,110566,2556],{"class":68},[62,110568,5607],{"class":72},[62,110570,110352],{"class":149},[62,110572,2583],{"class":72},[62,110574,110383],{"class":68},[62,110576,110577],{"class":72}," Deno.stdin.",[62,110579,21805],{"class":122},[62,110581,110582],{"class":72},"(buf);\n",[22,110584,110585,110586,108868,110589,110592],{},"Finally you need a way to turn that byte array into a string. The ",[59,110587,110588],{},"TextDecoder",[59,110590,110591],{},"decode()"," that can help us out with that.",[52,110594,110596],{"className":110167,"code":110595,"language":50401,"meta":57,"style":57},"decode(input?: BufferSource, options?: { stream: false }): string\n",[59,110597,110598],{"__ignoreMap":57},[62,110599,110600,110602,110605,110607,110610,110612,110615,110617],{"class":64,"line":65},[62,110601,21835],{"class":122},[62,110603,110604],{"class":72},"(input",[62,110606,75426],{"class":68},[62,110608,110609],{"class":72}," BufferSource, options",[62,110611,75426],{"class":68},[62,110613,110614],{"class":72}," { stream: ",[62,110616,50950],{"class":149},[62,110618,110619],{"class":72}," }): string\n",[22,110621,110622],{},"You will pass in the byte array and extract the positions that contain a value.",[52,110624,110626],{"className":110167,"code":110625,"language":50401,"meta":57,"style":57},"const buf = new Uint8Array(1024);\nconst n = \u003Cnumber>await Deno.stdin.read(buf);\nconst title = new TextDecoder().decode(buf.subarray(0, n)).trim();\nconsole.log(title);\n",[59,110627,110628,110646,110668,110702],{"__ignoreMap":57},[62,110629,110630,110632,110634,110636,110638,110640,110642,110644],{"class":64,"line":65},[62,110631,110541],{"class":68},[62,110633,110544],{"class":149},[62,110635,2556],{"class":68},[62,110637,466],{"class":68},[62,110639,110507],{"class":122},[62,110641,2109],{"class":72},[62,110643,110555],{"class":149},[62,110645,1133],{"class":72},[62,110647,110648,110650,110652,110654,110656,110658,110660,110662,110664,110666],{"class":64,"line":76},[62,110649,110541],{"class":68},[62,110651,110564],{"class":149},[62,110653,2556],{"class":68},[62,110655,5607],{"class":72},[62,110657,110352],{"class":149},[62,110659,2583],{"class":72},[62,110661,110383],{"class":68},[62,110663,110577],{"class":72},[62,110665,21805],{"class":122},[62,110667,110582],{"class":72},[62,110669,110670,110672,110674,110676,110678,110680,110682,110684,110687,110690,110692,110694,110697,110700],{"class":64,"line":82},[62,110671,110541],{"class":68},[62,110673,73720],{"class":149},[62,110675,2556],{"class":68},[62,110677,466],{"class":68},[62,110679,21760],{"class":122},[62,110681,3229],{"class":72},[62,110683,21835],{"class":122},[62,110685,110686],{"class":72},"(buf.",[62,110688,110689],{"class":122},"subarray",[62,110691,2109],{"class":72},[62,110693,1130],{"class":149},[62,110695,110696],{"class":72},", n)).",[62,110698,110699],{"class":122},"trim",[62,110701,822],{"class":72},[62,110703,110704,110706,110708],{"class":64,"line":89},[62,110705,110298],{"class":72},[62,110707,58271],{"class":122},[62,110709,110710],{"class":72},"(title);\n",[26,110712,110714],{"id":110713},"deno-writing-standard-out-and-reading-standard-in","Deno: Writing Standard Out and Reading Standard In",[22,110716,110717],{},"We could just repeat those same steps for each field we wanted to prompt the user for but that would get pretty repetitive. Whenever you start duplicating code an alarm should go off in your head and mine did here. Let's wrap our logic up into a function:",[52,110719,110721],{"className":110167,"code":110720,"language":50401,"meta":57,"style":57},"/*\n * Prompt for a response\n */\nasync function prompt(message: string = \"\") {\n const buf = new Uint8Array(1024);\n await Deno.stdout.write(new TextEncoder().encode(message + \": \"));\n const n = \u003Cnumber>await Deno.stdin.read(buf);\n return new TextDecoder().decode(buf.subarray(0, n)).trim();\n}\n",[59,110722,110723,110728,110733,110737,110761,110779,110808,110830,110856],{"__ignoreMap":57},[62,110724,110725],{"class":64,"line":65},[62,110726,110727],{"class":85},"/*\n",[62,110729,110730],{"class":64,"line":76},[62,110731,110732],{"class":85}," * Prompt for a response\n",[62,110734,110735],{"class":64,"line":82},[62,110736,110],{"class":85},[62,110738,110739,110741,110743,110746,110748,110750,110752,110755,110757,110759],{"class":64,"line":89},[62,110740,21516],{"class":68},[62,110742,21929],{"class":68},[62,110744,110745],{"class":122}," prompt",[62,110747,2109],{"class":72},[62,110749,19844],{"class":889},[62,110751,1266],{"class":68},[62,110753,110754],{"class":149}," string",[62,110756,2556],{"class":68},[62,110758,51048],{"class":1675},[62,110760,768],{"class":72},[62,110762,110763,110765,110767,110769,110771,110773,110775,110777],{"class":64,"line":95},[62,110764,85414],{"class":68},[62,110766,110544],{"class":149},[62,110768,2556],{"class":68},[62,110770,466],{"class":68},[62,110772,110507],{"class":122},[62,110774,2109],{"class":72},[62,110776,110555],{"class":149},[62,110778,1133],{"class":72},[62,110780,110781,110784,110786,110788,110790,110792,110794,110796,110798,110801,110803,110806],{"class":64,"line":101},[62,110782,110783],{"class":68}," await",[62,110785,110386],{"class":72},[62,110787,110341],{"class":122},[62,110789,2109],{"class":72},[62,110791,2426],{"class":68},[62,110793,110395],{"class":122},[62,110795,3229],{"class":72},[62,110797,97431],{"class":122},[62,110799,110800],{"class":72},"(message ",[62,110802,1148],{"class":68},[62,110804,110805],{"class":1675}," \": \"",[62,110807,6979],{"class":72},[62,110809,110810,110812,110814,110816,110818,110820,110822,110824,110826,110828],{"class":64,"line":107},[62,110811,85414],{"class":68},[62,110813,110564],{"class":149},[62,110815,2556],{"class":68},[62,110817,5607],{"class":72},[62,110819,110352],{"class":149},[62,110821,2583],{"class":72},[62,110823,110383],{"class":68},[62,110825,110577],{"class":72},[62,110827,21805],{"class":122},[62,110829,110582],{"class":72},[62,110831,110832,110834,110836,110838,110840,110842,110844,110846,110848,110850,110852,110854],{"class":64,"line":113},[62,110833,82091],{"class":68},[62,110835,466],{"class":68},[62,110837,21760],{"class":122},[62,110839,3229],{"class":72},[62,110841,21835],{"class":122},[62,110843,110686],{"class":72},[62,110845,110689],{"class":122},[62,110847,2109],{"class":72},[62,110849,1130],{"class":149},[62,110851,110696],{"class":72},[62,110853,110699],{"class":122},[62,110855,822],{"class":72},[62,110857,110858],{"class":64,"line":129},[62,110859,379],{"class":72},[22,110861,110862],{},"And these are the values I would like to ask the user for when I run the script:",[52,110864,110866],{"className":105863,"code":110865,"language":105865,"meta":57,"style":57},"const title = await prompt(\"Post Title\");\nconst manualSlug = await prompt(\"Slug (Leave blank to generate)\");\nconst excerpt = await prompt(\"Post Excerpt\");\nconst tags = await prompt(\"Tags (comma separated)\");\n",[59,110867,110868,110887,110907,110927],{"__ignoreMap":57},[62,110869,110870,110872,110874,110876,110878,110880,110882,110885],{"class":64,"line":65},[62,110871,110541],{"class":68},[62,110873,73720],{"class":149},[62,110875,2556],{"class":68},[62,110877,21707],{"class":68},[62,110879,110745],{"class":122},[62,110881,2109],{"class":72},[62,110883,110884],{"class":1675},"\"Post Title\"",[62,110886,1133],{"class":72},[62,110888,110889,110891,110894,110896,110898,110900,110902,110905],{"class":64,"line":76},[62,110890,110541],{"class":68},[62,110892,110893],{"class":149}," manualSlug",[62,110895,2556],{"class":68},[62,110897,21707],{"class":68},[62,110899,110745],{"class":122},[62,110901,2109],{"class":72},[62,110903,110904],{"class":1675},"\"Slug (Leave blank to generate)\"",[62,110906,1133],{"class":72},[62,110908,110909,110911,110914,110916,110918,110920,110922,110925],{"class":64,"line":82},[62,110910,110541],{"class":68},[62,110912,110913],{"class":149}," excerpt",[62,110915,2556],{"class":68},[62,110917,21707],{"class":68},[62,110919,110745],{"class":122},[62,110921,2109],{"class":72},[62,110923,110924],{"class":1675},"\"Post Excerpt\"",[62,110926,1133],{"class":72},[62,110928,110929,110931,110934,110936,110938,110940,110942,110945],{"class":64,"line":89},[62,110930,110541],{"class":68},[62,110932,110933],{"class":149}," tags",[62,110935,2556],{"class":68},[62,110937,21707],{"class":68},[62,110939,110745],{"class":122},[62,110941,2109],{"class":72},[62,110943,110944],{"class":1675},"\"Tags (comma separated)\"",[62,110946,1133],{"class":72},[26,110948,1499],{"id":1498},[22,110950,110951,110952,110957],{},"I hope this article helped you understand how to use standard in and standard out in Deno. I want to give a huge shout out to József Sallai who create the ",[677,110953,110956],{"href":110954,"rel":110955},"https://github.com/jozsefsallai/ask/blob/master/README.md",[681],"ask library",". It was a combination of digging through that code and the Deno documentation that I was able to piece this together.",[22,110959,36004,110960,82545],{},[36006,110961],{},[1527,110963,110964],{},"html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .s4-Ip, html code.shiki .s4-Ip{--shiki-default:#6A737D;--shiki-dark:#6A737D;--shiki-sepia:#6A737D}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .spoOn, html code.shiki .spoOn{--shiki-default:#E36209;--shiki-dark:#FFAB70;--shiki-sepia:#E36209}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}",{"title":57,"searchDepth":76,"depth":76,"links":110966},[110967,110968,110972,110973],{"id":110110,"depth":76,"text":110111},{"id":110125,"depth":76,"text":110126,"children":110969},[110970,110971],{"id":110281,"depth":82,"text":110282},{"id":110412,"depth":82,"text":110413},{"id":110713,"depth":76,"text":110714},{"id":1498,"depth":76,"text":1499},{"_id":110975,"path":110976,"title":110977,"description":110978,"meta":110979,"body":110985},"content/blog/2020/05/29/hello-deno.md","/blog/2020/05/29/hello-deno","Hello, Deno","An introduction to Deno which is a secure runtime for JavaScript and TypeScript",{"slug":110980,"date":110981,"published":13,"tags":110982,"author":17,"cover":110984,"excerpt":-1},"hello-deno","2020-05-29T12:00:00.000Z",[110983,32793],"deno","./hello-deno-cover.png",{"type":19,"value":110986,"toc":113390},[110987,110996,111000,111074,111078,111090,111099,111102,111111,111114,111125,111128,111148,111151,111172,111175,111250,111253,111296,111300,111307,111355,111361,112032,112036,112045,112048,112054,112057,112064,112082,112089,112137,112148,112186,112189,112192,112224,112227,112230,112237,112240,112244,112251,112352,112359,112489,112501,112504,112507,112514,112517,112554,112561,112564,112579,112584,112587,112595,112599,112602,112685,112688,112706,112712,112715,112718,112724,112798,112804,112922,112928,112934,112937,112940,112956,112996,113003,113026,113032,113035,113044,113053,113105,113111,113114,113229,113232,113235,113238,113241,113247,113297,113301,113307,113313,113316,113322,113325,113328,113379,113381,113384,113387],[22,110988,110989,110990,110995],{},"After two years of development, ",[677,110991,110994],{"href":110992,"rel":110993},"https://deno.land/v1",[681],"Deno 1.0"," was recently released and it has everyone talking. In this article, I want to give you a quick introduction to Deno, how to install it and how to create your first application. Finally, we will take a look at some of the features that make Deno an exciting project to pay attention to.",[26,110997,110999],{"id":110998},"summary","Summary",[915,111001,111002,111008,111014,111069],{},[37,111003,111004],{},[677,111005,111007],{"href":111006},"#what-is-deno","What is Deno",[37,111009,111010],{},[677,111011,111013],{"href":111012},"#installing-deno","Installing Deno",[37,111015,111016,111020],{},[677,111017,111019],{"href":111018},"#deno-features","Deno Features",[915,111021,111022,111028,111033,111039,111045,111051,111057,111063],{},[37,111023,111024],{},[677,111025,111027],{"href":111026},"#secure-runtime","Secure Runtime",[37,111029,111030],{},[677,111031,104848],{"href":111032},"#typescript-support",[37,111034,111035],{},[677,111036,111038],{"href":111037},"#standard-library","Standard Library",[37,111040,111041],{},[677,111042,111044],{"href":111043},"#built-in-testing-library","Built-in Testing Library",[37,111046,111047],{},[677,111048,111050],{"href":111049},"#promises-async--await","Promises, Async & Await",[37,111052,111053],{},[677,111054,111056],{"href":111055},"#browser-compatible-apis-fetch-window-object","Browser Compatible APIs (fetch, Window object)",[37,111058,111059],{},[677,111060,111062],{"href":111061},"#ecmascript-modules-es-modules","ECMAScript Modules (ES Modules)",[37,111064,111065],{},[677,111066,111068],{"href":111067},"#built-in-tools","Built-in tools",[37,111070,111071],{},[677,111072,1499],{"href":111073},"#conclusion",[26,111075,111077],{"id":111076},"what-is-deno","What is Deno?",[22,111079,111080,111081,111083,111084,19931,111086,111089],{},"Deno is a ",[646,111082,80588],{}," runtime for ",[646,111085,105534],{},[646,111087,111088],{},"TypeScript",". Wait, a runtime for JavaScript, isn't that what Node is? Yes, and Deno was created by Ryan Dahl, the creator of Node.",[22,111091,111092,111093,111098],{},"As most developers do we often look back at projects that we built and think about what we might do different if we started it from scratch today. Ryan has a popular talk called ",[677,111094,111097],{"href":111095,"rel":111096},"https://www.youtube.com/watch?v=M3BM9TB-8yA",[681],"10 Things I Regret About Node.js"," and if you haven't had a chance to watch I would check it out.",[22,111100,111101],{},"I know what you're thinking because I was thinking the same thing before I watched that video. Why not just address all of these concerns in Node instead of starting a brand new project. Node has grown into a massive project used by millions and it's not going anywhere. This led Ryan to create a new project called Deno and fix the problems he had with Node.",[22,111103,111104,111105,111110],{},"The first major difference with Deno is that it is written in Rust while Node was written in C++. Like Node it executes JavaScript using ",[677,111106,111109],{"href":111107,"rel":111108},"https://v8.dev/",[681],"V8"," which is Google’s open-source high-performance JavaScript and WebAssembly engine.",[26,111112,111013],{"id":111113},"installing-deno",[22,111115,111116,111117,111119,111120,2755],{},"The first thing you should do is get Deno installed and write a ",[646,111118,110977],{}," script. Deno ships as a single executable with no dependencies. You can install it using the installers below, or download a release binary from the ",[677,111121,111124],{"href":111122,"rel":111123},"https://github.com/denoland/deno/releases",[681],"releases page",[22,111126,111127],{},"Shell (Mac, Linux)",[52,111129,111131],{"className":1663,"code":111130,"language":1665,"meta":57,"style":57},"curl -fsSL https://deno.land/x/install/install.sh | sh\n",[59,111132,111133],{"__ignoreMap":57},[62,111134,111135,111137,111140,111143,111145],{"class":64,"line":65},[62,111136,18163],{"class":122},[62,111138,111139],{"class":149}," -fsSL",[62,111141,111142],{"class":1675}," https://deno.land/x/install/install.sh",[62,111144,46036],{"class":68},[62,111146,111147],{"class":122}," sh\n",[22,111149,111150],{},"Powershell (Windows)",[52,111152,111154],{"className":1663,"code":111153,"language":1665,"meta":57,"style":57},"iwr https://deno.land/x/install/install.ps1 -useb | iex\n",[59,111155,111156],{"__ignoreMap":57},[62,111157,111158,111161,111164,111167,111169],{"class":64,"line":65},[62,111159,111160],{"class":122},"iwr",[62,111162,111163],{"class":1675}," https://deno.land/x/install/install.ps1",[62,111165,111166],{"class":149}," -useb",[62,111168,46036],{"class":68},[62,111170,111171],{"class":122}," iex\n",[22,111173,111174],{},"After you install it you can use the REPL (Read Evaluate Print Loop). This will allow you to test some code right from the command line.",[52,111176,111178],{"className":1663,"code":111177,"language":1665,"meta":57,"style":57},"vega deno $ deno\nDeno 1.0.2\nexit using ctrl+d or close()\n> console.log(\"Hello, Deno 🦕\");\nHello, Deno 🦕\nundefined\n>\n",[59,111179,111180,111194,111201,111219,111231,111241,111246],{"__ignoreMap":57},[62,111181,111182,111185,111188,111191],{"class":64,"line":65},[62,111183,111184],{"class":122},"vega",[62,111186,111187],{"class":1675}," deno",[62,111189,111190],{"class":72}," $ ",[62,111192,111193],{"class":1675},"deno\n",[62,111195,111196,111198],{"class":64,"line":76},[62,111197,110086],{"class":122},[62,111199,111200],{"class":149}," 1.0.2\n",[62,111202,111203,111206,111209,111212,111214,111217],{"class":64,"line":82},[62,111204,111205],{"class":149},"exit",[62,111207,111208],{"class":1675}," using",[62,111210,111211],{"class":1675}," ctrl+d",[62,111213,72786],{"class":1675},[62,111215,111216],{"class":1675}," close",[62,111218,2223],{"class":72},[62,111220,111221,111223,111226,111229],{"class":64,"line":89},[62,111222,2583],{"class":68},[62,111224,111225],{"class":72}," console.log(",[62,111227,111228],{"class":122},"\"Hello, Deno 🦕\"",[62,111230,1133],{"class":72},[62,111232,111233,111235,111238],{"class":64,"line":95},[62,111234,86650],{"class":122},[62,111236,111237],{"class":1675}," Deno",[62,111239,111240],{"class":1675}," 🦕\n",[62,111242,111243],{"class":64,"line":101},[62,111244,111245],{"class":122},"undefined\n",[62,111247,111248],{"class":64,"line":107},[62,111249,1784],{"class":68},[22,111251,111252],{},"While you're in the REPL run the following code 🤓",[52,111254,111256],{"className":105863,"code":111255,"language":105865,"meta":57,"style":57},"\"node\"\n .split(\"\")\n .sort()\n .join(\"\");\n",[59,111257,111258,111263,111276,111284],{"__ignoreMap":57},[62,111259,111260],{"class":64,"line":65},[62,111261,111262],{"class":1675},"\"node\"\n",[62,111264,111265,111268,111270,111272,111274],{"class":64,"line":76},[62,111266,111267],{"class":72}," .",[62,111269,6894],{"class":122},[62,111271,2109],{"class":72},[62,111273,25895],{"class":1675},[62,111275,2212],{"class":72},[62,111277,111278,111280,111282],{"class":64,"line":82},[62,111279,111267],{"class":72},[62,111281,7228],{"class":122},[62,111283,2223],{"class":72},[62,111285,111286,111288,111290,111292,111294],{"class":64,"line":89},[62,111287,111267],{"class":72},[62,111289,27013],{"class":122},[62,111291,2109],{"class":72},[62,111293,25895],{"class":1675},[62,111295,1133],{"class":72},[636,111297,111299],{"id":111298},"deno-help","Deno Help",[22,111301,111302,111303,111306],{},"If you're not sure what version of Deno you're running you can use the command ",[59,111304,111305],{},"deno --version"," which will give you the versions of Deno, V8 & TypeScript.",[52,111308,111310],{"className":1663,"code":111309,"language":1665,"meta":57,"style":57},"vega deno $ deno --version\ndeno 1.0.2\nv8 8.4.300\ntypescript 3.9.2\nvega deno $\n",[59,111311,111312,111325,111331,111339,111346],{"__ignoreMap":57},[62,111313,111314,111316,111318,111320,111322],{"class":64,"line":65},[62,111315,111184],{"class":122},[62,111317,111187],{"class":1675},[62,111319,111190],{"class":72},[62,111321,110983],{"class":1675},[62,111323,111324],{"class":149}," --version\n",[62,111326,111327,111329],{"class":64,"line":76},[62,111328,110983],{"class":122},[62,111330,111200],{"class":149},[62,111332,111333,111336],{"class":64,"line":82},[62,111334,111335],{"class":122},"v8",[62,111337,111338],{"class":149}," 8.4.300\n",[62,111340,111341,111343],{"class":64,"line":89},[62,111342,50236],{"class":122},[62,111344,111345],{"class":149}," 3.9.2\n",[62,111347,111348,111350,111352],{"class":64,"line":95},[62,111349,111184],{"class":122},[62,111351,111187],{"class":1675},[62,111353,111354],{"class":72}," $\n",[22,111356,111357,111358,2755],{},"And if you ever forget where the docs are, how to start the REPL or can't remember what sub commands are available just run the command ",[59,111359,111360],{},"deno help",[52,111362,111364],{"className":1663,"code":111363,"language":1665,"meta":57,"style":57},"vega deno $ deno help\ndeno 1.0.2\nA secure JavaScript and TypeScript runtime\n\nDocs: https://deno.land/manual\nModules: https://deno.land/std/ https://deno.land/x/\nBugs: https://github.com/denoland/deno/issues\n\nTo start the REPL:\n deno\n\nTo execute a script:\n deno run https://deno.land/std/examples/welcome.ts\n\nTo evaluate code in the shell:\n deno eval \"console.log(30933 + 404)\"\n\nUSAGE:\n deno [OPTIONS] [SUBCOMMAND]\n\nOPTIONS:\n -h, --help Prints help information\n -L, --log-level \u003Clog-level> Set log level [possible values: debug, info]\n -q, --quiet Suppress diagnostic output\n -V, --version Prints version information\n\nSUBCOMMANDS:\n bundle Bundle module and dependencies into single file\n cache Cache the dependencies\n completions Generate shell completions\n doc Show documentation for a module\n eval Eval script\n fmt Format source files\n help Prints this message or the help of the given subcommand(s)\n info Show info about cache or info related to source file\n install Install script as an executable\n repl Read Eval Print Loop\n run Run a program given a filename or url to the module\n test Run tests\n types Print runtime TypeScript declarations\n upgrade Upgrade deno executable to given version\n\nENVIRONMENT VARIABLES:\n DENO_DIR Set deno's base directory (defaults to $HOME/.deno)\n DENO_INSTALL_ROOT Set deno install's output directory\n (defaults to $HOME/.deno/bin)\n NO_COLOR Set to disable color\n HTTP_PROXY Proxy address for HTTP requests\n (module downloads, fetch)\n HTTPS_PROXY Same but for HTTPS\nvega deno $\n",[59,111365,111366,111379,111385,111403,111407,111415,111426,111434,111438,111451,111456,111460,111472,111482,111486,111503,111513,111517,111522,111530,111534,111539,111556,111595,111612,111628,111632,111637,111662,111675,111689,111707,111718,111732,111766,111796,111815,111832,111863,111874,111890,111910,111914,111922,111933,111944,111961,111976,111995,112008,112024],{"__ignoreMap":57},[62,111367,111368,111370,111372,111374,111376],{"class":64,"line":65},[62,111369,111184],{"class":122},[62,111371,111187],{"class":1675},[62,111373,111190],{"class":72},[62,111375,110983],{"class":1675},[62,111377,111378],{"class":1675}," help\n",[62,111380,111381,111383],{"class":64,"line":76},[62,111382,110983],{"class":122},[62,111384,111200],{"class":149},[62,111386,111387,111389,111392,111395,111397,111400],{"class":64,"line":82},[62,111388,70492],{"class":122},[62,111390,111391],{"class":1675}," secure",[62,111393,111394],{"class":1675}," JavaScript",[62,111396,50404],{"class":1675},[62,111398,111399],{"class":1675}," TypeScript",[62,111401,111402],{"class":1675}," runtime\n",[62,111404,111405],{"class":64,"line":89},[62,111406,79],{"emptyLinePlaceholder":13},[62,111408,111409,111412],{"class":64,"line":95},[62,111410,111411],{"class":122},"Docs:",[62,111413,111414],{"class":1675}," https://deno.land/manual\n",[62,111416,111417,111420,111423],{"class":64,"line":101},[62,111418,111419],{"class":122},"Modules:",[62,111421,111422],{"class":1675}," https://deno.land/std/",[62,111424,111425],{"class":1675}," https://deno.land/x/\n",[62,111427,111428,111431],{"class":64,"line":107},[62,111429,111430],{"class":122},"Bugs:",[62,111432,111433],{"class":1675}," https://github.com/denoland/deno/issues\n",[62,111435,111436],{"class":64,"line":113},[62,111437,79],{"emptyLinePlaceholder":13},[62,111439,111440,111443,111446,111448],{"class":64,"line":129},[62,111441,111442],{"class":122},"To",[62,111444,111445],{"class":1675}," start",[62,111447,53632],{"class":1675},[62,111449,111450],{"class":1675}," REPL:\n",[62,111452,111453],{"class":64,"line":134},[62,111454,111455],{"class":122}," deno\n",[62,111457,111458],{"class":64,"line":156},[62,111459,79],{"emptyLinePlaceholder":13},[62,111461,111462,111464,111467,111469],{"class":64,"line":161},[62,111463,111442],{"class":122},[62,111465,111466],{"class":1675}," execute",[62,111468,28870],{"class":1675},[62,111470,111471],{"class":1675}," script:\n",[62,111473,111474,111477,111479],{"class":64,"line":167},[62,111475,111476],{"class":122}," deno",[62,111478,1716],{"class":1675},[62,111480,111481],{"class":1675}," https://deno.land/std/examples/welcome.ts\n",[62,111483,111484],{"class":64,"line":173},[62,111485,79],{"emptyLinePlaceholder":13},[62,111487,111488,111490,111493,111496,111498,111500],{"class":64,"line":179},[62,111489,111442],{"class":122},[62,111491,111492],{"class":1675}," evaluate",[62,111494,111495],{"class":1675}," code",[62,111497,57031],{"class":1675},[62,111499,53632],{"class":1675},[62,111501,111502],{"class":1675}," shell:\n",[62,111504,111505,111507,111510],{"class":64,"line":185},[62,111506,111476],{"class":122},[62,111508,111509],{"class":1675}," eval",[62,111511,111512],{"class":1675}," \"console.log(30933 + 404)\"\n",[62,111514,111515],{"class":64,"line":191},[62,111516,79],{"emptyLinePlaceholder":13},[62,111518,111519],{"class":64,"line":209},[62,111520,111521],{"class":122},"USAGE:\n",[62,111523,111524,111527],{"class":64,"line":220},[62,111525,111526],{"class":122}," deno",[62,111528,111529],{"class":72}," [OPTIONS] [SUBCOMMAND]\n",[62,111531,111532],{"class":64,"line":226},[62,111533,79],{"emptyLinePlaceholder":13},[62,111535,111536],{"class":64,"line":231},[62,111537,111538],{"class":122},"OPTIONS:\n",[62,111540,111541,111544,111547,111550,111553],{"class":64,"line":236},[62,111542,111543],{"class":122}," -h,",[62,111545,111546],{"class":149}," --help",[62,111548,111549],{"class":1675}," Prints",[62,111551,111552],{"class":1675}," help",[62,111554,111555],{"class":1675}," information\n",[62,111557,111558,111561,111564,111566,111569,111572,111574,111577,111580,111583,111586,111589,111592],{"class":64,"line":242},[62,111559,111560],{"class":122}," -L,",[62,111562,111563],{"class":149}," --log-level",[62,111565,5607],{"class":68},[62,111567,111568],{"class":1675},"log-leve",[62,111570,111571],{"class":72},"l",[62,111573,2583],{"class":68},[62,111575,111576],{"class":1675}," Set",[62,111578,111579],{"class":1675}," log",[62,111581,111582],{"class":1675}," level",[62,111584,111585],{"class":72}," [possible ",[62,111587,111588],{"class":1675},"values:",[62,111590,111591],{"class":1675}," debug,",[62,111593,111594],{"class":1675}," info]\n",[62,111596,111597,111600,111603,111606,111609],{"class":64,"line":247},[62,111598,111599],{"class":122}," -q,",[62,111601,111602],{"class":149}," --quiet",[62,111604,111605],{"class":1675}," Suppress",[62,111607,111608],{"class":1675}," diagnostic",[62,111610,111611],{"class":1675}," output\n",[62,111613,111614,111617,111620,111623,111626],{"class":64,"line":252},[62,111615,111616],{"class":122}," -V,",[62,111618,111619],{"class":149}," --version",[62,111621,111622],{"class":1675}," Prints",[62,111624,111625],{"class":1675}," version",[62,111627,111555],{"class":1675},[62,111629,111630],{"class":64,"line":257},[62,111631,79],{"emptyLinePlaceholder":13},[62,111633,111634],{"class":64,"line":271},[62,111635,111636],{"class":122},"SUBCOMMANDS:\n",[62,111638,111639,111642,111645,111648,111650,111653,111656,111659],{"class":64,"line":281},[62,111640,111641],{"class":122}," bundle",[62,111643,111644],{"class":1675}," Bundle",[62,111646,111647],{"class":1675}," module",[62,111649,50404],{"class":1675},[62,111651,111652],{"class":1675}," dependencies",[62,111654,111655],{"class":1675}," into",[62,111657,111658],{"class":1675}," single",[62,111660,111661],{"class":1675}," file\n",[62,111663,111664,111667,111670,111672],{"class":64,"line":286},[62,111665,111666],{"class":122}," cache",[62,111668,111669],{"class":1675}," Cache",[62,111671,53632],{"class":1675},[62,111673,111674],{"class":1675}," dependencies\n",[62,111676,111677,111680,111683,111686],{"class":64,"line":291},[62,111678,111679],{"class":122}," completions",[62,111681,111682],{"class":1675}," Generate",[62,111684,111685],{"class":1675}," shell",[62,111687,111688],{"class":1675}," completions\n",[62,111690,111691,111694,111697,111700,111702,111704],{"class":64,"line":296},[62,111692,111693],{"class":122}," doc",[62,111695,111696],{"class":1675}," Show",[62,111698,111699],{"class":1675}," documentation",[62,111701,53629],{"class":1675},[62,111703,28870],{"class":1675},[62,111705,111706],{"class":1675}," module\n",[62,111708,111709,111712,111715],{"class":64,"line":302},[62,111710,111711],{"class":149}," eval",[62,111713,111714],{"class":1675}," Eval",[62,111716,111717],{"class":1675}," script\n",[62,111719,111720,111723,111726,111729],{"class":64,"line":308},[62,111721,111722],{"class":122}," fmt",[62,111724,111725],{"class":1675}," Format",[62,111727,111728],{"class":1675}," source",[62,111730,111731],{"class":1675}," files\n",[62,111733,111734,111737,111740,111742,111744,111746,111748,111750,111752,111754,111757,111760,111762,111764],{"class":64,"line":314},[62,111735,111736],{"class":122}," help",[62,111738,111739],{"class":1675}," Prints",[62,111741,9961],{"class":1675},[62,111743,21545],{"class":1675},[62,111745,72786],{"class":1675},[62,111747,53632],{"class":1675},[62,111749,111552],{"class":1675},[62,111751,59066],{"class":1675},[62,111753,53632],{"class":1675},[62,111755,111756],{"class":1675}," given",[62,111758,111759],{"class":1675}," subcommand",[62,111761,2109],{"class":72},[62,111763,57106],{"class":122},[62,111765,2212],{"class":72},[62,111767,111768,111771,111774,111777,111780,111783,111785,111787,111790,111792,111794],{"class":64,"line":320},[62,111769,111770],{"class":122}," info",[62,111772,111773],{"class":1675}," Show",[62,111775,111776],{"class":1675}," info",[62,111778,111779],{"class":1675}," about",[62,111781,111782],{"class":1675}," cache",[62,111784,72786],{"class":1675},[62,111786,111776],{"class":1675},[62,111788,111789],{"class":1675}," related",[62,111791,57317],{"class":1675},[62,111793,111728],{"class":1675},[62,111795,111661],{"class":1675},[62,111797,111798,111801,111804,111807,111809,111812],{"class":64,"line":326},[62,111799,111800],{"class":122}," install",[62,111802,111803],{"class":1675}," Install",[62,111805,111806],{"class":1675}," script",[62,111808,53662],{"class":1675},[62,111810,111811],{"class":1675}," an",[62,111813,111814],{"class":1675}," executable\n",[62,111816,111817,111820,111823,111826,111829],{"class":64,"line":338},[62,111818,111819],{"class":122}," repl",[62,111821,111822],{"class":1675}," Read",[62,111824,111825],{"class":1675}," Eval",[62,111827,111828],{"class":1675}," Print",[62,111830,111831],{"class":1675}," Loop\n",[62,111833,111834,111837,111840,111842,111845,111847,111849,111852,111854,111857,111859,111861],{"class":64,"line":343},[62,111835,111836],{"class":122}," run",[62,111838,111839],{"class":1675}," Run",[62,111841,28870],{"class":1675},[62,111843,111844],{"class":1675}," program",[62,111846,111756],{"class":1675},[62,111848,28870],{"class":1675},[62,111850,111851],{"class":1675}," filename",[62,111853,72786],{"class":1675},[62,111855,111856],{"class":1675}," url",[62,111858,57317],{"class":1675},[62,111860,53632],{"class":1675},[62,111862,111706],{"class":1675},[62,111864,111865,111868,111871],{"class":64,"line":357},[62,111866,111867],{"class":149}," test",[62,111869,111870],{"class":1675}," Run",[62,111872,111873],{"class":1675}," tests\n",[62,111875,111876,111879,111882,111885,111887],{"class":64,"line":366},[62,111877,111878],{"class":122}," types",[62,111880,111881],{"class":1675}," Print",[62,111883,111884],{"class":1675}," runtime",[62,111886,111399],{"class":1675},[62,111888,111889],{"class":1675}," declarations\n",[62,111891,111892,111895,111898,111900,111903,111905,111907],{"class":64,"line":371},[62,111893,111894],{"class":122}," upgrade",[62,111896,111897],{"class":1675}," Upgrade",[62,111899,111187],{"class":1675},[62,111901,111902],{"class":1675}," executable",[62,111904,57317],{"class":1675},[62,111906,111756],{"class":1675},[62,111908,111909],{"class":1675}," version\n",[62,111911,111912],{"class":64,"line":376},[62,111913,79],{"emptyLinePlaceholder":13},[62,111915,111916,111919],{"class":64,"line":16333},[62,111917,111918],{"class":122},"ENVIRONMENT",[62,111920,111921],{"class":1675}," VARIABLES:\n",[62,111923,111924,111927,111930],{"class":64,"line":16349},[62,111925,111926],{"class":122}," DENO_DIR",[62,111928,111929],{"class":1675}," Set",[62,111931,111932],{"class":1675}," deno's base directory (defaults to $HOME/.deno)\n",[62,111934,111935,111938,111941],{"class":64,"line":16365},[62,111936,111937],{"class":1675}," DENO_INSTALL_ROOT Set deno install's",[62,111939,111940],{"class":1675}," output",[62,111942,111943],{"class":1675}," directory\n",[62,111945,111946,111949,111951,111953,111956,111959],{"class":64,"line":16381},[62,111947,111948],{"class":72}," (",[62,111950,36517],{"class":122},[62,111952,57317],{"class":1675},[62,111954,111955],{"class":72}," $HOME",[62,111957,111958],{"class":1675},"/.deno/bin",[62,111960,2212],{"class":72},[62,111962,111963,111966,111968,111970,111973],{"class":64,"line":16402},[62,111964,111965],{"class":122}," NO_COLOR",[62,111967,111929],{"class":1675},[62,111969,57317],{"class":1675},[62,111971,111972],{"class":1675}," disable",[62,111974,111975],{"class":1675}," color\n",[62,111977,111978,111981,111984,111987,111989,111992],{"class":64,"line":16411},[62,111979,111980],{"class":122}," HTTP_PROXY",[62,111982,111983],{"class":1675}," Proxy",[62,111985,111986],{"class":1675}," address",[62,111988,53629],{"class":1675},[62,111990,111991],{"class":1675}," HTTP",[62,111993,111994],{"class":1675}," requests\n",[62,111996,111997,111999,112001,112004,112006],{"class":64,"line":16427},[62,111998,111948],{"class":72},[62,112000,32813],{"class":122},[62,112002,112003],{"class":1675}," downloads,",[62,112005,21995],{"class":1675},[62,112007,2212],{"class":72},[62,112009,112010,112013,112016,112019,112021],{"class":64,"line":16448},[62,112011,112012],{"class":122}," HTTPS_PROXY",[62,112014,112015],{"class":1675}," Same",[62,112017,112018],{"class":1675}," but",[62,112020,53629],{"class":1675},[62,112022,112023],{"class":1675}," HTTPS\n",[62,112025,112026,112028,112030],{"class":64,"line":16457},[62,112027,111184],{"class":122},[62,112029,111187],{"class":1675},[62,112031,111354],{"class":72},[636,112033,112035],{"id":112034},"setting-up-your-development-environment","Setting up your development environment",[22,112037,112038,112039,112044],{},"Now that Deno is installed there is one last thing to do before you write that first program. You can check out ",[677,112040,112043],{"href":112041,"rel":112042},"https://deno.land/manual/getting_started/setup_your_environment",[681],"the manual"," for complete instructions on setting up your development environment.",[22,112046,112047],{},"If you're a Visual Studio Code user (of course you are 😜) there are a few Deno extensions in the Marketplace. I haven't had a chance to look at all of them yet but the link below is the official extension from the Deno team and it's the one I am using.",[22,112049,112050],{},[677,112051,112052],{"href":112052,"rel":112053},"https://marketplace.visualstudio.com/items?itemName=denoland.vscode-deno",[681],[636,112055,112056],{"id":110980},"Hello, Deno!",[22,112058,112059,112060,112063],{},"In your editor of choice create a new file called ",[59,112061,112062],{},"HelloWorld.ts"," and enter the following code",[52,112065,112067],{"className":105863,"code":112066,"language":105865,"meta":57,"style":57},"console.log(\"Welcome to Deno 🦕\");\n",[59,112068,112069],{"__ignoreMap":57},[62,112070,112071,112073,112075,112077,112080],{"class":64,"line":65},[62,112072,110298],{"class":72},[62,112074,58271],{"class":122},[62,112076,2109],{"class":72},[62,112078,112079],{"class":1675},"\"Welcome to Deno 🦕\"",[62,112081,1133],{"class":72},[22,112083,112084,112085,112088],{},"To run the program type ",[59,112086,112087],{},"deno run HelloWorld.ts"," from the command line. When you run that command you should get the following output.",[52,112090,112092],{"className":1663,"code":112091,"language":1665,"meta":57,"style":57},"vega hello-world $ deno run HelloWorld.ts\nCompile file:///Users/vega/dev/deno/hello-world/HelloWorld.ts\nWelcome to Deno 🦕\nvega hello-world $\n",[59,112093,112094,112110,112118,112129],{"__ignoreMap":57},[62,112095,112096,112098,112101,112103,112105,112107],{"class":64,"line":65},[62,112097,111184],{"class":122},[62,112099,112100],{"class":1675}," hello-world",[62,112102,111190],{"class":72},[62,112104,110983],{"class":1675},[62,112106,1716],{"class":1675},[62,112108,112109],{"class":1675}," HelloWorld.ts\n",[62,112111,112112,112115],{"class":64,"line":76},[62,112113,112114],{"class":122},"Compile",[62,112116,112117],{"class":1675}," file:///Users/vega/dev/deno/hello-world/HelloWorld.ts\n",[62,112119,112120,112123,112125,112127],{"class":64,"line":82},[62,112121,112122],{"class":122},"Welcome",[62,112124,57317],{"class":1675},[62,112126,111237],{"class":1675},[62,112128,111240],{"class":1675},[62,112130,112131,112133,112135],{"class":64,"line":89},[62,112132,111184],{"class":122},[62,112134,112100],{"class":1675},[62,112136,111354],{"class":72},[22,112138,112139,112140,112143,112144,112147],{},"You just wrote some JavaScript inside of a TypeScript file. Deno saw the ",[59,112141,112142],{},".ts"," extension and used the built-in TypeScript compiler to compile your application. As you just saw you are not forced into writing TypeScript but it is nice that it is supported out of the box and, we will talk more about that later in this article. If you change the extension to ",[59,112145,112146],{},".js"," and run it you will the same output minus the compilation process.",[52,112149,112151],{"className":1663,"code":112150,"language":1665,"meta":57,"style":57},"vega hello-world $ deno run HelloWorld.js\nWelcome to Deno 🦕\nvega hello-world $\n",[59,112152,112153,112168,112178],{"__ignoreMap":57},[62,112154,112155,112157,112159,112161,112163,112165],{"class":64,"line":65},[62,112156,111184],{"class":122},[62,112158,112100],{"class":1675},[62,112160,111190],{"class":72},[62,112162,110983],{"class":1675},[62,112164,1716],{"class":1675},[62,112166,112167],{"class":1675}," HelloWorld.js\n",[62,112169,112170,112172,112174,112176],{"class":64,"line":76},[62,112171,112122],{"class":122},[62,112173,57317],{"class":1675},[62,112175,111237],{"class":1675},[62,112177,111240],{"class":1675},[62,112179,112180,112182,112184],{"class":64,"line":82},[62,112181,111184],{"class":122},[62,112183,112100],{"class":1675},[62,112185,111354],{"class":72},[26,112187,111019],{"id":112188},"deno-features",[22,112190,112191],{},"Now that you have an introduction to Deno and have your first script I want to dive into some of the features. Here are some things I think make Deno an exciting project to pay attention to:",[915,112193,112194,112197,112200,112203,112206,112209,112212,112215,112221],{},[37,112195,112196],{},"✔ Secure Runtime",[37,112198,112199],{},"✔ TypeScript Support",[37,112201,112202],{},"✔ Standard Library",[37,112204,112205],{},"✔ Built-in Testing Library",[37,112207,112208],{},"✔ Promises, Async & Await",[37,112210,112211],{},"✔ Browser Compatible APIs",[37,112213,112214],{},"✔ ES Modules",[37,112216,112217,112218],{},"✔ No ",[59,112219,112220],{},"node_modules",[37,112222,112223],{},"✔ Built-in tools",[636,112225,111027],{"id":112226},"secure-runtime",[22,112228,112229],{},"The Node Package Manager (NPM) allows you to quickly find a package to solve just about any problem. A big problem here is that we as developers have gotten lazy when it comes to questioning if a package is necessary.",[22,112231,112232,112233,112236],{},"We have gotten to the point where if we need some code to perform some task we will just ",[59,112234,112235],{},"npm i write-my-code-for-me",". I'm not saying you can't install that library, but take the time and ask yourself is this something I really need and if so what is this code doing. That library you just downloaded has been given full permission to do whatever it wants and that can lead to some real problems.",[22,112238,112239],{},"Deno on the other hand executes code in a sandbox. This means that by default Deno will not have permission to access the network, file system, and the environment.",[28831,112241,112243],{"id":112242},"http-server-demo","Http Server Demo",[22,112245,112246,112247,112250],{},"Below is a script that will start up a simple http server on port 8000 and respond to a request with a simple key/value pair in JSON. Create a new file called ",[59,112248,112249],{},"server.ts"," and add in the following code:",[52,112252,112254],{"className":110167,"code":112253,"language":50401,"meta":57,"style":57},"import { serve } from \"https://deno.land/std@0.50.0/http/server.ts\";\nconst s = serve({ port: 8000 });\nconsole.log(\"http://localhost:8000/\");\nfor await (const req of s) {\n req.respond({ body: JSON.stringify({ msg: \"Hello, World!\" }) });\n}\n",[59,112255,112256,112270,112290,112303,112321,112348],{"__ignoreMap":57},[62,112257,112258,112260,112263,112265,112268],{"class":64,"line":65},[62,112259,27875],{"class":68},[62,112261,112262],{"class":72}," { serve } ",[62,112264,3507],{"class":68},[62,112266,112267],{"class":1675}," \"https://deno.land/std@0.50.0/http/server.ts\"",[62,112269,153],{"class":72},[62,112271,112272,112274,112277,112279,112282,112285,112288],{"class":64,"line":76},[62,112273,110541],{"class":68},[62,112275,112276],{"class":149}," s",[62,112278,2556],{"class":68},[62,112280,112281],{"class":122}," serve",[62,112283,112284],{"class":72},"({ port: ",[62,112286,112287],{"class":149},"8000",[62,112289,21843],{"class":72},[62,112291,112292,112294,112296,112298,112301],{"class":64,"line":82},[62,112293,110298],{"class":72},[62,112295,58271],{"class":122},[62,112297,2109],{"class":72},[62,112299,112300],{"class":1675},"\"http://localhost:8000/\"",[62,112302,1133],{"class":72},[62,112304,112305,112307,112309,112311,112313,112316,112318],{"class":64,"line":89},[62,112306,741],{"class":68},[62,112308,21707],{"class":68},[62,112310,744],{"class":72},[62,112312,110541],{"class":68},[62,112314,112315],{"class":149}," req",[62,112317,59066],{"class":68},[62,112319,112320],{"class":72}," s) {\n",[62,112322,112323,112326,112329,112332,112335,112337,112340,112343,112345],{"class":64,"line":95},[62,112324,112325],{"class":72}," req.",[62,112327,112328],{"class":122},"respond",[62,112330,112331],{"class":72},"({ body: ",[62,112333,112334],{"class":149},"JSON",[62,112336,2755],{"class":72},[62,112338,112339],{"class":122},"stringify",[62,112341,112342],{"class":72},"({ msg: ",[62,112344,27469],{"class":1675},[62,112346,112347],{"class":72}," }) });\n",[62,112349,112350],{"class":64,"line":101},[62,112351,379],{"class":72},[22,112353,112354,112355,112358],{},"If you try and run this script using ",[59,112356,112357],{},"deno run server.ts"," you will get the following error:",[52,112360,112362],{"className":1663,"code":112361,"language":1665,"meta":57,"style":57},"vega basic-server $ deno run server.ts\nCompile file:///Users/vega/dev/deno/basic-server/server.ts\nerror: Uncaught PermissionDenied: network access to \"0.0.0.0:8000\", run again with the --allow-net flag\n at unwrapResponse ($deno$/ops/dispatch_json.ts:43:11)\n at Object.sendSync ($deno$/ops/dispatch_json.ts:72:10)\n at Object.listen ($deno$/ops/net.ts:51:10)\n at listen ($deno$/net.ts:152:22)\n at serve (https://deno.land/std@0.50.0/http/server.ts:261:20)\n at file:///Users/vega/dev/deno/basic-server/server.ts:2:11\nvega basic-server $\n",[59,112363,112364,112380,112387,112425,112435,112445,112455,112465,112474,112481],{"__ignoreMap":57},[62,112365,112366,112368,112371,112373,112375,112377],{"class":64,"line":65},[62,112367,111184],{"class":122},[62,112369,112370],{"class":1675}," basic-server",[62,112372,111190],{"class":72},[62,112374,110983],{"class":1675},[62,112376,1716],{"class":1675},[62,112378,112379],{"class":1675}," server.ts\n",[62,112381,112382,112384],{"class":64,"line":76},[62,112383,112114],{"class":122},[62,112385,112386],{"class":1675}," file:///Users/vega/dev/deno/basic-server/server.ts\n",[62,112388,112389,112392,112395,112398,112401,112404,112406,112409,112411,112414,112417,112419,112422],{"class":64,"line":82},[62,112390,112391],{"class":122},"error:",[62,112393,112394],{"class":1675}," Uncaught",[62,112396,112397],{"class":1675}," PermissionDenied:",[62,112399,112400],{"class":1675}," network",[62,112402,112403],{"class":1675}," access",[62,112405,57317],{"class":1675},[62,112407,112408],{"class":1675}," \"0.0.0.0:8000\",",[62,112410,1716],{"class":1675},[62,112412,112413],{"class":1675}," again",[62,112415,112416],{"class":1675}," with",[62,112418,53632],{"class":1675},[62,112420,112421],{"class":149}," --allow-net",[62,112423,112424],{"class":1675}," flag\n",[62,112426,112427,112429,112432],{"class":64,"line":89},[62,112428,70808],{"class":122},[62,112430,112431],{"class":1675}," unwrapResponse",[62,112433,112434],{"class":72}," ($deno$/ops/dispatch_json.ts:43:11)\n",[62,112436,112437,112439,112442],{"class":64,"line":95},[62,112438,70808],{"class":122},[62,112440,112441],{"class":1675}," Object.sendSync",[62,112443,112444],{"class":72}," ($deno$/ops/dispatch_json.ts:72:10)\n",[62,112446,112447,112449,112452],{"class":64,"line":101},[62,112448,70808],{"class":122},[62,112450,112451],{"class":1675}," Object.listen",[62,112453,112454],{"class":72}," ($deno$/ops/net.ts:51:10)\n",[62,112456,112457,112459,112462],{"class":64,"line":107},[62,112458,70808],{"class":122},[62,112460,112461],{"class":1675}," listen",[62,112463,112464],{"class":72}," ($deno$/net.ts:152:22)\n",[62,112466,112467,112469,112471],{"class":64,"line":113},[62,112468,70808],{"class":122},[62,112470,112281],{"class":1675},[62,112472,112473],{"class":72}," (https://deno.land/std@0.50.0/http/server.ts:261:20)\n",[62,112475,112476,112478],{"class":64,"line":129},[62,112477,70808],{"class":122},[62,112479,112480],{"class":1675}," file:///Users/vega/dev/deno/basic-server/server.ts:2:11\n",[62,112482,112483,112485,112487],{"class":64,"line":134},[62,112484,111184],{"class":122},[62,112486,112370],{"class":1675},[62,112488,111354],{"class":72},[22,112490,112491,112492,112495,112496,2755],{},"To run the script you will need to run the command ",[59,112493,112494],{},"deno run --allow-net server.ts",". This is what Deno means when it says that it is secure by default. If you're interested there is a list of permissions in the ",[677,112497,112500],{"href":112498,"rel":112499},"https://deno.land/manual/getting_started/permissions#permissions-list",[681],"Deno Manual",[636,112502,104848],{"id":112503},"typescript-support",[22,112505,112506],{},"I mentioned this a couple of times already but Deno supports both JavaScript and TypeScript as first-class languages. Coming from a Java background I appreciate the benefits of types especially working on a team in larger projects. If you're not a fan of TypeScript, stick with JavaScript.",[22,112508,112509,112510,112513],{},"There are many frameworks & runtimes that support TypeScript but they have to provide additional tooling to do so. Deno is designed with TypeScript in mind and the standard modules are all written in TypeScript. The ",[59,112511,112512],{},"deno types"," command provides type declarations for everything provided by Deno.",[22,112515,112516],{},"Because both JavaScript and TypeScript are supported it means that you need to provide fully qualified names, including the extension.",[52,112518,112520],{"className":110167,"code":112519,"language":50401,"meta":57,"style":57},"import { serve } from \"https://deno.land/std@0.53.0/http/server.ts\"; // RIGHT\nimport { serve } from \"https://deno.land/std@0.53.0/http/server\"; // WRONG\n",[59,112521,112522,112538],{"__ignoreMap":57},[62,112523,112524,112526,112528,112530,112533,112535],{"class":64,"line":65},[62,112525,27875],{"class":68},[62,112527,112262],{"class":72},[62,112529,3507],{"class":68},[62,112531,112532],{"class":1675}," \"https://deno.land/std@0.53.0/http/server.ts\"",[62,112534,1168],{"class":72},[62,112536,112537],{"class":85},"// RIGHT\n",[62,112539,112540,112542,112544,112546,112549,112551],{"class":64,"line":76},[62,112541,27875],{"class":68},[62,112543,112262],{"class":72},[62,112545,3507],{"class":68},[62,112547,112548],{"class":1675}," \"https://deno.land/std@0.53.0/http/server\"",[62,112550,1168],{"class":72},[62,112552,112553],{"class":85},"// WRONG\n",[22,112555,112556,112557,2755],{},"If you want to learn more about using external type definitions or customizing the TypeScript compiler options checkout the ",[677,112558,112500],{"href":112559,"rel":112560},"https://deno.land/manual/getting_started/typescript",[681],[636,112562,111038],{"id":112563},"standard-library",[22,112565,112566,112567,112572,112573,112578],{},"I recently started learning the ",[677,112568,112571],{"href":112569,"rel":112570},"https://golang.org/",[681],"Go Programming Language"," and when I started diving into the ",[677,112574,112577],{"href":112575,"rel":112576},"https://deno.land/std",[681],"Deno Standard Library"," it felt familiar to me. The reason for this is that Deno was originally written in Go and to this day draws a lot of inspiration from the language. If you look in the documentation for the standard library it even says the following:",[29685,112580,112581],{},[22,112582,112583],{},"deno_std is a loose port of Go's standard library. When in doubt, simply port Go's source code, documentation, and tests.",[22,112585,112586],{},"As I mentioned earlier the standard library is written entirely in TypeScript and it is async by default, more on that in a bit. The standard library modules do not have external dependencies and they are reviewed by the Deno core team.",[22,112588,112589,112590,112594],{},"These modules are tagged in accordance with Deno releases. So, for example, the v0.53.0 tag is guaranteed to work with Deno v0.53.0. You can link to v0.53.0 using the URL ",[677,112591,112592],{"href":112592,"rel":112593},"https://deno.land/std@v0.53.0/",[681],". Not specifying a tag will link to the master branch. It is strongly recommended that you link to tagged releases to avoid unintended updates.",[28831,112596,112598],{"id":112597},"uuid-example","UUID Example",[22,112600,112601],{},"If you wanted to use UUID from the standard library the following example would always pull the latest version from master.",[52,112603,112605],{"className":110167,"code":112604,"language":50401,"meta":57,"style":57},"import { v4 } from \"https://deno.land/std/uuid/mod.ts\";\n\n// Generate a v4 uuid\nconst myUUID = v4.generate();\n\n// Validate a v4 uuid\nconst isValid = v4.validate(myUUID);\n\nconsole.log(isValid);\n",[59,112606,112607,112621,112625,112630,112647,112651,112656,112672,112676],{"__ignoreMap":57},[62,112608,112609,112611,112614,112616,112619],{"class":64,"line":65},[62,112610,27875],{"class":68},[62,112612,112613],{"class":72}," { v4 } ",[62,112615,3507],{"class":68},[62,112617,112618],{"class":1675}," \"https://deno.land/std/uuid/mod.ts\"",[62,112620,153],{"class":72},[62,112622,112623],{"class":64,"line":76},[62,112624,79],{"emptyLinePlaceholder":13},[62,112626,112627],{"class":64,"line":82},[62,112628,112629],{"class":85},"// Generate a v4 uuid\n",[62,112631,112632,112634,112637,112639,112642,112645],{"class":64,"line":89},[62,112633,110541],{"class":68},[62,112635,112636],{"class":149}," myUUID",[62,112638,2556],{"class":68},[62,112640,112641],{"class":72}," v4.",[62,112643,112644],{"class":122},"generate",[62,112646,822],{"class":72},[62,112648,112649],{"class":64,"line":95},[62,112650,79],{"emptyLinePlaceholder":13},[62,112652,112653],{"class":64,"line":101},[62,112654,112655],{"class":85},"// Validate a v4 uuid\n",[62,112657,112658,112660,112662,112664,112666,112669],{"class":64,"line":107},[62,112659,110541],{"class":68},[62,112661,59723],{"class":149},[62,112663,2556],{"class":68},[62,112665,112641],{"class":72},[62,112667,112668],{"class":122},"validate",[62,112670,112671],{"class":72},"(myUUID);\n",[62,112673,112674],{"class":64,"line":113},[62,112675,79],{"emptyLinePlaceholder":13},[62,112677,112678,112680,112682],{"class":64,"line":129},[62,112679,110298],{"class":72},[62,112681,58271],{"class":122},[62,112683,112684],{"class":72},"(isValid);\n",[22,112686,112687],{},"You should be specific with the version number to avoid uninteded updates",[52,112689,112691],{"className":110167,"code":112690,"language":50401,"meta":57,"style":57},"import { v4 } from \"https://deno.land/std@0.53.0/uuid/mod.ts\";\n",[59,112692,112693],{"__ignoreMap":57},[62,112694,112695,112697,112699,112701,112704],{"class":64,"line":65},[62,112696,27875],{"class":68},[62,112698,112613],{"class":72},[62,112700,3507],{"class":68},[62,112702,112703],{"class":1675}," \"https://deno.land/std@0.53.0/uuid/mod.ts\"",[62,112705,153],{"class":72},[22,112707,112708,112709,2755],{},"Deno provides a standard library at ",[677,112710,112575],{"href":112575,"rel":112711},[681],[636,112713,111044],{"id":112714},"built-in-testing-library",[22,112716,112717],{},"Deno has a built-in test runner that you can use for testing JavaScript or TypeScript code. I for one think this helps to improve the developer experience. When you don't have to investigate what testing framework to use and the language can just provide one out as part of the runtime it might help developers write more tests. This doesn't mean that you can't go use your favorite testing framework but it does make it easier to test out of the box.",[22,112719,46273,112720,112723],{},[59,112721,112722],{},"HelloWorldTest.ts"," with the following code:",[52,112725,112727],{"className":110167,"code":112726,"language":50401,"meta":57,"style":57},"import { assertEquals } from \"https://deno.land/std/testing/asserts.ts\";\n\nDeno.test(\"hello world\", () => {\n const x = 1 + 2;\n assertEquals(x, 3);\n});\n",[59,112728,112729,112743,112747,112765,112782,112794],{"__ignoreMap":57},[62,112730,112731,112733,112736,112738,112741],{"class":64,"line":65},[62,112732,27875],{"class":68},[62,112734,112735],{"class":72}," { assertEquals } ",[62,112737,3507],{"class":68},[62,112739,112740],{"class":1675}," \"https://deno.land/std/testing/asserts.ts\"",[62,112742,153],{"class":72},[62,112744,112745],{"class":64,"line":76},[62,112746,79],{"emptyLinePlaceholder":13},[62,112748,112749,112752,112754,112756,112759,112761,112763],{"class":64,"line":82},[62,112750,112751],{"class":72},"Deno.",[62,112753,28137],{"class":122},[62,112755,2109],{"class":72},[62,112757,112758],{"class":1675},"\"hello world\"",[62,112760,27373],{"class":72},[62,112762,21525],{"class":68},[62,112764,126],{"class":72},[62,112766,112767,112769,112772,112774,112776,112778,112780],{"class":64,"line":89},[62,112768,85414],{"class":68},[62,112770,112771],{"class":149}," x",[62,112773,2556],{"class":68},[62,112775,22038],{"class":149},[62,112777,4507],{"class":68},[62,112779,26797],{"class":149},[62,112781,153],{"class":72},[62,112783,112784,112787,112790,112792],{"class":64,"line":95},[62,112785,112786],{"class":122}," assertEquals",[62,112788,112789],{"class":72},"(x, ",[62,112791,4472],{"class":149},[62,112793,1133],{"class":72},[62,112795,112796],{"class":64,"line":101},[62,112797,85531],{"class":72},[22,112799,112800,112801],{},"You can run the test from the command line using the command: ",[59,112802,112803],{},"deno test HelloWorldTest.ts",[52,112805,112807],{"className":1663,"code":112806,"language":1665,"meta":57,"style":57},"vega test $ deno test HelloWorldTest.ts\nCompile file:///Users/vega/dev/deno/test/.deno.test.ts\nrunning 1 tests\ntest hello world ... ok (3ms)\n\ntest result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out (3ms)\n\nvega test $\n",[59,112808,112809,112825,112832,112841,112859,112863,112910,112914],{"__ignoreMap":57},[62,112810,112811,112813,112816,112818,112820,112822],{"class":64,"line":65},[62,112812,111184],{"class":122},[62,112814,112815],{"class":1675}," test",[62,112817,111190],{"class":72},[62,112819,110983],{"class":1675},[62,112821,112815],{"class":1675},[62,112823,112824],{"class":1675}," HelloWorldTest.ts\n",[62,112826,112827,112829],{"class":64,"line":76},[62,112828,112114],{"class":122},[62,112830,112831],{"class":1675}," file:///Users/vega/dev/deno/test/.deno.test.ts\n",[62,112833,112834,112837,112839],{"class":64,"line":82},[62,112835,112836],{"class":122},"running",[62,112838,22038],{"class":149},[62,112840,111873],{"class":1675},[62,112842,112843,112845,112847,112850,112853,112856],{"class":64,"line":89},[62,112844,28137],{"class":149},[62,112846,86794],{"class":1675},[62,112848,112849],{"class":1675}," world",[62,112851,112852],{"class":1675}," ...",[62,112854,112855],{"class":1675}," ok",[62,112857,112858],{"class":72}," (3ms)\n",[62,112860,112861],{"class":64,"line":95},[62,112862,79],{"emptyLinePlaceholder":13},[62,112864,112865,112867,112870,112873,112875,112878,112880,112882,112884,112886,112888,112891,112893,112895,112898,112900,112902,112905,112908],{"class":64,"line":101},[62,112866,28137],{"class":149},[62,112868,112869],{"class":1675}," result:",[62,112871,112872],{"class":1675}," ok.",[62,112874,22038],{"class":149},[62,112876,112877],{"class":1675}," passed",[62,112879,1168],{"class":72},[62,112881,1130],{"class":122},[62,112883,59042],{"class":1675},[62,112885,1168],{"class":72},[62,112887,1130],{"class":122},[62,112889,112890],{"class":1675}," ignored",[62,112892,1168],{"class":72},[62,112894,1130],{"class":122},[62,112896,112897],{"class":1675}," measured",[62,112899,1168],{"class":72},[62,112901,1130],{"class":122},[62,112903,112904],{"class":1675}," filtered",[62,112906,112907],{"class":1675}," out",[62,112909,112858],{"class":72},[62,112911,112912],{"class":64,"line":107},[62,112913,79],{"emptyLinePlaceholder":13},[62,112915,112916,112918,112920],{"class":64,"line":113},[62,112917,111184],{"class":122},[62,112919,112815],{"class":1675},[62,112921,111354],{"class":72},[22,112923,112924,112925,112927],{},"The method ",[59,112926,109231],{}," came from the standard library and if you take a look at it there are a number of helpful assertions.",[22,112929,112930],{},[677,112931,112932],{"href":112932,"rel":112933},"https://deno.land/std/testing",[681],[636,112935,111050],{"id":112936},"promises-async-await",[22,112938,112939],{},"For a technology built for events, Node.js didn’t do a great job with them. It came out before the concepts of Promises or the async/await patterns, so had to come up with its own version. Deno takes full advantage of Promises and the standard library is asynchronous by default.",[22,112941,112942,112943,112949,112950,112952,112953,112955],{},"Deno also provides ",[677,112944,112947],{"href":112945,"rel":112946},"https://github.com/tc39/proposal-top-level-await",[681],[59,112948,110330],{}," which really helps remove some boilerplate code. In a Vanilla JavaScript if you want to use ",[59,112951,110383],{}," it needs to be wrapped in ",[59,112954,21516],{}," function.",[52,112957,112959],{"className":105863,"code":112958,"language":105865,"meta":57,"style":57},"async function loadUsers() {\n const users = await fetch(\"https://users.com\");\n}\n",[59,112960,112961,112972,112992],{"__ignoreMap":57},[62,112962,112963,112965,112967,112970],{"class":64,"line":65},[62,112964,21516],{"class":68},[62,112966,21929],{"class":68},[62,112968,112969],{"class":122}," loadUsers",[62,112971,206],{"class":72},[62,112973,112974,112976,112979,112981,112983,112985,112987,112990],{"class":64,"line":76},[62,112975,85414],{"class":68},[62,112977,112978],{"class":149}," users",[62,112980,2556],{"class":68},[62,112982,21707],{"class":68},[62,112984,21995],{"class":122},[62,112986,2109],{"class":72},[62,112988,112989],{"class":1675},"\"https://users.com\"",[62,112991,1133],{"class":72},[62,112993,112994],{"class":64,"line":82},[62,112995,379],{"class":72},[22,112997,106332,112998,113000,113001,112955],{},[59,112999,110330],{}," support you don't have to wrap this in ",[59,113002,21516],{},[52,113004,113006],{"className":105863,"code":113005,"language":105865,"meta":57,"style":57},"const users = await fetch(\"https://users.com\");\n",[59,113007,113008],{"__ignoreMap":57},[62,113009,113010,113012,113014,113016,113018,113020,113022,113024],{"class":64,"line":65},[62,113011,110541],{"class":68},[62,113013,112978],{"class":149},[62,113015,2556],{"class":68},[62,113017,21707],{"class":68},[62,113019,21995],{"class":122},[62,113021,2109],{"class":72},[62,113023,112989],{"class":1675},[62,113025,1133],{"class":72},[22,113027,113028,113029,113031],{},"But wait, isn't ",[59,113030,106982],{}," a Browser API?",[636,113033,111056],{"id":113034},"browser-compatible-apis-fetch-window-object",[22,113036,113037,113038,2749,113040,113043],{},"Deno has Brower Compatible API's like ",[59,113039,106982],{},[59,113041,113042],{},"Window"," object built into the runtime. This is important because as a JavaScript developer that already understands fetch you don't have to learn something new or install a third-party library. For APIs where a web standard already exists, like fetch for HTTP requests, Deno uses these rather than inventing a new proprietary API.",[22,113045,113046,113047,113049,113050,113052],{},"If you combine ",[59,113048,106982],{}," with ",[59,113051,110330],{}," it becomes simple and clean to make a request and get some data back.",[52,113054,113056],{"className":110167,"code":113055,"language":50401,"meta":57,"style":57},"const todos = await fetch(\n \"https://jsonplaceholder.typicode.com/todos\"\n).then(response => response.json());\nconsole.table(todos);\n",[59,113057,113058,113073,113078,113096],{"__ignoreMap":57},[62,113059,113060,113062,113065,113067,113069,113071],{"class":64,"line":65},[62,113061,110541],{"class":68},[62,113063,113064],{"class":149}," todos",[62,113066,2556],{"class":68},[62,113068,21707],{"class":68},[62,113070,21995],{"class":122},[62,113072,3301],{"class":72},[62,113074,113075],{"class":64,"line":76},[62,113076,113077],{"class":1675}," \"https://jsonplaceholder.typicode.com/todos\"\n",[62,113079,113080,113082,113084,113086,113088,113090,113092,113094],{"class":64,"line":82},[62,113081,15503],{"class":72},[62,113083,36912],{"class":122},[62,113085,2109],{"class":72},[62,113087,13389],{"class":889},[62,113089,85402],{"class":68},[62,113091,12760],{"class":72},[62,113093,3673],{"class":122},[62,113095,1091],{"class":72},[62,113097,113098,113100,113102],{"class":64,"line":89},[62,113099,110298],{"class":72},[62,113101,11922],{"class":122},[62,113103,113104],{"class":72},"(todos);\n",[22,113106,113107],{},[653,113108],{"alt":113109,"src":113110},"Brower Compatible Fetch API","/images/blog/2020/05/29/todos-table.png",[22,113112,113113],{},"The subset of Deno programs which are written completely in JavaScript and do not use the global Deno namespace (or feature test for it), ought to also be able to be run in a modern web browser without change. Deno 1.0 provides the following web-compatible APIs.",[915,113115,113116,113118,113121,113124,113127,113130,113133,113136,113139,113142,113145,113148,113151,113154,113157,113160,113163,113166,113169,113172,113174,113177,113180,113183,113185,113188,113191,113194,113197,113200,113203,113206,113209,113211,113214,113217,113219,113221,113224,113227],{},[37,113117,21036],{},[37,113119,113120],{},"atob",[37,113122,113123],{},"btoa",[37,113125,113126],{},"clearInterval",[37,113128,113129],{},"clearTimeout",[37,113131,113132],{},"dispatchEvent",[37,113134,113135],{},"fetch",[37,113137,113138],{},"queueMicrotask",[37,113140,113141],{},"removeEventListener",[37,113143,113144],{},"setInterval",[37,113146,113147],{},"setTimeout",[37,113149,113150],{},"AbortSignal",[37,113152,113153],{},"Blob",[37,113155,113156],{},"File",[37,113158,113159],{},"FormData",[37,113161,113162],{},"Headers",[37,113164,113165],{},"ReadableStream",[37,113167,113168],{},"Request",[37,113170,113171],{},"Response",[37,113173,46401],{},[37,113175,113176],{},"URLSearchParams",[37,113178,113179],{},"console",[37,113181,113182],{},"isConsoleInstance",[37,113184,46321],{},[37,113186,113187],{},"onload",[37,113189,113190],{},"onunload",[37,113192,113193],{},"self",[37,113195,113196],{},"window",[37,113198,113199],{},"AbortController",[37,113201,113202],{},"CustomEvent",[37,113204,113205],{},"DOMException",[37,113207,113208],{},"ErrorEvent",[37,113210,45499],{},[37,113212,113213],{},"EventTarget",[37,113215,113216],{},"MessageEvent",[37,113218,110588],{},[37,113220,110364],{},[37,113222,113223],{},"Worker",[37,113225,113226],{},"ImportMeta",[37,113228,46307],{},[636,113230,111062],{"id":113231},"ecmascript-modules-es-modules",[22,113233,113234],{},"This is probably the biggest fundamental difference between Node and Deno. Node created it's own module system called Common JS. The way this works is the code that is being executed and code that it depends on both live in the same physical location so the server can stop what its doing, load the dependency and pick up where it left off. This is known as synchronous module loading and because this is done from disk it is extremely efficient.",[22,113236,113237],{},"The browser works very different from this model. When we execute our code we are doing so on our user's device. Those devices are not on the same machine as it's dependencies, in fact they are far across the network. So when our code that is running and it needs to load a dependency it can't just stop what its doing because the process of fetching it across the network can be very slow.",[22,113239,113240],{},"Node and the browser are two different runtimes and Common JS could never work in the browser. So the browser needed its own module system and this is where the design specification for ES Modules came from. Deno uses the official ECMAScript module standard rather than legacy CommonJS.",[22,113242,113243,113244,113246],{},"When your application depends on another module you can bring it into a script using the ",[59,113245,27875],{}," keyword. The module is referenced using a URL or file path and includes a mandatory file extension.",[52,113248,113250],{"className":110167,"code":113249,"language":50401,"meta":57,"style":57},"import * as log from \"https://deno.land/std/log/mod.ts\";\nimport { serve } from \"https://deno.land/std/http/server.ts\";\nimport { foo } from \"./app.ts\";\n",[59,113251,113252,113270,113283],{"__ignoreMap":57},[62,113253,113254,113256,113258,113260,113263,113265,113268],{"class":64,"line":65},[62,113255,27875],{"class":68},[62,113257,65159],{"class":149},[62,113259,53662],{"class":68},[62,113261,113262],{"class":72}," log ",[62,113264,3507],{"class":68},[62,113266,113267],{"class":1675}," \"https://deno.land/std/log/mod.ts\"",[62,113269,153],{"class":72},[62,113271,113272,113274,113276,113278,113281],{"class":64,"line":76},[62,113273,27875],{"class":68},[62,113275,112262],{"class":72},[62,113277,3507],{"class":68},[62,113279,113280],{"class":1675}," \"https://deno.land/std/http/server.ts\"",[62,113282,153],{"class":72},[62,113284,113285,113287,113290,113292,113295],{"class":64,"line":82},[62,113286,27875],{"class":68},[62,113288,113289],{"class":72}," { foo } ",[62,113291,3507],{"class":68},[62,113293,113294],{"class":1675}," \"./app.ts\"",[62,113296,153],{"class":72},[28831,113298,113300],{"id":113299},"goodbye-node_modules","Goodbye 👋🏻 node_modules",[22,113302,113303,113304,113306],{},"You just saw an example of importing an ES Module directly from a URL or file path. This means that we no longer need a package manager like NPM (Node Package Manager) or the project bloat that comes along with the ",[59,113305,112220],{}," folder.",[22,113308,113309],{},[653,113310],{"alt":113311,"src":113312},"Node Modules","/images/blog/2020/05/29/node-modules.jpeg",[22,113314,113315],{},"This is probably going to be something that developers debate about the most when it comes to Deno. Rather than relying on a central repository, it is decentralized. This means that anyone can host any type of file on the web, anywhere. There are pros and cons to this and I will need more time to share my personal thoughts on this. If you're looking for a collection of Third Party Modules you can find them on the official Deno website.",[22,113317,113318],{},[677,113319,113320],{"href":113320,"rel":113321},"https://deno.land/x",[681],[636,113323,111068],{"id":113324},"built-in-tools",[22,113326,113327],{},"Another feature I am really excited about is the set of built-in tools. This is a collection of tools that will help improve the developer experience.",[915,113329,113330,113337,113344,113351,113358,113365,113372],{},[37,113331,113332,113333,113336],{},"bundler (",[59,113334,113335],{},"deno bundle",") will output a single JavaScript file, which includes all dependencies of the specified input. For example:",[37,113338,113339,113340,113343],{},"debugger (",[59,113341,113342],{},"--inspect, --inspect-brk",") will support debugging using Chrome Devtools or other clients that support the protocol (eg. VSCode).",[37,113345,113346,113347,113350],{},"dependency inspector (",[59,113348,113349],{},"deno info",") will inspect ES module and all of its dependencies.",[37,113352,113353,113354,113357],{},"documentation generator (",[59,113355,113356],{},"deno doc",") will generate documentation for Deno",[37,113359,113360,113361,113364],{},"formatter (",[59,113362,113363],{},"deno fmt",") a built-in code formatter that auto-formats TypeScript and JavaScript code.",[37,113366,113367,113368,113371],{},"test runner (",[59,113369,113370],{},"deno test",") a built-in test runner that you can use for testing JavaScript or TypeScript code.",[37,113373,113374,113375,113378],{},"linter (",[59,113376,113377],{},"deno lint",") coming soon",[26,113380,1499],{"id":1498},[22,113382,113383],{},"I know a lot of developers reading this might be asking the question \"Is this going to replace Node\". The short answer is no and I think that is probably the wrong question to be asking. Node is a well-established runtime and relied upon by many so It won't be going away anytime soon.",[22,113385,113386],{},"The question you should be asking yourself is \"Do the features in Deno solve any of the problems that I currently have with Node?\" and if the answer to that is yes or you just like learning something new, give Deno a look.",[1527,113388,113389],{},"html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .s4-Ip, html code.shiki .s4-Ip{--shiki-default:#6A737D;--shiki-dark:#6A737D;--shiki-sepia:#6A737D}html pre.shiki code .spoOn, html code.shiki .spoOn{--shiki-default:#E36209;--shiki-dark:#FFAB70;--shiki-sepia:#E36209}",{"title":57,"searchDepth":76,"depth":76,"links":113391},[113392,113393,113394,113399,113409],{"id":110998,"depth":76,"text":110999},{"id":111076,"depth":76,"text":111077},{"id":111113,"depth":76,"text":111013,"children":113395},[113396,113397,113398],{"id":111298,"depth":82,"text":111299},{"id":112034,"depth":82,"text":112035},{"id":110980,"depth":82,"text":112056},{"id":112188,"depth":76,"text":111019,"children":113400},[113401,113402,113403,113404,113405,113406,113407,113408],{"id":112226,"depth":82,"text":111027},{"id":112503,"depth":82,"text":104848},{"id":112563,"depth":82,"text":111038},{"id":112714,"depth":82,"text":111044},{"id":112936,"depth":82,"text":111050},{"id":113034,"depth":82,"text":111056},{"id":113231,"depth":82,"text":111062},{"id":113324,"depth":82,"text":111068},{"id":1498,"depth":76,"text":1499},{"_id":113411,"path":113412,"title":113413,"description":113414,"meta":113415,"body":113421},"content/blog/2020/05/16/website-redesign-lessons-learned.md","/blog/2020/05/16/website-redesign-lessons-learned","Lessons learned from redesigning my website","I spent the last couple of months redesigning my website and I would like to share with you some of the lessons I learned",{"slug":113416,"date":113417,"published":13,"tags":113418,"author":17,"cover":113420,"excerpt":-1},"website-redesign-lessons-learned","2020-05-16T17:24:14.760Z",[32877,105335,113419],"gridsome","./website-redesign-lessons-learned-cover.png",{"type":19,"value":113422,"toc":113941},[113423,113426,113430,113450,113453,113462,113467,113471,113474,113477,113480,113483,113496,113499,113502,113505,113508,113516,113520,113527,113622,113625,113630,113637,113699,113702,113796,113802,113850,113853,113856,113859,113862,113867,113870,113873,113877,113885,113897,113900,113905,113908,113917,113924,113926,113938],[22,113424,113425],{},"A couple of months ago I decided it was time to update my website. I normally get this feeling every couple of years but this time it was different. I liked the look and feel but there were a list of things that I wanted to improve on.",[26,113427,113429],{"id":113428},"areas-of-improvement","Areas of improvement",[915,113431,113432,113435,113438,113441,113444,113447],{},[37,113433,113434],{},"Design",[37,113436,113437],{},"Removing CSS frameworks",[37,113439,113440],{},"Home Page Layout",[37,113442,113443],{},"Dark Mode",[37,113445,113446],{},"Responsive Design",[37,113448,113449],{},"Lighthouse Scores",[636,113451,113434],{"id":113452},"design",[22,113454,113455,113456,113461],{},"I am not a designer but I like to wear this hat from time to time and pretend I am one. I have gravitated towards ",[677,113457,113460],{"href":113458,"rel":113459},"https://www.adobe.com/products/xd.html",[681],"Adobe XD"," and I have been using it for a lot of my projects lately. I find it's really easy to use and it allows me prototype my application quickly. Once I have something I like its really easy for me to grab the colors, fonts and assets and build out the design in HTML & CSS. Below are some artboards from a couple of months ago when I decided to start redesigning the site.",[22,113463,113464],{},[653,113465],{"alt":113460,"src":113466},"/images/blog/2020/05/16/danvega_dev_adobexd.png",[28831,113468,113470],{"id":113469},"lesson-learned","Lesson Learned",[22,113472,113473],{},"It might seem like extra work up front to sit down and come up with a design but it pays off in the long run. When I know what I need to build I spend so much less time in development. Looking at a design I know what content is needed so I start with my HTML structure. With my content in place with the semantic structure I use CSS to make my look and feel match my design.",[636,113475,113437],{"id":113476},"removing-css-frameworks",[22,113478,113479],{},"The previous version of my website used a CSS framework called Bulma. I need to make something clear and say that I have absolutely nothing bad to say about Bulma. If I had to select a CSS framework not named Tailwind CSS Bulma would be my framework of choice. I didn't know Bluma when I started using it and that caused a lot of my problems. If you combine that with a lack of CSS foundational knowledge I had a pretty messy codebase.",[22,113481,113482],{},"Now that I understand layouts in CSS and how to accomplish different designs I find component-based frameworks less useful. In the past few years my frontend skills have gotten better and I thought this was a great time to put them to use. My redesign makes use of a variety of tools that make frontend development a lot of fun",[915,113484,113485,113488,113491,113494],{},[37,113486,113487],{},"CSS Variables",[37,113489,113490],{},"Flexbox",[37,113492,113493],{},"CSS Grid",[37,113495,113446],{},[28831,113497,113470],{"id":113498},"lesson-learned-1",[22,113500,113501],{},"Learn the foundations of HTML & CSS. I have been building websites for 20+ years and a lot has changed in the last 5 years. I am constantly hearing the same from other developers who have been doing this for a long time. Learn modern techniques like Flexbox & CSS Grid, they are both awesome and a real joy to work with.",[636,113503,113440],{"id":113504},"home-page-layout",[22,113506,113507],{},"My previous home page was really boring and I just wanted to add some style to it. I started messing around with some different designs and one thing that always stuck out to me with a total \"cool factor\" were layouts that had some type of wave or diagonal design. I came up with the wave on the home page and now it was time to implement it.",[22,113509,113510,113511,2755],{},"I attempted to figure this out on my own and failed a few times. I was trying to use an image that I exported from XD as a background image and had issues with it not being responsive and not working well in dark mode. I finally did some digging around and came across this ",[677,113512,113515],{"href":113513,"rel":113514},"https://blog.prototypr.io/how-to-add-svg-waves-to-your-next-web-project-b720efe1c692",[681],"great tutorial by Richard Zimerman",[28831,113517,113519],{"id":113518},"lessons-learned","Lessons Learned",[22,113521,113522,113523,113526],{},"The trick here was using an SVG and setting it to ",[59,113524,113525],{},"display:block",". I am still pretty new to using SVG images but I am finding them really useful. In this case I combined CSS custom properties with an SVG and now I have the header for the home page that looks nice, is responsive and looks good in dark mode!",[52,113528,113530],{"className":32875,"code":113529,"language":32877,"meta":57,"style":57},".wave-container {\n position: relative;\n background: var(--home-header-background);\n color: #4a4a4a;\n overflow: hidden;\n}\n.wave-container > svg {\n display: block;\n}\n",[59,113531,113532,113539,113551,113567,113579,113590,113594,113606,113618],{"__ignoreMap":57},[62,113533,113534,113537],{"class":64,"line":65},[62,113535,113536],{"class":122},".wave-container",[62,113538,126],{"class":72},[62,113540,113541,113544,113546,113549],{"class":64,"line":76},[62,113542,113543],{"class":149}," position",[62,113545,3696],{"class":72},[62,113547,113548],{"class":149},"relative",[62,113550,153],{"class":72},[62,113552,113553,113556,113558,113560,113562,113565],{"class":64,"line":82},[62,113554,113555],{"class":149}," background",[62,113557,3696],{"class":72},[62,113559,18226],{"class":149},[62,113561,2109],{"class":72},[62,113563,113564],{"class":889},"--home-header-background",[62,113566,1133],{"class":72},[62,113568,113569,113572,113574,113577],{"class":64,"line":89},[62,113570,113571],{"class":149}," color",[62,113573,3696],{"class":72},[62,113575,113576],{"class":149},"#4a4a4a",[62,113578,153],{"class":72},[62,113580,113581,113584,113586,113588],{"class":64,"line":95},[62,113582,113583],{"class":149}," overflow",[62,113585,3696],{"class":72},[62,113587,30177],{"class":149},[62,113589,153],{"class":72},[62,113591,113592],{"class":64,"line":101},[62,113593,379],{"class":72},[62,113595,113596,113598,113601,113604],{"class":64,"line":107},[62,113597,113536],{"class":122},[62,113599,113600],{"class":68}," >",[62,113602,113603],{"class":1780}," svg",[62,113605,126],{"class":72},[62,113607,113608,113611,113613,113616],{"class":64,"line":113},[62,113609,113610],{"class":149}," display",[62,113612,3696],{"class":72},[62,113614,113615],{"class":149},"block",[62,113617,153],{"class":72},[62,113619,113620],{"class":64,"line":129},[62,113621,379],{"class":72},[636,113623,113443],{"id":113624},"dark-mode",[22,113626,113627],{},[653,113628],{"alt":113443,"src":113629},"/images/blog/2020/05/16/darkmode.png",[22,113631,113632,113633,113636],{},"I have always loved the idea of having a dark mode on my website. I don't know how much my readers were asking for it but this is my website and I wanted to add it 🤷♂️. I have used CSS Custom Properties (variables) before but maybe not at this scale. The way this works is by setting some custom properties on the ",[59,113634,113635],{},":root"," which will be your default light theme. For each property you create you will create a dark theme property as well.",[52,113638,113640],{"className":32875,"code":113639,"language":32877,"meta":57,"style":57},":root {\n --background: white;\n}\n\n[data-theme=\"dark\"] {\n --background: black;\n}\n",[59,113641,113642,113648,113660,113664,113668,113684,113695],{"__ignoreMap":57},[62,113643,113644,113646],{"class":64,"line":65},[62,113645,113635],{"class":122},[62,113647,126],{"class":72},[62,113649,113650,113653,113655,113658],{"class":64,"line":76},[62,113651,113652],{"class":889}," --background",[62,113654,3696],{"class":72},[62,113656,113657],{"class":149},"white",[62,113659,153],{"class":72},[62,113661,113662],{"class":64,"line":82},[62,113663,379],{"class":72},[62,113665,113666],{"class":64,"line":89},[62,113667,79],{"emptyLinePlaceholder":13},[62,113669,113670,113673,113676,113678,113681],{"class":64,"line":95},[62,113671,113672],{"class":72},"[",[62,113674,113675],{"class":122},"data-theme",[62,113677,146],{"class":68},[62,113679,113680],{"class":1675},"\"dark\"",[62,113682,113683],{"class":72},"] {\n",[62,113685,113686,113688,113690,113693],{"class":64,"line":101},[62,113687,113652],{"class":889},[62,113689,3696],{"class":72},[62,113691,113692],{"class":149},"black",[62,113694,153],{"class":72},[62,113696,113697],{"class":64,"line":107},[62,113698,379],{"class":72},[22,113700,113701],{},"Now in your CSS instead of setting the background color to white or black you refer to the custom property",[52,113703,113705],{"className":32875,"code":113704,"language":32877,"meta":57,"style":57},"body {\n margin: 0;\n padding: 0;\n background-color: var(--background);\n border-top: 8px solid var(--bright-blue);\n font-family: \"Roboto Slab\", serif;\n}\n",[59,113706,113707,113713,113724,113735,113751,113775,113792],{"__ignoreMap":57},[62,113708,113709,113711],{"class":64,"line":65},[62,113710,11414],{"class":1780},[62,113712,126],{"class":72},[62,113714,113715,113718,113720,113722],{"class":64,"line":76},[62,113716,113717],{"class":149}," margin",[62,113719,3696],{"class":72},[62,113721,1130],{"class":149},[62,113723,153],{"class":72},[62,113725,113726,113729,113731,113733],{"class":64,"line":82},[62,113727,113728],{"class":149}," padding",[62,113730,3696],{"class":72},[62,113732,1130],{"class":149},[62,113734,153],{"class":72},[62,113736,113737,113740,113742,113744,113746,113749],{"class":64,"line":89},[62,113738,113739],{"class":149}," background-color",[62,113741,3696],{"class":72},[62,113743,18226],{"class":149},[62,113745,2109],{"class":72},[62,113747,113748],{"class":889},"--background",[62,113750,1133],{"class":72},[62,113752,113753,113756,113758,113760,113762,113765,113768,113770,113773],{"class":64,"line":95},[62,113754,113755],{"class":149}," border-top",[62,113757,3696],{"class":72},[62,113759,26743],{"class":149},[62,113761,30191],{"class":68},[62,113763,113764],{"class":149}," solid",[62,113766,113767],{"class":149}," var",[62,113769,2109],{"class":72},[62,113771,113772],{"class":889},"--bright-blue",[62,113774,1133],{"class":72},[62,113776,113777,113780,113782,113785,113787,113790],{"class":64,"line":101},[62,113778,113779],{"class":149}," font-family",[62,113781,3696],{"class":72},[62,113783,113784],{"class":1675},"\"Roboto Slab\"",[62,113786,976],{"class":72},[62,113788,113789],{"class":149},"serif",[62,113791,153],{"class":72},[62,113793,113794],{"class":64,"line":107},[62,113795,379],{"class":72},[22,113797,113798,113799,113801],{},"Next, I setup some icons using Font Awesome and by default the moon is displayed. When you click on that icon you are enabling dark mode. A little custom JavaScript will set the ",[59,113800,113675],{}," to dark:",[52,113803,113805],{"className":105863,"code":113804,"language":105865,"meta":57,"style":57},"if (document.documentElement.getAttribute(\"data-theme\") === null) {\n document.documentElement.setAttribute(\"data-theme\", \"dark\");\n}\n",[59,113806,113807,113829,113846],{"__ignoreMap":57},[62,113808,113809,113811,113814,113816,113818,113821,113823,113825,113827],{"class":64,"line":65},[62,113810,34116],{"class":68},[62,113812,113813],{"class":72}," (document.documentElement.",[62,113815,16806],{"class":122},[62,113817,2109],{"class":72},[62,113819,113820],{"class":1675},"\"data-theme\"",[62,113822,5024],{"class":72},[62,113824,21072],{"class":68},[62,113826,13324],{"class":149},[62,113828,768],{"class":72},[62,113830,113831,113834,113836,113838,113840,113842,113844],{"class":64,"line":76},[62,113832,113833],{"class":72}," document.documentElement.",[62,113835,79209],{"class":122},[62,113837,2109],{"class":72},[62,113839,113820],{"class":1675},[62,113841,976],{"class":72},[62,113843,113680],{"class":1675},[62,113845,1133],{"class":72},[62,113847,113848],{"class":64,"line":82},[62,113849,379],{"class":72},[28831,113851,113519],{"id":113852},"lessons-learned-1",[22,113854,113855],{},"If you are going to implement something like dark mode do it right from the start. If you notice yourself defining colors anywhere that don't use a CSS property you probably need to think about doing so. This is something I need to go back and fix up. I feel like I have too many properties defined and can probably condense this down.",[636,113857,113446],{"id":113858},"responsive-design",[22,113860,113861],{},"I know that both myself and my audience use my website on a variety of form factors so making sure my design was responsive was important. As you can tell by XD designs I didn't have a mobile-first approach and that caused me some issues along the way. It's important to always be thinking about how a layout or design element will scale up or down. I am really thankful for tools like Flexbox and CSS Grid which are really helpful in creating responsive designs.",[22,113863,113864],{},[653,113865],{"alt":113446,"src":113866},"/images/blog/2020/05/16/responsive-design.png",[28831,113868,113519],{"id":113869},"lessons-learned-2",[22,113871,113872],{},"When I design a layout in Adobe XD I need to make sure I have a plan for my responsive designs. The term mobile-first means that your layout starts out with a mobile layout and scales up. This ensures that the mobile design is not just an after thought. Take some time to look at the analytics and understand what devices your visitors are using.",[636,113874,113876],{"id":113875},"lighthouse-audits","Lighthouse Audits",[22,113878,113879,113880,113884],{},"I knew that once I finished my site I wanted to take a look at the ",[677,113881,113876],{"href":113882,"rel":113883},"https://web.dev/measure/",[681]," and try to improve scores where I could. If you aren't aware the Google Developer Tools has a tab called Audits where you can check your site for:",[915,113886,113887,113889,113892,113894],{},[37,113888,14118],{},[37,113890,113891],{},"Accessibility",[37,113893,10827],{},[37,113895,113896],{},"SEO",[22,113898,113899],{},"Right now the site is doing well on some page but still needs some work on others. My advice here is let these audits guide you to improve your site but do not obsess over them.",[22,113901,113902],{},[653,113903],{"alt":113876,"src":113904},"/images/blog/2020/05/16/lighthouse-audits.png",[28831,113906,113519],{"id":113907},"lessons-learned-3",[22,113909,113910,113911,113916],{},"The first big lesson I learned here was that pulling in the entire Font Awesome library for \u003C 10 icons was a big no no. I mean I knew this but I needed to find a way to resolve this and the audit gave me a big kick in the rear to do so. I was just going to grab the icons I needed but what happens if I want to use more in the future? There had to be a way to pull in just the icons I needed and thanks to the Gridsome documentation I came across a wonderful ",[677,113912,113915],{"href":113913,"rel":113914},"https://github.com/FortAwesome/vue-fontawesome",[681],"library from Fort Awesome"," for using Font Awesome in Vue.",[22,113918,113919,113920,113923],{},"I still have some work to do here but I am trying to take my own advice and not obsess over it. I think there are two big things that would really help me out. Right now on the home page all of the images for recent posts are pulling from GraphQL. There is a way to resize them but unlike when you use a ",[59,113921,113922],{},"\u003Cg-image>"," tag this isn't lazy loading the images and I need to figure out a way around this. The other problem is all of my ConvertKit forms are pulled in via JavaScript. I would like to just create these forms myself so I can make sure they are accessible and at the same time it would reduce some page weight.",[636,113925,1499],{"id":1498},[22,113927,113928,113929,113934,113935,2755],{},"I have had a lot of fun working on the redesign for my website and I hope it shows. If you have any questions about anything I did here you can check out the ",[677,113930,113933],{"href":113931,"rel":113932},"https://github.com/danvega/danvega-dev",[681],"source code"," for it here. If you would like me to explain anything in more detail please leave me a comment below or reach out to me on ",[677,113936,86336],{"href":93581,"rel":113937},[681],[1527,113939,113940],{},"html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}html pre.shiki code .spoOn, html code.shiki .spoOn{--shiki-default:#E36209;--shiki-dark:#FFAB70;--shiki-sepia:#E36209}html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .suaqH, html code.shiki .suaqH{--shiki-default:#22863A;--shiki-dark:#85E89D;--shiki-sepia:#22863A}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}",{"title":57,"searchDepth":76,"depth":76,"links":113942},[113943],{"id":113428,"depth":76,"text":113429,"children":113944},[113945,113946,113947,113948,113949,113950,113951],{"id":113452,"depth":82,"text":113434},{"id":113476,"depth":82,"text":113437},{"id":113504,"depth":82,"text":113440},{"id":113624,"depth":82,"text":113443},{"id":113858,"depth":82,"text":113446},{"id":113875,"depth":82,"text":113876},{"id":1498,"depth":82,"text":1499},{"_id":113953,"path":113954,"title":113955,"description":113956,"meta":113957,"body":113962},"content/blog/2020/03/09/spring-unit-vs-integration-test.md","/blog/2020/03/09/spring-unit-vs-integration-test","Spring Boot Testing Basics: How to Unit Test & Integration Test REST Controllers","In this article, we're going to dive into the world of testing in Spring Boot, specifically looking at unit tests and integration tests.",{"slug":113958,"date":113959,"published":13,"tags":113960,"author":17,"cover":113961,"excerpt":-1},"spring-unit-vs-integration-test","2020-03-09T10:00:00.000Z",[2925],"./unit-vs-int.jpeg",{"type":19,"value":113963,"toc":114572},[113964,113968,113971,113974,113978,113987,113993,114051,114069,114073,114079,114095,114097,114199,114208,114214,114218,114223,114331,114341,114345,114351,114354,114378,114381,114556,114562,114564,114567,114570],[26,113965,113967],{"id":113966},"demystifying-unit-tests-and-integration-tests-in-spring-boot","Demystifying Unit Tests and Integration Tests in Spring Boot",[22,113969,113970],{},"Hello, friends! In this article, we're going to dive into the world of testing in Spring Boot, specifically looking at unit tests and integration tests. Is there a difference between the two? Should we care? Let's find out!",[22,113972,113973],{},"This topic was sparked by a tweet I sent out recently, pleading with people to stop calling certain tests \"unit tests\" when they involve Spring components such as Dispatcher Servlet and the request/response lifecycle. To me, these are more like integration tests, albeit isolated ones. So, our goal today is to create a simple REST controller, perform a proper unit test, and then build upon that to create an integration test as well.",[26,113975,113977],{"id":113976},"creating-a-simple-rest-controller","Creating a Simple REST Controller",[22,113979,113980,113981,113984,113985,27738],{},"Before we get started with writing tests, let's create a very simple Spring Boot application with a REST controller. Head over to the ",[677,113982,24606],{"href":2901,"rel":113983},[681]," and create a project. For this example, all we need is the ",[646,113986,78714],{},[22,113988,113989,113990,6175],{},"Let's create a basic ",[59,113991,113992],{},"HelloController",[52,113994,113996],{"className":54,"code":113995,"language":56,"meta":57,"style":57},"public class HelloController {\n\n public String hello(String name) {\n return String.format(\"Hello, %s\", name);\n }\n}\n",[59,113997,113998,114009,114013,114027,114043,114047],{"__ignoreMap":57},[62,113999,114000,114002,114004,114007],{"class":64,"line":65},[62,114001,116],{"class":68},[62,114003,119],{"class":68},[62,114005,114006],{"class":122}," HelloController",[62,114008,126],{"class":72},[62,114010,114011],{"class":64,"line":76},[62,114012,79],{"emptyLinePlaceholder":13},[62,114014,114015,114017,114019,114021,114023,114025],{"class":64,"line":82},[62,114016,194],{"class":68},[62,114018,2469],{"class":72},[62,114020,82397],{"class":122},[62,114022,1049],{"class":72},[62,114024,3107],{"class":889},[62,114026,768],{"class":72},[62,114028,114029,114031,114033,114035,114037,114040],{"class":64,"line":89},[62,114030,360],{"class":68},[62,114032,41152],{"class":72},[62,114034,61567],{"class":122},[62,114036,2109],{"class":72},[62,114038,114039],{"class":1675},"\"Hello, %s\"",[62,114041,114042],{"class":72},", name);\n",[62,114044,114045],{"class":64,"line":95},[62,114046,223],{"class":72},[62,114048,114049],{"class":64,"line":101},[62,114050,379],{"class":72},[22,114052,114053,114054,114056,114057,114059,114060,114063,114064,34867,114066,114068],{},"This controller has one method, ",[59,114055,86629],{},", which takes a string argument ",[59,114058,3107],{}," and returns a formatted string: ",[59,114061,114062],{},"\"Hello, \" + name",". Notice that we haven't added any Spring annotations yet, such as ",[59,114065,40266],{},[59,114067,56483],{},". We'll get to that later.",[26,114070,114072],{"id":114071},"writing-a-unit-test","Writing a Unit Test",[22,114074,114075,114076,114078],{},"To create a unit test for our ",[59,114077,113992],{},", follow these steps:",[34,114080,114081,114086,114092],{},[37,114082,114083,114084,2755],{},"Create a test class for ",[59,114085,113992],{},[37,114087,114088,114089,114091],{},"Create an instance of ",[59,114090,113992],{}," and invoke its methods.",[37,114093,114094],{},"Use assertions to check the output of the methods.",[22,114096,10174],{},[52,114098,114100],{"className":54,"code":114099,"language":56,"meta":57,"style":57},"import org.junit.jupiter.api.Test;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\nclass HelloControllerTest {\n\n @Test\n void hello() {\n HelloController controller = new HelloController();\n String response = controller.hello(\"world\");\n assertEquals(\"Hello, world\", response);\n }\n}\n",[59,114101,114102,114108,114116,114120,114129,114133,114139,114147,114160,114179,114191,114195],{"__ignoreMap":57},[62,114103,114104,114106],{"class":64,"line":65},[62,114105,27875],{"class":68},[62,114107,29087],{"class":72},[62,114109,114110,114112,114114],{"class":64,"line":76},[62,114111,27875],{"class":68},[62,114113,2101],{"class":68},[62,114115,92463],{"class":72},[62,114117,114118],{"class":64,"line":82},[62,114119,79],{"emptyLinePlaceholder":13},[62,114121,114122,114124,114127],{"class":64,"line":89},[62,114123,11671],{"class":68},[62,114125,114126],{"class":122}," HelloControllerTest",[62,114128,126],{"class":72},[62,114130,114131],{"class":64,"line":95},[62,114132,79],{"emptyLinePlaceholder":13},[62,114134,114135,114137],{"class":64,"line":101},[62,114136,2143],{"class":72},[62,114138,11705],{"class":68},[62,114140,114141,114143,114145],{"class":64,"line":107},[62,114142,11710],{"class":68},[62,114144,86794],{"class":122},[62,114146,206],{"class":72},[62,114148,114149,114152,114154,114156,114158],{"class":64,"line":113},[62,114150,114151],{"class":72}," HelloController controller ",[62,114153,146],{"class":68},[62,114155,466],{"class":68},[62,114157,114006],{"class":122},[62,114159,822],{"class":72},[62,114161,114162,114165,114167,114170,114172,114174,114177],{"class":64,"line":129},[62,114163,114164],{"class":72}," String response ",[62,114166,146],{"class":68},[62,114168,114169],{"class":72}," controller.",[62,114171,82397],{"class":122},[62,114173,2109],{"class":72},[62,114175,114176],{"class":1675},"\"world\"",[62,114178,1133],{"class":72},[62,114180,114181,114183,114185,114188],{"class":64,"line":134},[62,114182,29183],{"class":122},[62,114184,2109],{"class":72},[62,114186,114187],{"class":1675},"\"Hello, world\"",[62,114189,114190],{"class":72},", response);\n",[62,114192,114193],{"class":64,"line":156},[62,114194,223],{"class":72},[62,114196,114197],{"class":64,"line":161},[62,114198,379],{"class":72},[22,114200,114201,114202,114204,114205,114207],{},"In this test, we're simply creating an instance of ",[59,114203,113992],{},", calling its ",[59,114206,86629],{}," method with the input \"world,\" and checking that the expected output is \"Hello, world.\" We don't involve any Spring components in this test, so this is a pure unit test.",[22,114209,114210,114211,114213],{},"The purpose of a unit test is to validate that each unit of the software performs as designed. A unit is the smallest testable part of any software. In our case, the smallest unit is the ",[59,114212,86629],{}," method in our controller. This test ensures that the method works as expected when given a particular input.",[26,114215,114217],{"id":114216},"adding-spring-components","Adding Spring Components",[22,114219,114220,114221,1266],{},"Now that we have a working unit test, let's add some Spring components to our ",[59,114222,113992],{},[52,114224,114226],{"className":54,"code":114225,"language":56,"meta":57,"style":57},"import org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class HelloController {\n\n @GetMapping(\"/hello\")\n public String hello(@RequestParam(defaultValue = \"world\") String name) {\n return String.format(\"Hello, %s\", name);\n }\n}\n",[59,114227,114228,114234,114241,114247,114251,114257,114267,114271,114283,114309,114323,114327],{"__ignoreMap":57},[62,114229,114230,114232],{"class":64,"line":65},[62,114231,27875],{"class":68},[62,114233,27885],{"class":72},[62,114235,114236,114238],{"class":64,"line":76},[62,114237,27875],{"class":68},[62,114239,114240],{"class":72}," org.springframework.web.bind.annotation.RequestParam;\n",[62,114242,114243,114245],{"class":64,"line":82},[62,114244,27875],{"class":68},[62,114246,27892],{"class":72},[62,114248,114249],{"class":64,"line":89},[62,114250,79],{"emptyLinePlaceholder":13},[62,114252,114253,114255],{"class":64,"line":95},[62,114254,942],{"class":72},[62,114256,2342],{"class":68},[62,114258,114259,114261,114263,114265],{"class":64,"line":101},[62,114260,116],{"class":68},[62,114262,119],{"class":68},[62,114264,114006],{"class":122},[62,114266,126],{"class":72},[62,114268,114269],{"class":64,"line":107},[62,114270,79],{"emptyLinePlaceholder":13},[62,114272,114273,114275,114277,114279,114281],{"class":64,"line":113},[62,114274,2143],{"class":72},[62,114276,2548],{"class":68},[62,114278,2109],{"class":72},[62,114280,105749],{"class":1675},[62,114282,2212],{"class":72},[62,114284,114285,114287,114289,114291,114293,114295,114297,114299,114301,114303,114305,114307],{"class":64,"line":129},[62,114286,194],{"class":68},[62,114288,2469],{"class":72},[62,114290,82397],{"class":122},[62,114292,2475],{"class":72},[62,114294,2591],{"class":68},[62,114296,2109],{"class":72},[62,114298,17908],{"class":149},[62,114300,2556],{"class":68},[62,114302,86753],{"class":1675},[62,114304,17916],{"class":72},[62,114306,3107],{"class":889},[62,114308,768],{"class":72},[62,114310,114311,114313,114315,114317,114319,114321],{"class":64,"line":134},[62,114312,360],{"class":68},[62,114314,41152],{"class":72},[62,114316,61567],{"class":122},[62,114318,2109],{"class":72},[62,114320,114039],{"class":1675},[62,114322,114042],{"class":72},[62,114324,114325],{"class":64,"line":156},[62,114326,223],{"class":72},[62,114328,114329],{"class":64,"line":161},[62,114330,379],{"class":72},[22,114332,114333,114334,976,114336,4201,114338,114340],{},"We've added the ",[59,114335,40266],{},[59,114337,48908],{},[59,114339,100863],{}," annotations. Now our controller is ready to accept HTTP GET requests at the \"/hello\" endpoint and respond with the appropriate message.",[26,114342,114344],{"id":114343},"writing-an-integration-test","Writing an Integration Test",[22,114346,114347,114348,114350],{},"With the Spring components in place, it's time to write an integration test for our ",[59,114349,113992],{},". Unlike a unit test, an integration test involves the Spring framework and tests how the various components work together.",[22,114352,114353],{},"To create an integration test, follow these steps:",[34,114355,114356,114365,114372],{},[37,114357,114083,114358,89163,114360,19931,114362,2755],{},[59,114359,113992],{},[59,114361,55195],{},[59,114363,114364],{},"@ExtendWith(SpringExtension.class)",[37,114366,114367,114368,114371],{},"Autowire a ",[59,114369,114370],{},"MockMvc"," instance.",[37,114373,114374,114375,114377],{},"Perform HTTP requests using the ",[59,114376,114370],{}," instance and check the expected output.",[22,114379,114380],{},"Here's an example integration test:",[52,114382,114384],{"className":54,"code":114383,"language":56,"meta":57,"style":57},"import org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.test.web.servlet.MockMvc;\n\n@WebMvcTest(HelloController.class)\n@ExtendWith(SpringExtension.class)\nclass HelloIntegrationTest {\n\n @Autowired\n private MockMvc mockMvc;\n\n @Test\n void hello() throws Exception {\n mockMvc.perform(get(\"/hello\"))\n .andExpect(status().isOk())\n .andExpect(content().string(\"Hello, world\"));\n }\n}\n",[59,114385,114386,114392,114398,114404,114411,114418,114425,114429,114438,114447,114456,114460,114466,114473,114477,114483,114495,114512,114528,114548,114552],{"__ignoreMap":57},[62,114387,114388,114390],{"class":64,"line":65},[62,114389,27875],{"class":68},[62,114391,29087],{"class":72},[62,114393,114394,114396],{"class":64,"line":76},[62,114395,27875],{"class":68},[62,114397,92436],{"class":72},[62,114399,114400,114402],{"class":64,"line":82},[62,114401,27875],{"class":68},[62,114403,86973],{"class":72},[62,114405,114406,114408],{"class":64,"line":89},[62,114407,27875],{"class":68},[62,114409,114410],{"class":72}," org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;\n",[62,114412,114413,114415],{"class":64,"line":95},[62,114414,27875],{"class":68},[62,114416,114417],{"class":72}," org.springframework.test.context.junit.jupiter.SpringExtension;\n",[62,114419,114420,114422],{"class":64,"line":101},[62,114421,27875],{"class":68},[62,114423,114424],{"class":72}," org.springframework.test.web.servlet.MockMvc;\n",[62,114426,114427],{"class":64,"line":107},[62,114428,79],{"emptyLinePlaceholder":13},[62,114430,114431,114433,114435],{"class":64,"line":113},[62,114432,942],{"class":72},[62,114434,97866],{"class":68},[62,114436,114437],{"class":72},"(HelloController.class)\n",[62,114439,114440,114442,114444],{"class":64,"line":129},[62,114441,942],{"class":72},[62,114443,92501],{"class":68},[62,114445,114446],{"class":72},"(SpringExtension.class)\n",[62,114448,114449,114451,114454],{"class":64,"line":134},[62,114450,11671],{"class":68},[62,114452,114453],{"class":122}," HelloIntegrationTest",[62,114455,126],{"class":72},[62,114457,114458],{"class":64,"line":156},[62,114459,79],{"emptyLinePlaceholder":13},[62,114461,114462,114464],{"class":64,"line":161},[62,114463,2143],{"class":72},[62,114465,11687],{"class":68},[62,114467,114468,114470],{"class":64,"line":167},[62,114469,137],{"class":68},[62,114471,114472],{"class":72}," MockMvc mockMvc;\n",[62,114474,114475],{"class":64,"line":173},[62,114476,79],{"emptyLinePlaceholder":13},[62,114478,114479,114481],{"class":64,"line":179},[62,114480,2143],{"class":72},[62,114482,11705],{"class":68},[62,114484,114485,114487,114489,114491,114493],{"class":64,"line":185},[62,114486,11710],{"class":68},[62,114488,86794],{"class":122},[62,114490,5398],{"class":72},[62,114492,11501],{"class":68},[62,114494,11504],{"class":72},[62,114496,114497,114500,114502,114504,114506,114508,114510],{"class":64,"line":191},[62,114498,114499],{"class":72}," mockMvc.",[62,114501,97936],{"class":122},[62,114503,2109],{"class":72},[62,114505,11363],{"class":122},[62,114507,2109],{"class":72},[62,114509,105749],{"class":1675},[62,114511,5047],{"class":72},[62,114513,114514,114516,114518,114520,114522,114524,114526],{"class":64,"line":209},[62,114515,2418],{"class":72},[62,114517,97953],{"class":122},[62,114519,2109],{"class":72},[62,114521,55012],{"class":122},[62,114523,3229],{"class":72},[62,114525,98049],{"class":122},[62,114527,4460],{"class":72},[62,114529,114530,114532,114534,114536,114538,114540,114542,114544,114546],{"class":64,"line":220},[62,114531,2418],{"class":72},[62,114533,97953],{"class":122},[62,114535,2109],{"class":72},[62,114537,2230],{"class":122},[62,114539,3229],{"class":72},[62,114541,88059],{"class":122},[62,114543,2109],{"class":72},[62,114545,114187],{"class":1675},[62,114547,6979],{"class":72},[62,114549,114550],{"class":64,"line":226},[62,114551,223],{"class":72},[62,114553,114554],{"class":64,"line":231},[62,114555,379],{"class":72},[22,114557,114558,114559,114561],{},"In this test, we use the ",[59,114560,114370],{}," instance to perform an HTTP GET request to the \"/hello\" endpoint and check that the response is \"Hello, world.\" Since this test involves Spring components, it's considered an integration test.",[26,114563,1499],{"id":1498},[22,114565,114566],{},"In this article, we've covered the differences between unit tests and integration tests in a Spring Boot application. Unit tests are focused on testing the smallest units of functionality, while integration tests ensure that various components work together as expected. Both types of tests are valuable in ensuring the quality of your application, and it's important to know when to use each type.",[22,114568,114569],{},"Remember, it's not about what we call these tests, but rather understanding their purpose and how they fit into your testing strategy. Happy coding, and stay curious!",[1527,114571,34789],{},{"title":57,"searchDepth":76,"depth":76,"links":114573},[114574,114575,114576,114577,114578,114579],{"id":113966,"depth":76,"text":113967},{"id":113976,"depth":76,"text":113977},{"id":114071,"depth":76,"text":114072},{"id":114216,"depth":76,"text":114217},{"id":114343,"depth":76,"text":114344},{"id":1498,"depth":76,"text":1499},{"_id":114581,"path":114582,"title":114583,"description":114584,"meta":114585,"body":114590},"content/blog/2020/02/12/vue3-ref-vs-reactive.md","/blog/2020/02/12/vue3-ref-vs-reactive","Vue 3 Composition API: Ref vs Reactive","One question I keep hearing over and over is what method should I use to declare reactive data in the Vue 3 Composition API? In this article, I explain what ref() and reactive() are and try to answer that question.",{"slug":114586,"date":114587,"published":13,"tags":114588,"author":17,"cover":114589,"excerpt":-1},"vue3-ref-vs-reactive","2020-02-12T16:27:46.472Z",[105335],"./vue3-ref-vs-reactive-cover.png",{"type":19,"value":114591,"toc":116923},[114592,114595,114598,114606,114612,114615,114618,114622,114625,114721,114731,114735,114742,114840,114849,114983,114997,115164,115171,115175,115181,115202,115312,115318,115421,115438,115513,115528,115532,115538,115571,115577,115584,115588,115603,115616,115697,115713,115837,115841,115856,115861,115873,115984,115991,116191,116194,116198,116209,116212,116215,116402,116405,116539,116542,116655,116658,116801,116808,116902,116905,116907,116916,116920],[22,114593,114594],{},"At the time of writing this article, we are getting closer and closer to the release of Vue 3. I think what I am most excited about is to see how other developers embrace it and use it. While I have had a chance to play with it over the last few months I know that isn't the case for everyone.",[22,114596,114597],{},"The biggest feature coming to Vue 3 is the Composition API. This offers an alternative approach to creating components that is much different than the existing options API. I have no problem admitting that when I first saw it, I didn't get it. The more I use it though the more it just makes sense. While you won't go rewriting entire applications using the Composition API it will make you think about how create components and compose functionality going forward.",[22,114599,66859,114600,114605],{},[677,114601,114604],{"href":114602,"rel":114603},"https://www.danvega.dev/blog/2020/01/09/codemash-2020/",[681],"given a couple"," presentations on Vue 3 recently and one question that keeps coming up is when do I use Ref vs Reactive to declare a reactive property. I never had a great answer for this so over the past couple of weeks I set out to answer this question and this article is the result of that research.",[22,114607,114608],{},[653,114609],{"alt":114610,"src":114611},"New Technology","/images/blog/2020/02/12/christopher-gower-m_HRfLhgABo-unsplash.jpg",[22,114613,114614],{},"I would also like to point out that this is my opinion and please do not take this as \"the way\" things should be. This is how I am going to use Ref & Reactive until someone tells me otherwise or until I discover a better approach. With any new technology, I think that it takes some time to figure out how we use it and from there a best practice might emerge.",[22,114616,114617],{},"Before we get started I am going to assume that you have at least looked at the Composition API and understand the different components of it. This article is going to focus on Ref vs Reactive and not the mechanics of the Composition API. If you're interested in an in-depth tutorial on that please let me know.",[26,114619,114621],{"id":114620},"reactive-state-in-vue-2","Reactive State in Vue 2",[22,114623,114624],{},"To give this article a little context I want to quickly explore how to create reactive data in a Vue 2 application. When you want Vue to keep track of changes to data you need to declare that property inside of an object that is returned from the data function.",[52,114626,114628],{"className":15773,"code":114627,"language":15775,"meta":57,"style":57},"\u003Ctemplate>\n \u003Ch1>{{ title }}\u003C/h1>\n\u003C/template>\n\n\u003Cscript>\n export default {\n data() {\n return {\n title: \"Hello, Vue!\"\n };\n }\n };\n\u003C/script>\n",[59,114629,114630,114638,114651,114659,114663,114671,114680,114687,114693,114701,114705,114709,114713],{"__ignoreMap":57},[62,114631,114632,114634,114636],{"class":64,"line":65},[62,114633,760],{"class":72},[62,114635,105991],{"class":1780},[62,114637,1784],{"class":72},[62,114639,114640,114642,114644,114647,114649],{"class":64,"line":76},[62,114641,33056],{"class":72},[62,114643,4168],{"class":1780},[62,114645,114646],{"class":72},">{{ title }}\u003C/",[62,114648,4168],{"class":1780},[62,114650,1784],{"class":72},[62,114652,114653,114655,114657],{"class":64,"line":82},[62,114654,1818],{"class":72},[62,114656,105991],{"class":1780},[62,114658,1784],{"class":72},[62,114660,114661],{"class":64,"line":89},[62,114662,79],{"emptyLinePlaceholder":13},[62,114664,114665,114667,114669],{"class":64,"line":95},[62,114666,760],{"class":72},[62,114668,15846],{"class":1780},[62,114670,1784],{"class":72},[62,114672,114673,114676,114678],{"class":64,"line":101},[62,114674,114675],{"class":68}," export",[62,114677,106045],{"class":68},[62,114679,126],{"class":72},[62,114681,114682,114685],{"class":64,"line":107},[62,114683,114684],{"class":122}," data",[62,114686,206],{"class":72},[62,114688,114689,114691],{"class":64,"line":113},[62,114690,28884],{"class":68},[62,114692,126],{"class":72},[62,114694,114695,114698],{"class":64,"line":129},[62,114696,114697],{"class":72}," title: ",[62,114699,114700],{"class":1675},"\"Hello, Vue!\"\n",[62,114702,114703],{"class":64,"line":134},[62,114704,59009],{"class":72},[62,114706,114707],{"class":64,"line":156},[62,114708,223],{"class":72},[62,114710,114711],{"class":64,"line":161},[62,114712,82135],{"class":72},[62,114714,114715,114717,114719],{"class":64,"line":167},[62,114716,1818],{"class":72},[62,114718,15846],{"class":1780},[62,114720,1784],{"class":72},[22,114722,114723,114724,114727,114728,112955],{},"Under the hood Vue 2, looks at each property and uses ",[59,114725,114726],{},"Object.defineProperty()"," to create getters and setters for each piece of data it needs to keep track of. This is a basic explanation of the process but what I want to get across is that it is not magic. You can't just create data anywhere and expect Vue to keep track of it. You must follow the process of defining it in the ",[59,114729,114730],{},"data()",[26,114732,114734],{"id":114733},"ref-vs-reactive","Ref vs Reactive",[22,114736,114737,114738,114741],{},"With the Options API, we have to follow some rules when defining reactive data and the Composition API is no different. You can't just declare data and expect Vue to know that you would like it tracked for changes. In the following example, I have defined a title and returned that from the ",[59,114739,114740],{},"setup()"," function making it available in the template.",[52,114743,114745],{"className":15773,"code":114744,"language":15775,"meta":57,"style":57},"\u003Ctemplate>\n \u003Ch1>{{ title }}\u003C/h1>\n\u003C/template>\n\n\u003Cscript>\n export default {\n setup() {\n let title = \"Hello, Vue 3!\";\n return { title };\n }\n };\n\u003C/script>\n",[59,114746,114747,114755,114767,114775,114779,114787,114795,114802,114817,114824,114828,114832],{"__ignoreMap":57},[62,114748,114749,114751,114753],{"class":64,"line":65},[62,114750,760],{"class":72},[62,114752,105991],{"class":1780},[62,114754,1784],{"class":72},[62,114756,114757,114759,114761,114763,114765],{"class":64,"line":76},[62,114758,33056],{"class":72},[62,114760,4168],{"class":1780},[62,114762,114646],{"class":72},[62,114764,4168],{"class":1780},[62,114766,1784],{"class":72},[62,114768,114769,114771,114773],{"class":64,"line":82},[62,114770,1818],{"class":72},[62,114772,105991],{"class":1780},[62,114774,1784],{"class":72},[62,114776,114777],{"class":64,"line":89},[62,114778,79],{"emptyLinePlaceholder":13},[62,114780,114781,114783,114785],{"class":64,"line":95},[62,114782,760],{"class":72},[62,114784,15846],{"class":1780},[62,114786,1784],{"class":72},[62,114788,114789,114791,114793],{"class":64,"line":101},[62,114790,114675],{"class":68},[62,114792,106045],{"class":68},[62,114794,126],{"class":72},[62,114796,114797,114800],{"class":64,"line":107},[62,114798,114799],{"class":122}," setup",[62,114801,206],{"class":72},[62,114803,114804,114807,114810,114812,114815],{"class":64,"line":113},[62,114805,114806],{"class":68}," let",[62,114808,114809],{"class":72}," title ",[62,114811,146],{"class":68},[62,114813,114814],{"class":1675}," \"Hello, Vue 3!\"",[62,114816,153],{"class":72},[62,114818,114819,114821],{"class":64,"line":129},[62,114820,28884],{"class":68},[62,114822,114823],{"class":72}," { title };\n",[62,114825,114826],{"class":64,"line":134},[62,114827,223],{"class":72},[62,114829,114830],{"class":64,"line":156},[62,114831,82135],{"class":72},[62,114833,114834,114836,114838],{"class":64,"line":161},[62,114835,1818],{"class":72},[62,114837,15846],{"class":1780},[62,114839,1784],{"class":72},[22,114841,114842,114843,114845,114846,114848],{},"This will work but the title property is not reactive. This means that if something changes title those changes will ",[646,114844,69066],{}," be reflected in the DOM. Say for example you wanted to update the title after 5 seconds, the following will ",[646,114847,69066],{}," work.",[52,114850,114852],{"className":15773,"code":114851,"language":15775,"meta":57,"style":57},"\u003Ctemplate>\n \u003Ch1>{{ title }}\u003C/h1>\n\u003C/template>\n\n\u003Cscript>\n export default {\n setup() {\n let title = \"Hello, Vue 3!\";\n\n setTimeout(() => {\n title = \"THIS IS A NEW TITLE\";\n }, 5000);\n\n return { title };\n }\n };\n\u003C/script>\n",[59,114853,114854,114862,114874,114882,114886,114894,114902,114908,114920,114924,114935,114947,114957,114961,114967,114971,114975],{"__ignoreMap":57},[62,114855,114856,114858,114860],{"class":64,"line":65},[62,114857,760],{"class":72},[62,114859,105991],{"class":1780},[62,114861,1784],{"class":72},[62,114863,114864,114866,114868,114870,114872],{"class":64,"line":76},[62,114865,33056],{"class":72},[62,114867,4168],{"class":1780},[62,114869,114646],{"class":72},[62,114871,4168],{"class":1780},[62,114873,1784],{"class":72},[62,114875,114876,114878,114880],{"class":64,"line":82},[62,114877,1818],{"class":72},[62,114879,105991],{"class":1780},[62,114881,1784],{"class":72},[62,114883,114884],{"class":64,"line":89},[62,114885,79],{"emptyLinePlaceholder":13},[62,114887,114888,114890,114892],{"class":64,"line":95},[62,114889,760],{"class":72},[62,114891,15846],{"class":1780},[62,114893,1784],{"class":72},[62,114895,114896,114898,114900],{"class":64,"line":101},[62,114897,114675],{"class":68},[62,114899,106045],{"class":68},[62,114901,126],{"class":72},[62,114903,114904,114906],{"class":64,"line":107},[62,114905,114799],{"class":122},[62,114907,206],{"class":72},[62,114909,114910,114912,114914,114916,114918],{"class":64,"line":113},[62,114911,114806],{"class":68},[62,114913,114809],{"class":72},[62,114915,146],{"class":68},[62,114917,114814],{"class":1675},[62,114919,153],{"class":72},[62,114921,114922],{"class":64,"line":129},[62,114923,79],{"emptyLinePlaceholder":13},[62,114925,114926,114929,114931,114933],{"class":64,"line":134},[62,114927,114928],{"class":122}," setTimeout",[62,114930,797],{"class":72},[62,114932,21525],{"class":68},[62,114934,126],{"class":72},[62,114936,114937,114940,114942,114945],{"class":64,"line":156},[62,114938,114939],{"class":72}," title ",[62,114941,146],{"class":68},[62,114943,114944],{"class":1675}," \"THIS IS A NEW TITLE\"",[62,114946,153],{"class":72},[62,114948,114949,114952,114955],{"class":64,"line":161},[62,114950,114951],{"class":72}," }, ",[62,114953,114954],{"class":149},"5000",[62,114956,1133],{"class":72},[62,114958,114959],{"class":64,"line":167},[62,114960,79],{"emptyLinePlaceholder":13},[62,114962,114963,114965],{"class":64,"line":173},[62,114964,28884],{"class":68},[62,114966,114823],{"class":72},[62,114968,114969],{"class":64,"line":179},[62,114970,223],{"class":72},[62,114972,114973],{"class":64,"line":185},[62,114974,82135],{"class":72},[62,114976,114977,114979,114981],{"class":64,"line":191},[62,114978,1818],{"class":72},[62,114980,15846],{"class":1780},[62,114982,1784],{"class":72},[22,114984,114985,114986,100139,114989,114992,114993,2755],{},"To fix the example above we can ",[59,114987,114988],{},"import { ref } from 'vue'",[59,114990,114991],{},"ref()"," which will mark that variable as reactive data. Under the hood, and new in Vue 3, Vue will create a ",[677,114994,90578],{"href":114995,"rel":114996},"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy",[681],[52,114998,115000],{"className":15773,"code":114999,"language":15775,"meta":57,"style":57},"\u003Ctemplate>\n \u003Ch1>{{ title }}\u003C/h1>\n\u003C/template>\n\n\u003Cscript>\n import { ref } from \"vue\";\n\n export default {\n setup() {\n const title = ref(\"Hello, Vue 3!\");\n\n setTimeout(() => {\n // you might be asking yourself, what is this .value all about...\n // more about that soon\n title.value = \"New Title\";\n }, 5000);\n\n return { title };\n }\n };\n\u003C/script>\n",[59,115001,115002,115010,115022,115030,115034,115042,115057,115061,115069,115075,115094,115098,115108,115113,115118,115130,115138,115142,115148,115152,115156],{"__ignoreMap":57},[62,115003,115004,115006,115008],{"class":64,"line":65},[62,115005,760],{"class":72},[62,115007,105991],{"class":1780},[62,115009,1784],{"class":72},[62,115011,115012,115014,115016,115018,115020],{"class":64,"line":76},[62,115013,33056],{"class":72},[62,115015,4168],{"class":1780},[62,115017,114646],{"class":72},[62,115019,4168],{"class":1780},[62,115021,1784],{"class":72},[62,115023,115024,115026,115028],{"class":64,"line":82},[62,115025,1818],{"class":72},[62,115027,105991],{"class":1780},[62,115029,1784],{"class":72},[62,115031,115032],{"class":64,"line":89},[62,115033,79],{"emptyLinePlaceholder":13},[62,115035,115036,115038,115040],{"class":64,"line":95},[62,115037,760],{"class":72},[62,115039,15846],{"class":1780},[62,115041,1784],{"class":72},[62,115043,115044,115047,115050,115052,115055],{"class":64,"line":101},[62,115045,115046],{"class":68}," import",[62,115048,115049],{"class":72}," { ref } ",[62,115051,3507],{"class":68},[62,115053,115054],{"class":1675}," \"vue\"",[62,115056,153],{"class":72},[62,115058,115059],{"class":64,"line":107},[62,115060,79],{"emptyLinePlaceholder":13},[62,115062,115063,115065,115067],{"class":64,"line":113},[62,115064,114675],{"class":68},[62,115066,106045],{"class":68},[62,115068,126],{"class":72},[62,115070,115071,115073],{"class":64,"line":129},[62,115072,114799],{"class":122},[62,115074,206],{"class":72},[62,115076,115077,115080,115082,115084,115087,115089,115092],{"class":64,"line":134},[62,115078,115079],{"class":68}," const",[62,115081,73720],{"class":149},[62,115083,2556],{"class":68},[62,115085,115086],{"class":122}," ref",[62,115088,2109],{"class":72},[62,115090,115091],{"class":1675},"\"Hello, Vue 3!\"",[62,115093,1133],{"class":72},[62,115095,115096],{"class":64,"line":156},[62,115097,79],{"emptyLinePlaceholder":13},[62,115099,115100,115102,115104,115106],{"class":64,"line":161},[62,115101,114928],{"class":122},[62,115103,797],{"class":72},[62,115105,21525],{"class":68},[62,115107,126],{"class":72},[62,115109,115110],{"class":64,"line":167},[62,115111,115112],{"class":85}," // you might be asking yourself, what is this .value all about...\n",[62,115114,115115],{"class":64,"line":173},[62,115116,115117],{"class":85}," // more about that soon\n",[62,115119,115120,115123,115125,115128],{"class":64,"line":179},[62,115121,115122],{"class":72}," title.value ",[62,115124,146],{"class":68},[62,115126,115127],{"class":1675}," \"New Title\"",[62,115129,153],{"class":72},[62,115131,115132,115134,115136],{"class":64,"line":185},[62,115133,114951],{"class":72},[62,115135,114954],{"class":149},[62,115137,1133],{"class":72},[62,115139,115140],{"class":64,"line":191},[62,115141,79],{"emptyLinePlaceholder":13},[62,115143,115144,115146],{"class":64,"line":209},[62,115145,28884],{"class":68},[62,115147,114823],{"class":72},[62,115149,115150],{"class":64,"line":220},[62,115151,223],{"class":72},[62,115153,115154],{"class":64,"line":226},[62,115155,82135],{"class":72},[62,115157,115158,115160,115162],{"class":64,"line":231},[62,115159,1818],{"class":72},[62,115161,15846],{"class":1780},[62,115163,1784],{"class":72},[22,115165,115166,115167,115170],{},"I also want to be clear that when it comes to Ref vs Reactive I believe there are two stories to be told. The first story has to do when you're creating a component like we are above and you need to define reactive data. The second story is when you are creating composable functions that functions or components will ",[646,115168,115169],{},"use",". In this article, I will take a look at each of these scenarios.",[636,115172,115174],{"id":115173},"ref","Ref",[22,115176,115177,115178,115180],{},"If you want to make a primitive data type a reactive property, ",[59,115179,114991],{}," is going to be your first choice. Again, this isn't a silver bullet but this is a good place to start. If you need a refresher the seven primitive data types in JavaScript are:",[915,115182,115183,115185,115188,115191,115193,115196,115199],{},[37,115184,973],{},[37,115186,115187],{},"Number",[37,115189,115190],{},"BigInt",[37,115192,76514],{},[37,115194,115195],{},"Symbol",[37,115197,115198],{},"Null",[37,115200,115201],{},"Undefined",[52,115203,115205],{"className":105863,"code":115204,"language":105865,"meta":57,"style":57},"import { ref } from \"vue\";\n\nexport default {\n setup() {\n const title = ref(\"\");\n const one = ref(1);\n const isValid = ref(true);\n const foo = ref(null);\n }\n};\n",[59,115206,115207,115219,115223,115231,115238,115254,115271,115287,115304,115308],{"__ignoreMap":57},[62,115208,115209,115211,115213,115215,115217],{"class":64,"line":65},[62,115210,27875],{"class":68},[62,115212,115049],{"class":72},[62,115214,3507],{"class":68},[62,115216,115054],{"class":1675},[62,115218,153],{"class":72},[62,115220,115221],{"class":64,"line":76},[62,115222,79],{"emptyLinePlaceholder":13},[62,115224,115225,115227,115229],{"class":64,"line":82},[62,115226,14767],{"class":68},[62,115228,106045],{"class":68},[62,115230,126],{"class":72},[62,115232,115233,115236],{"class":64,"line":89},[62,115234,115235],{"class":122}," setup",[62,115237,206],{"class":72},[62,115239,115240,115242,115244,115246,115248,115250,115252],{"class":64,"line":95},[62,115241,36857],{"class":68},[62,115243,73720],{"class":149},[62,115245,2556],{"class":68},[62,115247,115086],{"class":122},[62,115249,2109],{"class":72},[62,115251,25895],{"class":1675},[62,115253,1133],{"class":72},[62,115255,115256,115258,115261,115263,115265,115267,115269],{"class":64,"line":101},[62,115257,36857],{"class":68},[62,115259,115260],{"class":149}," one",[62,115262,2556],{"class":68},[62,115264,115086],{"class":122},[62,115266,2109],{"class":72},[62,115268,6689],{"class":149},[62,115270,1133],{"class":72},[62,115272,115273,115275,115277,115279,115281,115283,115285],{"class":64,"line":107},[62,115274,36857],{"class":68},[62,115276,59723],{"class":149},[62,115278,2556],{"class":68},[62,115280,115086],{"class":122},[62,115282,2109],{"class":72},[62,115284,21775],{"class":149},[62,115286,1133],{"class":72},[62,115288,115289,115291,115294,115296,115298,115300,115302],{"class":64,"line":113},[62,115290,36857],{"class":68},[62,115292,115293],{"class":149}," foo",[62,115295,2556],{"class":68},[62,115297,115086],{"class":122},[62,115299,2109],{"class":72},[62,115301,3256],{"class":149},[62,115303,1133],{"class":72},[62,115305,115306],{"class":64,"line":129},[62,115307,3731],{"class":72},[62,115309,115310],{"class":64,"line":134},[62,115311,107354],{"class":72},[22,115313,115314,115315,115317],{},"From the previous example, we had a String called title so ",[59,115316,114991],{}," was a good choice for declaring reactive data. If you have some questions about that code we wrote below don't worry, I had the same questions.",[52,115319,115321],{"className":105863,"code":115320,"language":105865,"meta":57,"style":57},"import { ref } from \"vue\";\n\nexport default {\n setup() {\n const title = ref(\"Hello, Vue 3!\");\n\n setTimeout(() => {\n title.value = \"New Title\";\n }, 5000);\n\n return { title };\n }\n};\n",[59,115322,115323,115335,115339,115347,115353,115369,115373,115383,115394,115403,115407,115413,115417],{"__ignoreMap":57},[62,115324,115325,115327,115329,115331,115333],{"class":64,"line":65},[62,115326,27875],{"class":68},[62,115328,115049],{"class":72},[62,115330,3507],{"class":68},[62,115332,115054],{"class":1675},[62,115334,153],{"class":72},[62,115336,115337],{"class":64,"line":76},[62,115338,79],{"emptyLinePlaceholder":13},[62,115340,115341,115343,115345],{"class":64,"line":82},[62,115342,14767],{"class":68},[62,115344,106045],{"class":68},[62,115346,126],{"class":72},[62,115348,115349,115351],{"class":64,"line":89},[62,115350,115235],{"class":122},[62,115352,206],{"class":72},[62,115354,115355,115357,115359,115361,115363,115365,115367],{"class":64,"line":95},[62,115356,36857],{"class":68},[62,115358,73720],{"class":149},[62,115360,2556],{"class":68},[62,115362,115086],{"class":122},[62,115364,2109],{"class":72},[62,115366,115091],{"class":1675},[62,115368,1133],{"class":72},[62,115370,115371],{"class":64,"line":101},[62,115372,79],{"emptyLinePlaceholder":13},[62,115374,115375,115377,115379,115381],{"class":64,"line":107},[62,115376,107578],{"class":122},[62,115378,797],{"class":72},[62,115380,21525],{"class":68},[62,115382,126],{"class":72},[62,115384,115385,115388,115390,115392],{"class":64,"line":113},[62,115386,115387],{"class":72}," title.value ",[62,115389,146],{"class":68},[62,115391,115127],{"class":1675},[62,115393,153],{"class":72},[62,115395,115396,115399,115401],{"class":64,"line":129},[62,115397,115398],{"class":72}," }, ",[62,115400,114954],{"class":149},[62,115402,1133],{"class":72},[62,115404,115405],{"class":64,"line":134},[62,115406,79],{"emptyLinePlaceholder":13},[62,115408,115409,115411],{"class":64,"line":156},[62,115410,2599],{"class":68},[62,115412,114823],{"class":72},[62,115414,115415],{"class":64,"line":161},[62,115416,3731],{"class":72},[62,115418,115419],{"class":64,"line":167},[62,115420,107354],{"class":72},[22,115422,115423,115424,115426,115427,115429,115430,115433,115434,115437],{},"Why use a ",[59,115425,110541],{}," for the title when the value is going to change? Shouldn't we be using ",[59,115428,21958],{}," here? If you were to ",[59,115431,115432],{},"console.log(title)"," you might expect to see the value ",[59,115435,115436],{},"Hello, Vue 3!",", instead you get an object that looks like this:",[52,115439,115441],{"className":105863,"code":115440,"language":105865,"meta":57,"style":57},"{_isRef: true}\nvalue: (...)\n_isRef: true\nget value: ƒ value()\nset value: ƒ value(newVal)\n__proto__: Object\n",[59,115442,115443,115457,115469,115477,115491,115505],{"__ignoreMap":57},[62,115444,115445,115448,115451,115453,115455],{"class":64,"line":65},[62,115446,115447],{"class":72},"{",[62,115449,115450],{"class":122},"_isRef",[62,115452,3696],{"class":72},[62,115454,21775],{"class":149},[62,115456,379],{"class":72},[62,115458,115459,115461,115464,115467],{"class":64,"line":76},[62,115460,2553],{"class":122},[62,115462,115463],{"class":72},": (",[62,115465,115466],{"class":68},"...",[62,115468,2212],{"class":72},[62,115470,115471,115473,115475],{"class":64,"line":82},[62,115472,115450],{"class":122},[62,115474,3696],{"class":72},[62,115476,51914],{"class":149},[62,115478,115479,115482,115484,115487,115489],{"class":64,"line":89},[62,115480,115481],{"class":72},"get ",[62,115483,2553],{"class":122},[62,115485,115486],{"class":72},": ƒ ",[62,115488,2553],{"class":122},[62,115490,2223],{"class":72},[62,115492,115493,115496,115498,115500,115502],{"class":64,"line":95},[62,115494,115495],{"class":72},"set ",[62,115497,2553],{"class":122},[62,115499,115486],{"class":72},[62,115501,2553],{"class":122},[62,115503,115504],{"class":72},"(newVal)\n",[62,115506,115507,115510],{"class":64,"line":101},[62,115508,115509],{"class":122},"__proto__",[62,115511,115512],{"class":72},": Object\n",[22,115514,115515,115517,115518,115521,115522,115525,115526,2755],{},[59,115516,114991],{}," takes an inner value and returns a reactive and mutable ref object. The ref object has a single property ",[59,115519,115520],{},".value"," that points to the inner value. This means that if you want to access or mutate the value you need to use ",[59,115523,115524],{},"title.value",". and because this is an object that won't change I have decided to declare it as a ",[59,115527,110541],{},[636,115529,115531],{"id":115530},"ref-unwrapping","Ref Unwrapping",[22,115533,115534,115535,115537],{},"The next question you might ask is \"Why don't we have to reference ",[59,115536,115520],{}," in the template\"?",[52,115539,115541],{"className":15773,"code":115540,"language":15775,"meta":57,"style":57},"\u003Ctemplate>\n \u003Ch1>{{ title }}\u003C/h1>\n\u003C/template>\n",[59,115542,115543,115551,115563],{"__ignoreMap":57},[62,115544,115545,115547,115549],{"class":64,"line":65},[62,115546,760],{"class":72},[62,115548,105991],{"class":1780},[62,115550,1784],{"class":72},[62,115552,115553,115555,115557,115559,115561],{"class":64,"line":76},[62,115554,33056],{"class":72},[62,115556,4168],{"class":1780},[62,115558,114646],{"class":72},[62,115560,4168],{"class":1780},[62,115562,1784],{"class":72},[62,115564,115565,115567,115569],{"class":64,"line":82},[62,115566,1818],{"class":72},[62,115568,105991],{"class":1780},[62,115570,1784],{"class":72},[22,115572,115573,115574,115576],{},"When a ref is returned as a property on the render context (the object returned from setup()) and accessed in the template, it automatically unwraps to the inner value. There is no need to append ",[59,115575,115520],{}," in the template.",[29685,115578,115579],{},[22,115580,115581,115582],{},"Computed Properties work the same so if you need the value of a computed property within the setup() method you will need to use ",[59,115583,115520],{},[636,115585,115587],{"id":115586},"reactive","Reactive",[22,115589,115590,115591,115593,115594,115596,115597,115600,115601,2755],{},"We just looked at some examples of using ",[59,115592,114991],{}," when you want to define reactive data on primitive values. What happens if you want to create an reactive object? In that case, you could still use ",[59,115595,114991],{}," but underneath the hood, it's just calling ",[59,115598,115599],{},"reactive()"," so I will stick to using ",[59,115602,115599],{},[22,115604,115605,115606,115608,115609,115611,115612,115615],{},"On the flip side of that ",[59,115607,115599],{}," will not work with primitive values. ",[59,115610,115599],{}," takes an object and returns a reactive proxy of the original. This is equivalent to 2.x's ",[59,115613,115614],{},"Vue.observable()"," and was renamed to avoid confusion with RxJS observables.",[52,115617,115619],{"className":105863,"code":115618,"language":105865,"meta":57,"style":57},"import { reactive } from \"vue\";\n\nexport default {\n setup() {\n const data = reactive({\n title: \"Hello, Vue 3\"\n });\n\n return { data };\n }\n};\n",[59,115620,115621,115634,115638,115646,115652,115666,115674,115678,115682,115689,115693],{"__ignoreMap":57},[62,115622,115623,115625,115628,115630,115632],{"class":64,"line":65},[62,115624,27875],{"class":68},[62,115626,115627],{"class":72}," { reactive } ",[62,115629,3507],{"class":68},[62,115631,115054],{"class":1675},[62,115633,153],{"class":72},[62,115635,115636],{"class":64,"line":76},[62,115637,79],{"emptyLinePlaceholder":13},[62,115639,115640,115642,115644],{"class":64,"line":82},[62,115641,14767],{"class":68},[62,115643,106045],{"class":68},[62,115645,126],{"class":72},[62,115647,115648,115650],{"class":64,"line":89},[62,115649,115235],{"class":122},[62,115651,206],{"class":72},[62,115653,115654,115656,115659,115661,115664],{"class":64,"line":95},[62,115655,36857],{"class":68},[62,115657,115658],{"class":149}," data",[62,115660,2556],{"class":68},[62,115662,115663],{"class":122}," reactive",[62,115665,50544],{"class":72},[62,115667,115668,115671],{"class":64,"line":101},[62,115669,115670],{"class":72}," title: ",[62,115672,115673],{"class":1675},"\"Hello, Vue 3\"\n",[62,115675,115676],{"class":64,"line":107},[62,115677,906],{"class":72},[62,115679,115680],{"class":64,"line":113},[62,115681,79],{"emptyLinePlaceholder":13},[62,115683,115684,115686],{"class":64,"line":129},[62,115685,2599],{"class":68},[62,115687,115688],{"class":72}," { data };\n",[62,115690,115691],{"class":64,"line":134},[62,115692,3731],{"class":72},[62,115694,115695],{"class":64,"line":156},[62,115696,107354],{"class":72},[22,115698,115699,115700,115702,115703,115705,115706,115708,115709,115712],{},"The big difference here is when you want to access data defined using ",[59,115701,115599],{}," in your template. In the previous example ",[59,115704,106264],{}," is an object that contains a property named ",[59,115707,3196],{},". You will need to reference ",[59,115710,115711],{},"data.title"," in your template:",[52,115714,115716],{"className":15773,"code":115715,"language":15775,"meta":57,"style":57},"\u003Ctemplate>\n \u003Ch1>{{ data.title }}\u003C/h1>\n\u003C/template>\n\n\u003Cscript>\n import { ref } from \"vue\";\n\n export default {\n setup() {\n const data = ref({\n title: \"Hello, Vue 3\"\n });\n\n return { data };\n }\n };\n\u003C/script>\n",[59,115717,115718,115726,115739,115747,115751,115759,115771,115775,115783,115789,115801,115807,115811,115815,115821,115825,115829],{"__ignoreMap":57},[62,115719,115720,115722,115724],{"class":64,"line":65},[62,115721,760],{"class":72},[62,115723,105991],{"class":1780},[62,115725,1784],{"class":72},[62,115727,115728,115730,115732,115735,115737],{"class":64,"line":76},[62,115729,33056],{"class":72},[62,115731,4168],{"class":1780},[62,115733,115734],{"class":72},">{{ data.title }}\u003C/",[62,115736,4168],{"class":1780},[62,115738,1784],{"class":72},[62,115740,115741,115743,115745],{"class":64,"line":82},[62,115742,1818],{"class":72},[62,115744,105991],{"class":1780},[62,115746,1784],{"class":72},[62,115748,115749],{"class":64,"line":89},[62,115750,79],{"emptyLinePlaceholder":13},[62,115752,115753,115755,115757],{"class":64,"line":95},[62,115754,760],{"class":72},[62,115756,15846],{"class":1780},[62,115758,1784],{"class":72},[62,115760,115761,115763,115765,115767,115769],{"class":64,"line":101},[62,115762,115046],{"class":68},[62,115764,115049],{"class":72},[62,115766,3507],{"class":68},[62,115768,115054],{"class":1675},[62,115770,153],{"class":72},[62,115772,115773],{"class":64,"line":107},[62,115774,79],{"emptyLinePlaceholder":13},[62,115776,115777,115779,115781],{"class":64,"line":113},[62,115778,114675],{"class":68},[62,115780,106045],{"class":68},[62,115782,126],{"class":72},[62,115784,115785,115787],{"class":64,"line":129},[62,115786,114799],{"class":122},[62,115788,206],{"class":72},[62,115790,115791,115793,115795,115797,115799],{"class":64,"line":134},[62,115792,115079],{"class":68},[62,115794,115658],{"class":149},[62,115796,2556],{"class":68},[62,115798,115086],{"class":122},[62,115800,50544],{"class":72},[62,115802,115803,115805],{"class":64,"line":156},[62,115804,114697],{"class":72},[62,115806,115673],{"class":1675},[62,115808,115809],{"class":64,"line":161},[62,115810,106288],{"class":72},[62,115812,115813],{"class":64,"line":167},[62,115814,79],{"emptyLinePlaceholder":13},[62,115816,115817,115819],{"class":64,"line":173},[62,115818,28884],{"class":68},[62,115820,115688],{"class":72},[62,115822,115823],{"class":64,"line":179},[62,115824,223],{"class":72},[62,115826,115827],{"class":64,"line":185},[62,115828,82135],{"class":72},[62,115830,115831,115833,115835],{"class":64,"line":191},[62,115832,1818],{"class":72},[62,115834,15846],{"class":1780},[62,115836,1784],{"class":72},[636,115838,115840],{"id":115839},"ref-vs-reactive-in-components","Ref vs Reactive in Components",[22,115842,115843,115844,115846,115847,115849,115850,115855],{},"So based on everything discussed so far the answer is pretty easy right? We should just use ",[59,115845,114991],{}," for primitives and ",[59,115848,115599],{}," for objects. As I started building components out that wasn't always the case and in-fact ",[677,115851,115854],{"href":115852,"rel":115853},"https://vue-composition-api-rfc.netlify.com/#ref-vs-reactive",[681],"the documentation"," states:",[29685,115857,115858],{},[22,115859,115860],{},"The difference between using ref and reactive can be somewhat compared to how you would write standard JavaScript logic",[22,115862,115863,115864,115866,115867,115869,115870,115872],{},"I started thinking about that and it led me to the following conclusion. In the examples, we have seen I single property named ",[59,115865,3196],{}," which was a ",[59,115868,973],{}," and it made perfect sense to use ",[59,115871,114991],{},". As my application started growing though I had the following properties defined:",[52,115874,115876],{"className":105863,"code":115875,"language":105865,"meta":57,"style":57},"export default {\n setup() {\n const title = ref(\"Hello, World!\");\n const description = ref(\"\");\n const content = ref(\"Hello world\");\n const wordCount = computed(() => content.value.length);\n\n return { title, description, content, wordCount };\n }\n};\n",[59,115877,115878,115886,115892,115908,115925,115942,115965,115969,115976,115980],{"__ignoreMap":57},[62,115879,115880,115882,115884],{"class":64,"line":65},[62,115881,14767],{"class":68},[62,115883,106045],{"class":68},[62,115885,126],{"class":72},[62,115887,115888,115890],{"class":64,"line":76},[62,115889,115235],{"class":122},[62,115891,206],{"class":72},[62,115893,115894,115896,115898,115900,115902,115904,115906],{"class":64,"line":82},[62,115895,36857],{"class":68},[62,115897,73720],{"class":149},[62,115899,2556],{"class":68},[62,115901,115086],{"class":122},[62,115903,2109],{"class":72},[62,115905,27469],{"class":1675},[62,115907,1133],{"class":72},[62,115909,115910,115912,115915,115917,115919,115921,115923],{"class":64,"line":89},[62,115911,36857],{"class":68},[62,115913,115914],{"class":149}," description",[62,115916,2556],{"class":68},[62,115918,115086],{"class":122},[62,115920,2109],{"class":72},[62,115922,25895],{"class":1675},[62,115924,1133],{"class":72},[62,115926,115927,115929,115931,115933,115935,115937,115940],{"class":64,"line":95},[62,115928,36857],{"class":68},[62,115930,20727],{"class":149},[62,115932,2556],{"class":68},[62,115934,115086],{"class":122},[62,115936,2109],{"class":72},[62,115938,115939],{"class":1675},"\"Hello world\"",[62,115941,1133],{"class":72},[62,115943,115944,115946,115949,115951,115954,115956,115958,115961,115963],{"class":64,"line":101},[62,115945,36857],{"class":68},[62,115947,115948],{"class":149}," wordCount",[62,115950,2556],{"class":68},[62,115952,115953],{"class":122}," computed",[62,115955,797],{"class":72},[62,115957,21525],{"class":68},[62,115959,115960],{"class":72}," content.value.",[62,115962,14193],{"class":149},[62,115964,1133],{"class":72},[62,115966,115967],{"class":64,"line":107},[62,115968,79],{"emptyLinePlaceholder":13},[62,115970,115971,115973],{"class":64,"line":113},[62,115972,2599],{"class":68},[62,115974,115975],{"class":72}," { title, description, content, wordCount };\n",[62,115977,115978],{"class":64,"line":129},[62,115979,3731],{"class":72},[62,115981,115982],{"class":64,"line":134},[62,115983,107354],{"class":72},[22,115985,115986,115987,115990],{},"In JavaScript, I would look at these properties and determine that they are all properties of my ",[59,115988,115989],{},"page"," object. In that case, I would group them all of them into a JavaScript object so why not do the same here.",[52,115992,115994],{"className":15773,"code":115993,"language":15775,"meta":57,"style":57},"\u003Ctemplate>\n \u003Cdiv class=\"page\">\n \u003Ch1>{{ page.title }}\u003C/h1>\n \u003Cp>{{ page.wordCount }}\u003C/p>\n \u003C/div>\n\u003C/template>\n\n\u003Cscript>\n import { ref, computed, reactive } from \"vue\";\n\n export default {\n setup() {\n const page = reactive({\n title: \"Hello, World!\",\n description: \"\",\n content: \"Hello world\",\n wordCount: computed(() => page.content.length)\n });\n\n return { page };\n }\n };\n\u003C/script>\n",[59,115995,115996,116004,116018,116031,116044,116052,116060,116064,116072,116085,116089,116097,116103,116115,116123,116132,116141,116160,116164,116168,116175,116179,116183],{"__ignoreMap":57},[62,115997,115998,116000,116002],{"class":64,"line":65},[62,115999,760],{"class":72},[62,116001,105991],{"class":1780},[62,116003,1784],{"class":72},[62,116005,116006,116008,116010,116012,116014,116016],{"class":64,"line":76},[62,116007,33056],{"class":72},[62,116009,15944],{"class":1780},[62,116011,119],{"class":122},[62,116013,146],{"class":72},[62,116015,25986],{"class":1675},[62,116017,1784],{"class":72},[62,116019,116020,116022,116024,116027,116029],{"class":64,"line":82},[62,116021,1789],{"class":72},[62,116023,4168],{"class":1780},[62,116025,116026],{"class":72},">{{ page.title }}\u003C/",[62,116028,4168],{"class":1780},[62,116030,1784],{"class":72},[62,116032,116033,116035,116037,116040,116042],{"class":64,"line":89},[62,116034,1789],{"class":72},[62,116036,22],{"class":1780},[62,116038,116039],{"class":72},">{{ page.wordCount }}\u003C/",[62,116041,22],{"class":1780},[62,116043,1784],{"class":72},[62,116045,116046,116048,116050],{"class":64,"line":95},[62,116047,33187],{"class":72},[62,116049,15944],{"class":1780},[62,116051,1784],{"class":72},[62,116053,116054,116056,116058],{"class":64,"line":101},[62,116055,1818],{"class":72},[62,116057,105991],{"class":1780},[62,116059,1784],{"class":72},[62,116061,116062],{"class":64,"line":107},[62,116063,79],{"emptyLinePlaceholder":13},[62,116065,116066,116068,116070],{"class":64,"line":113},[62,116067,760],{"class":72},[62,116069,15846],{"class":1780},[62,116071,1784],{"class":72},[62,116073,116074,116076,116079,116081,116083],{"class":64,"line":129},[62,116075,115046],{"class":68},[62,116077,116078],{"class":72}," { ref, computed, reactive } ",[62,116080,3507],{"class":68},[62,116082,115054],{"class":1675},[62,116084,153],{"class":72},[62,116086,116087],{"class":64,"line":134},[62,116088,79],{"emptyLinePlaceholder":13},[62,116090,116091,116093,116095],{"class":64,"line":156},[62,116092,114675],{"class":68},[62,116094,106045],{"class":68},[62,116096,126],{"class":72},[62,116098,116099,116101],{"class":64,"line":161},[62,116100,114799],{"class":122},[62,116102,206],{"class":72},[62,116104,116105,116107,116109,116111,116113],{"class":64,"line":167},[62,116106,115079],{"class":68},[62,116108,102940],{"class":149},[62,116110,2556],{"class":68},[62,116112,115663],{"class":122},[62,116114,50544],{"class":72},[62,116116,116117,116119,116121],{"class":64,"line":173},[62,116118,114697],{"class":72},[62,116120,27469],{"class":1675},[62,116122,3338],{"class":72},[62,116124,116125,116128,116130],{"class":64,"line":179},[62,116126,116127],{"class":72}," description: ",[62,116129,25895],{"class":1675},[62,116131,3338],{"class":72},[62,116133,116134,116137,116139],{"class":64,"line":185},[62,116135,116136],{"class":72}," content: ",[62,116138,115939],{"class":1675},[62,116140,3338],{"class":72},[62,116142,116143,116146,116149,116151,116153,116156,116158],{"class":64,"line":191},[62,116144,116145],{"class":72}," wordCount: ",[62,116147,116148],{"class":122},"computed",[62,116150,797],{"class":72},[62,116152,21525],{"class":68},[62,116154,116155],{"class":72}," page.content.",[62,116157,14193],{"class":149},[62,116159,2212],{"class":72},[62,116161,116162],{"class":64,"line":209},[62,116163,106288],{"class":72},[62,116165,116166],{"class":64,"line":220},[62,116167,79],{"emptyLinePlaceholder":13},[62,116169,116170,116172],{"class":64,"line":226},[62,116171,28884],{"class":68},[62,116173,116174],{"class":72}," { page };\n",[62,116176,116177],{"class":64,"line":231},[62,116178,223],{"class":72},[62,116180,116181],{"class":64,"line":236},[62,116182,82135],{"class":72},[62,116184,116185,116187,116189],{"class":64,"line":242},[62,116186,1818],{"class":72},[62,116188,15846],{"class":1780},[62,116190,1784],{"class":72},[22,116192,116193],{},"This is how I have been approaching Ref vs Reactive in my components but I would love to hear from you. Are you doing something similar? Is this approach wrong? Please leave me some feedback below.",[26,116195,116197],{"id":116196},"creating-composable-logic","Creating Composable Logic",[22,116199,116200,116201,34867,116203,116205,116206,116208],{},"There isn't any wrong answer when using ",[59,116202,114991],{},[59,116204,115599],{}," in your components. They both will create reactive data and as long as you understand how to access that data in your ",[59,116207,114740],{}," method and in your templates you shouldn't have any issues.",[22,116210,116211],{},"When you start writing composable functions though you need to understand the difference. I am going to use the example from the RFC documentation because it does a great job of explaining the side effects.",[22,116213,116214],{},"You have been tasked with creating some logic that will keep track of a user's mouse position. You also need the ability to reuse this logic in any component that needs it. You create a composition function that tracks the x and y coordinates and then returns them to the consumer.",[52,116216,116218],{"className":105863,"code":116217,"language":105865,"meta":57,"style":57},"import { ref, onMounted, onUnmounted } from \"vue\";\n\nexport function useMousePosition() {\n const x = ref(0);\n const y = ref(0);\n\n function update(e) {\n x.value = e.pageX;\n y.value = e.pageY;\n }\n\n onMounted(() => {\n window.addEventListener(\"mousemove\", update);\n });\n\n onUnmounted(() => {\n window.removeEventListener(\"mousemove\", update);\n });\n\n return { x, y };\n}\n",[59,116219,116220,116233,116237,116248,116264,116281,116285,116298,116308,116318,116322,116326,116337,116352,116356,116360,116371,116383,116387,116391,116398],{"__ignoreMap":57},[62,116221,116222,116224,116227,116229,116231],{"class":64,"line":65},[62,116223,27875],{"class":68},[62,116225,116226],{"class":72}," { ref, onMounted, onUnmounted } ",[62,116228,3507],{"class":68},[62,116230,115054],{"class":1675},[62,116232,153],{"class":72},[62,116234,116235],{"class":64,"line":76},[62,116236,79],{"emptyLinePlaceholder":13},[62,116238,116239,116241,116243,116246],{"class":64,"line":82},[62,116240,14767],{"class":68},[62,116242,21929],{"class":68},[62,116244,116245],{"class":122}," useMousePosition",[62,116247,206],{"class":72},[62,116249,116250,116252,116254,116256,116258,116260,116262],{"class":64,"line":89},[62,116251,85414],{"class":68},[62,116253,112771],{"class":149},[62,116255,2556],{"class":68},[62,116257,115086],{"class":122},[62,116259,2109],{"class":72},[62,116261,1130],{"class":149},[62,116263,1133],{"class":72},[62,116265,116266,116268,116271,116273,116275,116277,116279],{"class":64,"line":95},[62,116267,85414],{"class":68},[62,116269,116270],{"class":149}," y",[62,116272,2556],{"class":68},[62,116274,115086],{"class":122},[62,116276,2109],{"class":72},[62,116278,1130],{"class":149},[62,116280,1133],{"class":72},[62,116282,116283],{"class":64,"line":101},[62,116284,79],{"emptyLinePlaceholder":13},[62,116286,116287,116290,116292,116294,116296],{"class":64,"line":107},[62,116288,116289],{"class":68}," function",[62,116291,38909],{"class":122},[62,116293,2109],{"class":72},[62,116295,890],{"class":889},[62,116297,768],{"class":72},[62,116299,116300,116303,116305],{"class":64,"line":113},[62,116301,116302],{"class":72}," x.value ",[62,116304,146],{"class":68},[62,116306,116307],{"class":72}," e.pageX;\n",[62,116309,116310,116313,116315],{"class":64,"line":129},[62,116311,116312],{"class":72}," y.value ",[62,116314,146],{"class":68},[62,116316,116317],{"class":72}," e.pageY;\n",[62,116319,116320],{"class":64,"line":134},[62,116321,3731],{"class":72},[62,116323,116324],{"class":64,"line":156},[62,116325,79],{"emptyLinePlaceholder":13},[62,116327,116328,116331,116333,116335],{"class":64,"line":161},[62,116329,116330],{"class":122}," onMounted",[62,116332,797],{"class":72},[62,116334,21525],{"class":68},[62,116336,126],{"class":72},[62,116338,116339,116342,116344,116346,116349],{"class":64,"line":167},[62,116340,116341],{"class":72}," window.",[62,116343,21036],{"class":122},[62,116345,2109],{"class":72},[62,116347,116348],{"class":1675},"\"mousemove\"",[62,116350,116351],{"class":72},", update);\n",[62,116353,116354],{"class":64,"line":173},[62,116355,85488],{"class":72},[62,116357,116358],{"class":64,"line":179},[62,116359,79],{"emptyLinePlaceholder":13},[62,116361,116362,116365,116367,116369],{"class":64,"line":185},[62,116363,116364],{"class":122}," onUnmounted",[62,116366,797],{"class":72},[62,116368,21525],{"class":68},[62,116370,126],{"class":72},[62,116372,116373,116375,116377,116379,116381],{"class":64,"line":191},[62,116374,116341],{"class":72},[62,116376,113141],{"class":122},[62,116378,2109],{"class":72},[62,116380,116348],{"class":1675},[62,116382,116351],{"class":72},[62,116384,116385],{"class":64,"line":209},[62,116386,85488],{"class":72},[62,116388,116389],{"class":64,"line":220},[62,116390,79],{"emptyLinePlaceholder":13},[62,116392,116393,116395],{"class":64,"line":226},[62,116394,82091],{"class":68},[62,116396,116397],{"class":72}," { x, y };\n",[62,116399,116400],{"class":64,"line":231},[62,116401,379],{"class":72},[22,116403,116404],{},"If you want to consume this logic in a component you can call the function, destructure the return object and then return the x and y coordinates to your template.",[52,116406,116408],{"className":15773,"code":116407,"language":15775,"meta":57,"style":57},"\u003Ctemplate>\n \u003Ch1>Use Mouse Demo\u003C/h1>\n \u003Cp>x: {{ x }} | y: {{ y }}\u003C/p>\n\u003C/template>\n\n\u003Cscript>\n import { useMousePosition } from \"./use/useMousePosition\";\n\n export default {\n setup() {\n const { x, y } = useMousePosition();\n return { x, y };\n }\n };\n\u003C/script>\n",[59,116409,116410,116418,116431,116444,116452,116456,116464,116478,116482,116490,116496,116517,116523,116527,116531],{"__ignoreMap":57},[62,116411,116412,116414,116416],{"class":64,"line":65},[62,116413,760],{"class":72},[62,116415,105991],{"class":1780},[62,116417,1784],{"class":72},[62,116419,116420,116422,116424,116427,116429],{"class":64,"line":76},[62,116421,33056],{"class":72},[62,116423,4168],{"class":1780},[62,116425,116426],{"class":72},">Use Mouse Demo\u003C/",[62,116428,4168],{"class":1780},[62,116430,1784],{"class":72},[62,116432,116433,116435,116437,116440,116442],{"class":64,"line":82},[62,116434,33056],{"class":72},[62,116436,22],{"class":1780},[62,116438,116439],{"class":72},">x: {{ x }} | y: {{ y }}\u003C/",[62,116441,22],{"class":1780},[62,116443,1784],{"class":72},[62,116445,116446,116448,116450],{"class":64,"line":89},[62,116447,1818],{"class":72},[62,116449,105991],{"class":1780},[62,116451,1784],{"class":72},[62,116453,116454],{"class":64,"line":95},[62,116455,79],{"emptyLinePlaceholder":13},[62,116457,116458,116460,116462],{"class":64,"line":101},[62,116459,760],{"class":72},[62,116461,15846],{"class":1780},[62,116463,1784],{"class":72},[62,116465,116466,116468,116471,116473,116476],{"class":64,"line":107},[62,116467,115046],{"class":68},[62,116469,116470],{"class":72}," { useMousePosition } ",[62,116472,3507],{"class":68},[62,116474,116475],{"class":1675}," \"./use/useMousePosition\"",[62,116477,153],{"class":72},[62,116479,116480],{"class":64,"line":113},[62,116481,79],{"emptyLinePlaceholder":13},[62,116483,116484,116486,116488],{"class":64,"line":129},[62,116485,114675],{"class":68},[62,116487,106045],{"class":68},[62,116489,126],{"class":72},[62,116491,116492,116494],{"class":64,"line":134},[62,116493,114799],{"class":122},[62,116495,206],{"class":72},[62,116497,116498,116500,116502,116504,116506,116509,116511,116513,116515],{"class":64,"line":156},[62,116499,115079],{"class":68},[62,116501,21785],{"class":72},[62,116503,85717],{"class":149},[62,116505,976],{"class":72},[62,116507,116508],{"class":149},"y",[62,116510,21795],{"class":72},[62,116512,146],{"class":68},[62,116514,116245],{"class":122},[62,116516,822],{"class":72},[62,116518,116519,116521],{"class":64,"line":161},[62,116520,28884],{"class":68},[62,116522,116397],{"class":72},[62,116524,116525],{"class":64,"line":167},[62,116526,223],{"class":72},[62,116528,116529],{"class":64,"line":173},[62,116530,82135],{"class":72},[62,116532,116533,116535,116537],{"class":64,"line":179},[62,116534,1818],{"class":72},[62,116536,15846],{"class":1780},[62,116538,1784],{"class":72},[22,116540,116541],{},"This will work but as you took a look at this function you decided to refactor x and y into a position object:",[52,116543,116545],{"className":105863,"code":116544,"language":105865,"meta":57,"style":57},"import { ref, onMounted, onUnmounted } from \"vue\";\n\nexport function useMousePosition() {\n const pos = {\n x: 0,\n y: 0\n };\n\n function update(e) {\n pos.x = e.pageX;\n pos.y = e.pageY;\n }\n\n // ...\n}\n",[59,116546,116547,116559,116563,116573,116584,116593,116600,116604,116608,116620,116629,116638,116642,116646,116651],{"__ignoreMap":57},[62,116548,116549,116551,116553,116555,116557],{"class":64,"line":65},[62,116550,27875],{"class":68},[62,116552,116226],{"class":72},[62,116554,3507],{"class":68},[62,116556,115054],{"class":1675},[62,116558,153],{"class":72},[62,116560,116561],{"class":64,"line":76},[62,116562,79],{"emptyLinePlaceholder":13},[62,116564,116565,116567,116569,116571],{"class":64,"line":82},[62,116566,14767],{"class":68},[62,116568,21929],{"class":68},[62,116570,116245],{"class":122},[62,116572,206],{"class":72},[62,116574,116575,116577,116580,116582],{"class":64,"line":89},[62,116576,85414],{"class":68},[62,116578,116579],{"class":149}," pos",[62,116581,2556],{"class":68},[62,116583,126],{"class":72},[62,116585,116586,116589,116591],{"class":64,"line":95},[62,116587,116588],{"class":72}," x: ",[62,116590,1130],{"class":149},[62,116592,3338],{"class":72},[62,116594,116595,116598],{"class":64,"line":101},[62,116596,116597],{"class":72}," y: ",[62,116599,72365],{"class":149},[62,116601,116602],{"class":64,"line":107},[62,116603,82135],{"class":72},[62,116605,116606],{"class":64,"line":113},[62,116607,79],{"emptyLinePlaceholder":13},[62,116609,116610,116612,116614,116616,116618],{"class":64,"line":129},[62,116611,116289],{"class":68},[62,116613,38909],{"class":122},[62,116615,2109],{"class":72},[62,116617,890],{"class":889},[62,116619,768],{"class":72},[62,116621,116622,116625,116627],{"class":64,"line":134},[62,116623,116624],{"class":72}," pos.x ",[62,116626,146],{"class":68},[62,116628,116307],{"class":72},[62,116630,116631,116634,116636],{"class":64,"line":156},[62,116632,116633],{"class":72}," pos.y ",[62,116635,146],{"class":68},[62,116637,116317],{"class":72},[62,116639,116640],{"class":64,"line":161},[62,116641,3731],{"class":72},[62,116643,116644],{"class":64,"line":167},[62,116645,79],{"emptyLinePlaceholder":13},[62,116647,116648],{"class":64,"line":173},[62,116649,116650],{"class":85}," // ...\n",[62,116652,116653],{"class":64,"line":179},[62,116654,379],{"class":72},[22,116656,116657],{},"The problem with this approach is that the consumer of the composition function must keep the reference to the returned object at all times in order to retain reactivity. This means that the object cannot be destructured or spread:",[52,116659,116661],{"className":105863,"code":116660,"language":105865,"meta":57,"style":57},"// consuming component\nexport default {\n setup() {\n // reactivity lost!\n const { x, y } = useMousePosition();\n return {\n x,\n y\n };\n\n // reactivity lost!\n return {\n ...useMousePosition()\n };\n\n // this is the only way to retain reactivity.\n // you must return `pos` as-is and reference x and y as `pos.x` and `pos.y`\n // in the template.\n return {\n pos: useMousePosition()\n };\n }\n};\n",[59,116662,116663,116668,116676,116682,116687,116707,116713,116718,116723,116727,116731,116735,116741,116751,116755,116759,116764,116769,116774,116780,116789,116793,116797],{"__ignoreMap":57},[62,116664,116665],{"class":64,"line":65},[62,116666,116667],{"class":85},"// consuming component\n",[62,116669,116670,116672,116674],{"class":64,"line":76},[62,116671,14767],{"class":68},[62,116673,106045],{"class":68},[62,116675,126],{"class":72},[62,116677,116678,116680],{"class":64,"line":82},[62,116679,115235],{"class":122},[62,116681,206],{"class":72},[62,116683,116684],{"class":64,"line":89},[62,116685,116686],{"class":85}," // reactivity lost!\n",[62,116688,116689,116691,116693,116695,116697,116699,116701,116703,116705],{"class":64,"line":95},[62,116690,36857],{"class":68},[62,116692,21785],{"class":72},[62,116694,85717],{"class":149},[62,116696,976],{"class":72},[62,116698,116508],{"class":149},[62,116700,21795],{"class":72},[62,116702,146],{"class":68},[62,116704,116245],{"class":122},[62,116706,822],{"class":72},[62,116708,116709,116711],{"class":64,"line":101},[62,116710,2599],{"class":68},[62,116712,126],{"class":72},[62,116714,116715],{"class":64,"line":107},[62,116716,116717],{"class":72}," x,\n",[62,116719,116720],{"class":64,"line":113},[62,116721,116722],{"class":72}," y\n",[62,116724,116725],{"class":64,"line":129},[62,116726,40087],{"class":72},[62,116728,116729],{"class":64,"line":134},[62,116730,79],{"emptyLinePlaceholder":13},[62,116732,116733],{"class":64,"line":156},[62,116734,116686],{"class":85},[62,116736,116737,116739],{"class":64,"line":161},[62,116738,2599],{"class":68},[62,116740,126],{"class":72},[62,116742,116743,116746,116749],{"class":64,"line":167},[62,116744,116745],{"class":68}," ...",[62,116747,116748],{"class":122},"useMousePosition",[62,116750,2223],{"class":72},[62,116752,116753],{"class":64,"line":173},[62,116754,40087],{"class":72},[62,116756,116757],{"class":64,"line":179},[62,116758,79],{"emptyLinePlaceholder":13},[62,116760,116761],{"class":64,"line":185},[62,116762,116763],{"class":85}," // this is the only way to retain reactivity.\n",[62,116765,116766],{"class":64,"line":191},[62,116767,116768],{"class":85}," // you must return `pos` as-is and reference x and y as `pos.x` and `pos.y`\n",[62,116770,116771],{"class":64,"line":209},[62,116772,116773],{"class":85}," // in the template.\n",[62,116775,116776,116778],{"class":64,"line":220},[62,116777,2599],{"class":68},[62,116779,126],{"class":72},[62,116781,116782,116785,116787],{"class":64,"line":226},[62,116783,116784],{"class":72}," pos: ",[62,116786,116748],{"class":122},[62,116788,2223],{"class":72},[62,116790,116791],{"class":64,"line":231},[62,116792,40087],{"class":72},[62,116794,116795],{"class":64,"line":236},[62,116796,3731],{"class":72},[62,116798,116799],{"class":64,"line":242},[62,116800,107354],{"class":72},[22,116802,116803,116804,116807],{},"This doesn't mean that you can't use reactive though. There is a ",[59,116805,116806],{},"toRefs()"," method that will convert a reactive object to a plain object, where each property on the resulting object is a ref pointing to the corresponding property in the original object.",[52,116809,116811],{"className":105863,"code":116810,"language":105865,"meta":57,"style":57},"function useMousePosition() {\n const pos = reactive({\n x: 0,\n y: 0\n });\n\n // ...\n return toRefs(pos);\n}\n\n// x & y are now refs!\nconst { x, y } = useMousePosition();\n",[59,116812,116813,116821,116833,116841,116847,116851,116855,116859,116869,116873,116877,116882],{"__ignoreMap":57},[62,116814,116815,116817,116819],{"class":64,"line":65},[62,116816,21046],{"class":68},[62,116818,116245],{"class":122},[62,116820,206],{"class":72},[62,116822,116823,116825,116827,116829,116831],{"class":64,"line":76},[62,116824,85414],{"class":68},[62,116826,116579],{"class":149},[62,116828,2556],{"class":68},[62,116830,115663],{"class":122},[62,116832,50544],{"class":72},[62,116834,116835,116837,116839],{"class":64,"line":82},[62,116836,116588],{"class":72},[62,116838,1130],{"class":149},[62,116840,3338],{"class":72},[62,116842,116843,116845],{"class":64,"line":89},[62,116844,116597],{"class":72},[62,116846,72365],{"class":149},[62,116848,116849],{"class":64,"line":95},[62,116850,85488],{"class":72},[62,116852,116853],{"class":64,"line":101},[62,116854,79],{"emptyLinePlaceholder":13},[62,116856,116857],{"class":64,"line":107},[62,116858,116650],{"class":85},[62,116860,116861,116863,116866],{"class":64,"line":113},[62,116862,82091],{"class":68},[62,116864,116865],{"class":122}," toRefs",[62,116867,116868],{"class":72},"(pos);\n",[62,116870,116871],{"class":64,"line":129},[62,116872,379],{"class":72},[62,116874,116875],{"class":64,"line":134},[62,116876,79],{"emptyLinePlaceholder":13},[62,116878,116879],{"class":64,"line":156},[62,116880,116881],{"class":85},"// x & y are now refs!\n",[62,116883,116884,116886,116888,116890,116892,116894,116896,116898,116900],{"class":64,"line":161},[62,116885,110541],{"class":68},[62,116887,21785],{"class":72},[62,116889,85717],{"class":149},[62,116891,976],{"class":72},[62,116893,116508],{"class":149},[62,116895,21795],{"class":72},[62,116897,146],{"class":68},[62,116899,116245],{"class":122},[62,116901,822],{"class":72},[22,116903,116904],{},"As you can see there are some things to consider when creating composition functions. As long as you understand how your functions might be consumed you should be ok.",[26,116906,110999],{"id":110998},[22,116908,116909,116910,116912,116913,116915],{},"When I first started creating components using the Composition API I was confused when to reach for ",[59,116911,114991],{}," and when to favor ",[59,116914,115599],{},". I still might be doing it wrong but until someone tells me I am this is the approach I am going to take. I hope I helped clear up some questions and I would love to hear your feedback below. Thanks for reading, and as always friends...",[22,116917,36004,116918,82545],{},[36006,116919],{},[1527,116921,116922],{},"html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .suaqH, html code.shiki .suaqH{--shiki-default:#22863A;--shiki-dark:#85E89D;--shiki-sepia:#22863A}html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}html pre.shiki code .s4-Ip, html code.shiki .s4-Ip{--shiki-default:#6A737D;--shiki-dark:#6A737D;--shiki-sepia:#6A737D}html pre.shiki code .spoOn, html code.shiki .spoOn{--shiki-default:#E36209;--shiki-dark:#FFAB70;--shiki-sepia:#E36209}",{"title":57,"searchDepth":76,"depth":76,"links":116924},[116925,116926,116932,116933],{"id":114620,"depth":76,"text":114621},{"id":114733,"depth":76,"text":114734,"children":116927},[116928,116929,116930,116931],{"id":115173,"depth":82,"text":115174},{"id":115530,"depth":82,"text":115531},{"id":115586,"depth":82,"text":115587},{"id":115839,"depth":82,"text":115840},{"id":116196,"depth":76,"text":116197},{"id":110998,"depth":76,"text":110999},{"_id":116935,"path":116936,"title":116937,"description":116938,"meta":116939,"body":116946},"content/blog/2020/01/17/vue-3-alpha-cli-plugin.md","/blog/2020/01/17/vue-3-alpha-cli-plugin","Start using Vue 3 in a new project right now","In this tutorial, I walk you through how to add Vue 3 to a new project.",{"slug":116940,"date":116941,"published":13,"tags":116942,"author":17,"cover":116945,"excerpt":-1},"vue-3-alpha-cli-plugin","2020-01-17T13:58:29.936Z",[116943,116944],"vue3","screencast","./start-using-vue3-today-cover.png",{"type":19,"value":116947,"toc":117334},[116948,116951,116954,116958,116991,116993,117120,117124,117309,117311,117331],[22,116949,116950],{},"So you've heard of Vue 3 and you want to start playing around with it but not sure where to start. Did you know that you can add Vue 3 to a new project using the Vue CLI? In this tutorial, I will show you how to create a new project using the Vue CLI and then using a new plugin, add Vue 3 Alpha to your project.",[22,116952,116953],{},"With Vue 3 installed we will walk through creating a new component using the Composition API. We will start by creating a simple Counter component and it might be a trivial example it does allow you to see the building blocks of Vue's new Composition API.",[26,116955,116957],{"id":116956},"sample-code","Sample Code",[52,116959,116961],{"className":32625,"code":116960,"language":32627,"meta":57,"style":57},"# create a Vue 2 project using the Vue CLI\nvue create hello-vue3-sfc\n# in an existing Vue CLI project\nvue add vue-next\n",[59,116962,116963,116968,116977,116982],{"__ignoreMap":57},[62,116964,116965],{"class":64,"line":65},[62,116966,116967],{"class":85},"# create a Vue 2 project using the Vue CLI\n",[62,116969,116970,116972,116974],{"class":64,"line":76},[62,116971,105335],{"class":122},[62,116973,23301],{"class":1675},[62,116975,116976],{"class":1675}," hello-vue3-sfc\n",[62,116978,116979],{"class":64,"line":82},[62,116980,116981],{"class":85},"# in an existing Vue CLI project\n",[62,116983,116984,116986,116988],{"class":64,"line":89},[62,116985,105335],{"class":122},[62,116987,28863],{"class":1675},[62,116989,116990],{"class":1675}," vue-next\n",[636,116992,105973],{"id":105972},[52,116994,116996],{"className":105981,"code":116995,"language":105335,"meta":57,"style":57},"\u003Ctemplate>\n \u003Cdiv id=\"app\">\n \u003Ccounter>\u003C/counter>\n \u003C/div>\n\u003C/template>\n\n\u003Cscript>\nimport Counter from './components/Counter.vue';\n\nexport default {\n name: \"app\",\n components: {\n Counter\n }\n};\n\u003C/script>\n",[59,116997,116998,117006,117020,117033,117041,117049,117053,117061,117075,117079,117087,117095,117099,117104,117108,117112],{"__ignoreMap":57},[62,116999,117000,117002,117004],{"class":64,"line":65},[62,117001,760],{"class":72},[62,117003,105991],{"class":1780},[62,117005,1784],{"class":72},[62,117007,117008,117010,117012,117014,117016,117018],{"class":64,"line":76},[62,117009,33056],{"class":72},[62,117011,15944],{"class":1780},[62,117013,20831],{"class":122},[62,117015,146],{"class":72},[62,117017,49987],{"class":1675},[62,117019,1784],{"class":72},[62,117021,117022,117024,117027,117029,117031],{"class":64,"line":82},[62,117023,1789],{"class":72},[62,117025,117026],{"class":1780},"counter",[62,117028,15857],{"class":72},[62,117030,117026],{"class":1780},[62,117032,1784],{"class":72},[62,117034,117035,117037,117039],{"class":64,"line":89},[62,117036,33187],{"class":72},[62,117038,15944],{"class":1780},[62,117040,1784],{"class":72},[62,117042,117043,117045,117047],{"class":64,"line":95},[62,117044,1818],{"class":72},[62,117046,105991],{"class":1780},[62,117048,1784],{"class":72},[62,117050,117051],{"class":64,"line":101},[62,117052,79],{"emptyLinePlaceholder":13},[62,117054,117055,117057,117059],{"class":64,"line":107},[62,117056,760],{"class":72},[62,117058,15846],{"class":1780},[62,117060,1784],{"class":72},[62,117062,117063,117065,117068,117070,117073],{"class":64,"line":113},[62,117064,27875],{"class":68},[62,117066,117067],{"class":72}," Counter ",[62,117069,3507],{"class":68},[62,117071,117072],{"class":1675}," './components/Counter.vue'",[62,117074,153],{"class":72},[62,117076,117077],{"class":64,"line":129},[62,117078,79],{"emptyLinePlaceholder":13},[62,117080,117081,117083,117085],{"class":64,"line":134},[62,117082,14767],{"class":68},[62,117084,106045],{"class":68},[62,117086,126],{"class":72},[62,117088,117089,117091,117093],{"class":64,"line":156},[62,117090,106052],{"class":72},[62,117092,49987],{"class":1675},[62,117094,3338],{"class":72},[62,117096,117097],{"class":64,"line":161},[62,117098,106062],{"class":72},[62,117100,117101],{"class":64,"line":167},[62,117102,117103],{"class":72}," Counter\n",[62,117105,117106],{"class":64,"line":173},[62,117107,3731],{"class":72},[62,117109,117110],{"class":64,"line":179},[62,117111,107354],{"class":72},[62,117113,117114,117116,117118],{"class":64,"line":185},[62,117115,1818],{"class":72},[62,117117,15846],{"class":1780},[62,117119,1784],{"class":72},[636,117121,117123],{"id":117122},"countervue","Counter.vue",[52,117125,117127],{"className":105981,"code":117126,"language":105335,"meta":57,"style":57},"\u003Ctemplate>\n \u003Cbutton @click=\"increment\">\n Count: {{ state.count }} Double {{ state.double }}\n \u003C/button>\n\u003C/template>\n\n\u003Cscript>\nimport { reactive, computed } from 'vue';\n\nexport default {\n setup() {\n const state = reactive({\n count: 0,\n double: computed(() => state.count * 2)\n });\n function increment() {\n state.count++;\n }\n return { state, increment };\n }\n};\n\u003C/script>\n",[59,117128,117129,117137,117153,117158,117166,117174,117178,117186,117200,117204,117212,117218,117231,117240,117260,117264,117273,117282,117286,117293,117297,117301],{"__ignoreMap":57},[62,117130,117131,117133,117135],{"class":64,"line":65},[62,117132,760],{"class":72},[62,117134,105991],{"class":1780},[62,117136,1784],{"class":72},[62,117138,117139,117141,117143,117146,117148,117151],{"class":64,"line":76},[62,117140,33056],{"class":72},[62,117142,16275],{"class":1780},[62,117144,117145],{"class":122}," @click",[62,117147,146],{"class":72},[62,117149,117150],{"class":1675},"\"increment\"",[62,117152,1784],{"class":72},[62,117154,117155],{"class":64,"line":82},[62,117156,117157],{"class":72}," Count: {{ state.count }} Double {{ state.double }}\n",[62,117159,117160,117162,117164],{"class":64,"line":89},[62,117161,33187],{"class":72},[62,117163,16275],{"class":1780},[62,117165,1784],{"class":72},[62,117167,117168,117170,117172],{"class":64,"line":95},[62,117169,1818],{"class":72},[62,117171,105991],{"class":1780},[62,117173,1784],{"class":72},[62,117175,117176],{"class":64,"line":101},[62,117177,79],{"emptyLinePlaceholder":13},[62,117179,117180,117182,117184],{"class":64,"line":107},[62,117181,760],{"class":72},[62,117183,15846],{"class":1780},[62,117185,1784],{"class":72},[62,117187,117188,117190,117193,117195,117198],{"class":64,"line":113},[62,117189,27875],{"class":68},[62,117191,117192],{"class":72}," { reactive, computed } ",[62,117194,3507],{"class":68},[62,117196,117197],{"class":1675}," 'vue'",[62,117199,153],{"class":72},[62,117201,117202],{"class":64,"line":129},[62,117203,79],{"emptyLinePlaceholder":13},[62,117205,117206,117208,117210],{"class":64,"line":134},[62,117207,14767],{"class":68},[62,117209,106045],{"class":68},[62,117211,126],{"class":72},[62,117213,117214,117216],{"class":64,"line":156},[62,117215,115235],{"class":122},[62,117217,206],{"class":72},[62,117219,117220,117222,117225,117227,117229],{"class":64,"line":161},[62,117221,36857],{"class":68},[62,117223,117224],{"class":149}," state",[62,117226,2556],{"class":68},[62,117228,115663],{"class":122},[62,117230,50544],{"class":72},[62,117232,117233,117236,117238],{"class":64,"line":167},[62,117234,117235],{"class":72}," count: ",[62,117237,1130],{"class":149},[62,117239,3338],{"class":72},[62,117241,117242,117245,117247,117249,117251,117254,117256,117258],{"class":64,"line":173},[62,117243,117244],{"class":72}," double: ",[62,117246,116148],{"class":122},[62,117248,797],{"class":72},[62,117250,21525],{"class":68},[62,117252,117253],{"class":72}," state.count ",[62,117255,6973],{"class":68},[62,117257,26797],{"class":149},[62,117259,2212],{"class":72},[62,117261,117262],{"class":64,"line":179},[62,117263,906],{"class":72},[62,117265,117266,117269,117271],{"class":64,"line":185},[62,117267,117268],{"class":68}," function",[62,117270,203],{"class":122},[62,117272,206],{"class":72},[62,117274,117275,117278,117280],{"class":64,"line":191},[62,117276,117277],{"class":72}," state.count",[62,117279,215],{"class":68},[62,117281,153],{"class":72},[62,117283,117284],{"class":64,"line":209},[62,117285,223],{"class":72},[62,117287,117288,117290],{"class":64,"line":220},[62,117289,2599],{"class":68},[62,117291,117292],{"class":72}," { state, increment };\n",[62,117294,117295],{"class":64,"line":226},[62,117296,3731],{"class":72},[62,117298,117299],{"class":64,"line":231},[62,117300,107354],{"class":72},[62,117302,117303,117305,117307],{"class":64,"line":236},[62,117304,1818],{"class":72},[62,117306,15846],{"class":1780},[62,117308,1784],{"class":72},[26,117310,4098],{"id":4097},[915,117312,117313,117319,117325],{},[37,117314,117315],{},[677,117316,117317],{"href":117317,"rel":117318},"https://github.com/vuejs/vue-next",[681],[37,117320,117321],{},[677,117322,117323],{"href":117323,"rel":117324},"https://github.com/vuejs/vue-next-webpack-preview",[681],[37,117326,117327],{},[677,117328,117329],{"href":117329,"rel":117330},"https://github.com/vuejs/vue-cli-plugin-vue-next",[681],[1527,117332,117333],{},"html pre.shiki code .s4-Ip, html code.shiki .s4-Ip{--shiki-default:#6A737D;--shiki-dark:#6A737D;--shiki-sepia:#6A737D}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .suaqH, html code.shiki .suaqH{--shiki-default:#22863A;--shiki-dark:#85E89D;--shiki-sepia:#22863A}html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}",{"title":57,"searchDepth":76,"depth":76,"links":117335},[117336,117340],{"id":116956,"depth":76,"text":116957,"children":117337},[117338,117339],{"id":105972,"depth":82,"text":105973},{"id":117122,"depth":82,"text":117123},{"id":4097,"depth":76,"text":4098},{"_id":117342,"path":117343,"title":117344,"description":117345,"meta":117346,"body":117351},"content/blog/2020/01/13/codemash-2020-recap.md","/blog/2020/01/13/codemash-2020-recap","CodeMash 2020 Recap","A recap of the conference CodeMash 2020 as both an attendee and a speaker.",{"slug":117347,"date":117348,"published":13,"tags":117349,"author":17,"cover":117350,"excerpt":-1},"codemash-2020-recap","2020-01-13T14:30:00.000Z",[105334,105335],"./codemash-recap-cover.png",{"type":19,"value":117352,"toc":117523},[117353,117356,117360,117363,117367,117370,117376,117380,117383,117387,117395,117398,117404,117408,117416,117419,117425,117428,117432,117440,117443,117449,117453,117461,117470,117476,117480,117483,117487,117490,117496,117504,117510,117518,117520],[22,117354,117355],{},"I'm back home after attending and speaking at my first CodeMash and I would like to tell you all about it. The highlights for me were the awesome venue, being able to take my family with me and getting to spend some time with my friend Todd who I haven't seen in awhile.",[26,117357,117359],{"id":117358},"codemash-newbie","CodeMash Newbie",[22,117361,117362],{},"The first thing I need to say is that I have been to a decent amount of conferences in my life and this was one of the most organized and well run conferences I have ever attended. I just want to give a huge kudos to everyone involved in putting this conference together. The venue was amazing, the sessions were great and I couldn't think of a better way to kick off the new year.",[26,117364,117366],{"id":117365},"kalahari","Kalahari",[22,117368,117369],{},"I was lucky enough to bring my wife and daughter with me to Kalahari so this conference doubled as a nice little vacation for the family. This was my first trip to Kalahari and we had a blast. The water park had so many things to do for everyone. They also had a great assortment of places to eat and drink. I will definitely be taking the family back there one day.",[22,117371,117372],{},[653,117373],{"alt":117374,"src":117375},"Bella & Dad at Kalahari","/images/blog/2020/01/13/bella_codemash.jpg",[26,117377,117379],{"id":117378},"sessions-workshops-i-attended","Sessions & Workshops I attended",[22,117381,117382],{},"I attended a lot of sessions and workshops but these are a few that I want to share with you.",[636,117384,117386],{"id":117385},"java-9-10-11-workshop","Java 9, 10 & 11 Workshop",[22,117388,117389,117390,2712],{},"Presented by: Chris Judd (",[677,117391,117394],{"href":117392,"rel":117393},"https://twitter.com/javajudd",[681],"@javajudd",[22,117396,117397],{},"This was a really great workshop that went into some of the new features in Java 9, 10 & 11. Most companies are stuck on Java 8 so a lot of these features really are new to people. Chris did a really great job of opening the workshop by answering some questions around licensing and LTS (long term support) which really confuses a lot of people.",[22,117399,117400],{},[677,117401,117402],{"href":117402,"rel":117403},"https://twitter.com/therealdanvega/status/1214969350869995520",[681],[636,117405,117407],{"id":117406},"calculating-insulin-with-automated-carb-counting-using-ai-ml-and-web-bluetooth","Calculating Insulin With Automated Carb Counting Using AI, ML and Web Bluetooth",[22,117409,117410,117411,2712],{},"Presented by: Todd Sharp (",[677,117412,117415],{"href":117413,"rel":117414},"https://twitter.com/recursivecodes",[681],"@recursivecodes",[22,117417,117418],{},"Not only is Todd a good friend but he is a better human being and developer. In this session Todd shows off the app he built to help is daughter out who was diagnosed with Type 1 Diabetes. There is a lot of cool tech and code that went into building this application and it was fun to see how it all goes together. I really enjoyed how Todd closed his presentation by saying we all have passions outside of coding, try and take your coding skills and your passions and do something good for this world. Kudos to you my friend.",[22,117420,117421],{},[677,117422,117423],{"href":117423,"rel":117424},"https://twitter.com/therealdanvega/status/1215689790756179968",[681],[22,117426,117427],{},"The picture above is a really bad picture by me because it looks like there are no people in the talk. I zoomed in on Todd and I just need to point that out because it was a well attended session. Sorry Todd 🤦♂️",[636,117429,117431],{"id":117430},"building-a-super-performant-gossipgirlcom-in-gatsby-in-under-50-minutes","Building a super performant GossipGirl.com in Gatsby in under 50 Minutes",[22,117433,117434,117435,2712],{},"Presented by: Jennifer Wadella (",[677,117436,117439],{"href":117437,"rel":117438},"https://twitter.com/likeOMGitsFEDAY",[681],"@likeOMGitsFEDAY",[22,117441,117442],{},"I am not a React developer but I have heard so many great things about Gatsby so I thought I would check out this session and I am glad I did. Jennifer is a really great presenter who had a lot of fun with this talk and brought some great energy to the morning crowd. She ended up live coding an entire website from scratch and did an amazing job with it.",[22,117444,117445],{},[677,117446,117447],{"href":117447,"rel":117448},"https://twitter.com/therealdanvega/status/1215661794238324739",[681],[636,117450,117452],{"id":117451},"supersonic-subatomic-java","Supersonic, Subatomic Java",[22,117454,117455,117456,2712],{},"Presented by: Scott Seighman (",[677,117457,117460],{"href":117458,"rel":117459},"https://twitter.com/ScottSeighman",[681],"@ScottSeighman",[22,117462,117463,117464,117469],{},"I have known Scott for a long time and I was excited to run into him at the conference. He told about this project he was working on at RedHat called ",[677,117465,117468],{"href":117466,"rel":117467},"https://quarkus.io/",[681],"Quarkus"," and that he was giving a presentation on it. I was really interested in this for writing AWS Lambda functions. While you can use Java to write your serverless functions startup times and memory consumption with using the out of the box Java 11 runtime are a big issue. With the small memory footprint and really fast startup times I think this could be the answer I have been looking for.",[22,117471,117472],{},[677,117473,117474],{"href":117474,"rel":117475},"https://twitter.com/therealdanvega/status/1215703419543003136",[681],[26,117477,117479],{"id":117478},"pool-party","Pool Party",[22,117481,117482],{},"I am really glad that I was selected to speak on Thursday so that I could enjoy the pool party on Thursday night. The water park opened up for CodeMash attendees at 10 PM and I went down with some friends. We go to go on some really cool slides and relax in the swim bar. It was a lot of fun and if you plan on attending CodeMash in the future I would try and make the party.",[26,117484,117486],{"id":117485},"my-session-vue-3-smaller-faster-stronger","My Session: Vue 3 - Smaller, Faster & Stronger",[22,117488,117489],{},"I put a lot of time and effort into this presentation and at the end of the day I think it really showed. The session was well attended and the audience was engaged. I got a lot of really great feedback after the talk which really makes me so happy and I feel incredibly lucky to be a speaker at such an amazing conference. My wife and daughter hung out in the back and watched the beginning of my talk and that made it all the more special.",[22,117491,117492],{},[653,117493],{"alt":117494,"src":117495},"Vue 3: Smaller, Faster & Stronger","/images/blog/2020/01/13/danvega_vue3_session.jpg",[22,117497,117498,117499,117503],{},"I als want to really thank everyone at ",[677,117500,104491],{"href":117501,"rel":117502},"https://www.techelevator.com",[681]," for supporting me. It means a lot to work for such a great company where everyone shares the same mission.",[22,117505,117506],{},[677,117507,117508],{"href":117508,"rel":117509},"https://twitter.com/Tech_Elevator/status/1215730352582512640",[681],[22,117511,117512,117513,117517],{},"Thank you to the CodeMash selection committee for such a privilege. If you want to learn more about the talk I have a ",[677,117514,117516],{"href":114602,"rel":117515},[681],"blog post here"," that has details and resources about it. The talk was recorded by Pluralsight and I will let you all know when it has been released to the public.",[26,117519,1499],{"id":1498},[22,117521,117522],{},"CodeMash reminded me just how much I love going to & speaking at conferences. I am going to make a huge effort to submit more CFPs this year and I hope to see all of you at a conference near you. I also look forward to attending CodeMash next year as an attendee and hopefully speaker!",{"title":57,"searchDepth":76,"depth":76,"links":117524},[117525,117526,117527,117533,117534,117535],{"id":117358,"depth":76,"text":117359},{"id":117365,"depth":76,"text":117366},{"id":117378,"depth":76,"text":117379,"children":117528},[117529,117530,117531,117532],{"id":117385,"depth":82,"text":117386},{"id":117406,"depth":82,"text":117407},{"id":117430,"depth":82,"text":117431},{"id":117451,"depth":82,"text":117452},{"id":117478,"depth":76,"text":117479},{"id":117485,"depth":76,"text":117486},{"id":1498,"depth":76,"text":1499},{"_id":117537,"path":117538,"title":117539,"description":117540,"meta":117541,"body":117546},"content/blog/2020/01/09/codemash-2020.md","/blog/2020/01/09/codemash-2020","Vue3: Smaller, Faster & Stronger. CodeMash 2020","This post is a collection of resources for my presentation at CodeMash 2020.",{"slug":117542,"date":117543,"published":13,"tags":117544,"author":17,"cover":117545,"excerpt":-1},"codemash-2020","2020-01-09T09:00:00.000Z",[105335],"./codemash-vue3.png",{"type":19,"value":117547,"toc":117714},[117548,117552,117555,117558,117561,117574,117580,117583,117621,117624,117626,117628,117631,117634,117637,117643,117646,117649,117655,117657,117692,117696,117699,117705,117707,117710],[26,117549,117551],{"id":117550},"overview","Overview",[22,117553,117554],{},"This was one of the more interesting & challenging presentations that I have had to put together. Usually when I create a presentation I have a wealth of knowledge and examples to fall back on. In the case of Vue 3 still being in alpha I didn't have a lot to go on. This took a lot of tinkering around with the source code which I love to do so I was up for the challenge.",[22,117556,117557],{},"I think I watched a few presentations by Evan You at least a dozen times, so Thank You Evan. I also want to give a huge kudos to the entire Vue Core team for the RFCs. These documents were extremely detailed and I loved the fact that they were explicit with the motivation behind each feature. These were the two main resources I used for both learning the new features of Vue 3 and putting together this presentation.",[22,117559,117560],{},"Going into this talk I had higher hopes of showing off more features than I actually covered. I think this is usually the case though whenever I start thinking about a talk and then realizing I only have an hour. I ended up with 134 slides which is extremely ambitious but I will get through them.",[22,117562,117563,117564,19931,117567,117570,117571,2755],{},"After taking a look at all the new features in Vue 3 it became clear to me that there were a couple of goals with this release. First, they wanted to make the framework ",[646,117565,117566],{},"Smaller",[646,117568,117569],{},"Faster"," (which they accomplished) and a lot of what I covered aligned with those goals. Second, they wanted to make the framework scalable and for me that is where the Composition API shines and makes Vue ",[646,117572,117573],{},"Stronger",[22,117575,117576],{},[653,117577],{"alt":117578,"src":117579},"Slide Deck Cover","/images/blog/2020/01/09/presentation-cover.png",[22,117581,117582],{},"This is how I ended up coming up with the title \"Vue 3: Smaller, Faster and Stronger\" for my presentation. These are a list of features I ended up talking about that really fit into those buckets.",[915,117584,117585,117602,117613],{},[37,117586,117587,117588],{},"Smaller\n",[915,117589,117590,117593,117596,117599],{},[37,117591,117592],{},"TypeScript Rewrite",[37,117594,117595],{},"Global API Change",[37,117597,117598],{},"Treeshaking",[37,117600,117601],{},"Fragments",[37,117603,117604,117605],{},"Faster\n",[915,117606,117607,117610],{},[37,117608,117609],{},"Virtual DOM",[37,117611,117612],{},"Reactivity System",[37,117614,117615,117616],{},"Stronger\n",[915,117617,117618],{},[37,117619,117620],{},"Composition API",[22,117622,117623],{},"Below are some resources such as slides, a Github repository with demo code and any links I mentioned during the presentation.",[26,117625,4098],{"id":4097},[636,117627,8434],{"id":8433},[22,117629,117630],{},"For now I have exported my slides to a PDF that you can find in the Github repository below. I might share these out in additional formats later but I am curious to find out how speakers are sharing the keynote presentations so please let me know.",[636,117632,50830],{"id":117633},"github",[22,117635,117636],{},"This repository contains most of the examples that I showed off during my presentation. There was no live coding so most of this code was just shown on slides.",[22,117638,117639],{},[677,117640,117641],{"href":117641,"rel":117642},"https://github.com/danvega/vue3-smaller-faster-stronger",[681],[636,117644,37667],{"id":117645},"articles",[22,117647,117648],{},"I wrote an article for Vue Mastery on some of my favorite resources for learning Vue 3.",[22,117650,117651],{},[677,117652,117653],{"href":117653,"rel":117654},"https://www.vuemastery.com/blog/top-ways-to-learn-Vue-3",[681],[636,117656,37639],{"id":86173},[915,117658,117659,117665,117671,117678,117685],{},[37,117660,117661],{},[677,117662,117664],{"href":117317,"rel":117663},[681],"Vue Next",[37,117666,117667],{},[677,117668,117670],{"href":117323,"rel":117669},[681],"Vue 3 + Webpack Setup",[37,117672,117673],{},[677,117674,117677],{"href":117675,"rel":117676},"https://github.com/vuejs/rfcs",[681],"Vue Request for Comments (RFCs)",[37,117679,117680],{},[677,117681,117684],{"href":117682,"rel":117683},"https://vue-composition-api-rfc.netlify.com/",[681],"Composition API RFC",[37,117686,117687],{},[677,117688,117691],{"href":117689,"rel":117690},"https://vue-composition-api-rfc.netlify.com/api.html",[681],"Composition API Reference",[636,117693,117695],{"id":117694},"vue-3-course","Vue 3 Course",[22,117697,117698],{},"I am currently building out a curriculum for the upcoming release of Vue 3 . If you're interested in learning about all of the new and exciting features this course is for you. Please use the form on this page and you will receive updates as they become available.",[22,117700,117701],{},[677,117702,117703],{"href":117703,"rel":117704},"https://www.danvega.dev/courses/vue3",[681],[26,117706,1499],{"id":1498},[22,117708,117709],{},"If you attended this presentation I really hope you enjoyed it. I put a lot of work into this and my hope is that it showed. I am hoping to give this presentation at more conferences and meetups this year so if you're interested in me presenting please reach out to me. As always friends...",[22,117711,36004,117712,36008],{},[36006,117713],{},{"title":57,"searchDepth":76,"depth":76,"links":117715},[117716,117717,117724],{"id":117550,"depth":76,"text":117551},{"id":4097,"depth":76,"text":4098,"children":117718},[117719,117720,117721,117722,117723],{"id":8433,"depth":82,"text":8434},{"id":117633,"depth":82,"text":50830},{"id":117645,"depth":82,"text":37667},{"id":86173,"depth":82,"text":37639},{"id":117694,"depth":82,"text":117695},{"id":1498,"depth":76,"text":1499},{"_id":117726,"path":117727,"title":117728,"description":117729,"meta":117730,"body":117735},"content/blog/2019/12/19/up-and-running-with-vuejs.md","/blog/2019/12/19/up-and-running-with-vuejs","New Course: Up & Running with Vue.js","I released a new course on how to get up and running with Vue.js for beginners",{"slug":117731,"date":117732,"published":13,"tags":117733,"author":17,"cover":117734,"excerpt":-1},"up-and-running-with-vuejs","2019-12-19T15:30:00.000Z",[105335],"./up-and-running-with-vuejs-cover.png",{"type":19,"value":117736,"toc":117912},[117737,117752,117756,117761,117764,117767,117771,117776,117779,117782,117787,117790,117795,117798,117803,117806,117810,117815,117818,117822,117901,117903,117906],[22,117738,117739,117740,117745,117746,117751],{},"This is a course that I have wanted to make for a long time now and I am happy to announce that ",[677,117741,117744],{"href":117742,"rel":117743},"https://www.udemy.com/course/vue-intro/?couponCode=4F5DE476322755E7AFBP",[681],"it is finally here",". If you follow me at all you know that I have been a big fan of ",[677,117747,117750],{"href":117748,"rel":117749},"https://vuejs.org/",[681],"Vue.JS"," for awhile now. I use it personally on this blog, we teach it at Tech Elevator and now I have my own course that teaches the very basics of Vue.",[26,117753,117755],{"id":117754},"course-motivation","Course Motivation",[22,117757,117758],{},[653,117759],{"alt":117755,"src":117760},"/images/blog/2019/12/19/mia-baker-CuoMduHwRZY-unsplash.jpg",[22,117762,117763],{},"For most of my career I have primarily been a back-end developer. I have always still had to work on projects where I would be doing front-end development but I can honestly say I never liked it. It was always something I wanted to get better at but the tools available to me weren't that great.",[22,117765,117766],{},"A little less than two years ago I found Vue and it was just so easy to get started with. About the same time CSS started making some advancements with Flexbox and CSS Grid. JavaScript and the Browser API support have also made working on the client a lot more enjoyable. When you combine all of that with just how awesome some of these front-end frameworks like Vue are, it's a great time to be a front-end developer.",[26,117768,117770],{"id":117769},"course-goals","Course Goals",[22,117772,117773],{},[653,117774],{"alt":117770,"src":117775},"/images/blog/2019/12/19/s-o-c-i-a-l-c-u-t-6iYb1BWWbV0-unsplash.jpg",[22,117777,117778],{},"When I started looking at the current landscape of courses in the Vue community I noticed a couple of things. First, there are some really great courses out there by some amazing instructors. I also noticed that a lot of the introduction material came bundled with hours of additional content that might not be relevant for a beginner. It also could be pretty intimidating to want to learn something new and find a course and that is 25 hours long.",[22,117780,117781],{},"With that, I set out to create a course that taught the basics of Vue to beginners. I had a few goals in mind when I started designing the curriculum for this course.",[22,117783,117784],{},[646,117785,117786],{},"#1 Keep the course length short",[22,117788,117789],{},"Like Vue, I wanted this course to be approachable and I felt like 1-3 hours was a good target. This course comes with 2 hours of video content and its packed with tons of extras.",[22,117791,117792],{},[646,117793,117794],{},"#2 Backup lessons with quizzes, exercises, and coding challenges",[22,117796,117797],{},"If you're going to learn how to code you can't just watch someone else do it. That is why I really wanted to make sure that this course included quizzes, exercises, and coding challenges. The coding challenges ask you to build something with what you just learned in the lesson.",[22,117799,117800],{},[646,117801,117802],{},"#3 Keep it Simple",[22,117804,117805],{},"I wanted to target developers with HTML/CSS/JS experience. This meant that I wanted you to just be able to drop a script tag on a page and go. No prior knowledge of Node or NPM would be needed and we would stay away from installing any command-line interfaces.",[26,117807,117809],{"id":117808},"course-curriculum","Course Curriculum",[22,117811,117812],{},[653,117813],{"alt":117809,"src":117814},"/images/blog/2019/12/19/tim-mossholder-WE_Kv_ZB1l0-unsplash.jpg",[22,117816,117817],{},"This course is really geared towards beginners who know HTML, CSS & JavaScript but maybe haven't jumped into a framework just yet. If that sounds like you I think Vue is the perfect framework to learn. Below is a trailer for the course and an outline of the curriculum.",[104354,117819],{"width":104356,"src":117820,"frameBorder":1130,"allow":117821,"allowFullScreen":13},"https://www.youtube.com/embed/CtQGbnNxhHk","accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture",[915,117823,117824,117851,117887],{},[37,117825,117826,117827],{},"Introduction\n",[915,117828,117829,117831,117834,117837,117840,117848],{},[37,117830,112122],{},[37,117832,117833],{},"What is Vue",[37,117835,117836],{},"Why use Vue",[37,117838,117839],{},"Moving from jQuery to Vue",[37,117841,117842,117843],{},"Hello, World!\n",[915,117844,117845],{},[37,117846,117847],{},"Coding Exercise",[37,117849,117850],{},"Coding Challenge",[37,117852,117853,117854],{},"Vue Core Concepts\n",[915,117855,117856,117858,117861,117864,117872,117875,117878,117881,117884],{},[37,117857,52935],{},[37,117859,117860],{},"Data Binding: Expressions & Directives",[37,117862,117863],{},"Lists",[37,117865,117866,117867],{},"Counter Application\n",[915,117868,117869],{},[37,117870,117871],{},"Counter Application Coding Challenge",[37,117873,117874],{},"Two Way Data Binding",[37,117876,117877],{},"Timer Component",[37,117879,117880],{},"Creating Vue Components",[37,117882,117883],{},"Core Concepts Quiz",[37,117885,117886],{},"Final Coding Challenge",[37,117888,117889,117890],{},"Resource & Next Steps\n",[915,117891,117892,117895,117898],{},[37,117893,117894],{},"Thank You",[37,117896,117897],{},"Resource & Next Steps",[37,117899,117900],{},"Bonus Lesson",[26,117902,1499],{"id":1498},[22,117904,117905],{},"I am really happy with the way the course turned out and I hope you find some real value in it. You can use the link below to check out the course landing page. So what are you waiting for, click that enroll button and I will see you inside!",[22,117907,117908],{},[677,117909,117911],{"href":117742,"rel":117910},[681],"Up & Running with Vue.js",{"title":57,"searchDepth":76,"depth":76,"links":117913},[117914,117915,117916,117917],{"id":117754,"depth":76,"text":117755},{"id":117769,"depth":76,"text":117770},{"id":117808,"depth":76,"text":117809},{"id":1498,"depth":76,"text":1499},{"_id":117919,"path":117920,"title":117921,"description":117922,"meta":117923,"body":117928},"content/blog/2019/11/21/spring-boot-visual-studio-code.md","/blog/2019/11/21/spring-boot-visual-studio-code","Spring Boot in Visual Studio Code","In this tutorial I will show you how to create a new Spring Boot application in Visual Studio Code",{"slug":117924,"date":117925,"published":13,"tags":117926,"author":17,"cover":117927,"excerpt":-1},"spring-boot-visual-studio-code","2019-11-21T11:28:07.266Z",[2925,56],"./spring-boot-in-vscode.png",{"type":19,"value":117929,"toc":117987},[117930,117933,117936,117939,117942,117946,117949,117952,117968,117972,117975,117978,117980,117983],[22,117931,117932],{},"I feel very lucky that I get to work with so many different programming languages & frameworks on a day to day basis. This wouldn't be as much fun if I had to bounce around using different IDE's, editors and tools.",[22,117934,117935],{},"Thankfully we have a tool like Visual Studio Code that has support for so many languages, frameworks, and a really great extension ecosystem. It is because of this I can switch between languages like Java, C# and Go or Frameworks like Spring Boot, .NET and Java and still be productive.",[22,117937,117938],{},"I mentioned to someone that I was creating Spring Boot Applications in Visual Studio Code and the reaction was \"You can do that?\". With that, I decided to put together a tutorial on how to create a Spring Boot Application in Visual Studio Code.",[22,117940,117941],{},"Assuming you already have VS Code installed, you're probably wondering \"What extensions do I need to install?\"",[26,117943,117945],{"id":117944},"visual-studio-code-extension-packs","Visual Studio Code Extension Packs",[22,117947,117948],{},"I don't know if a lot of people know about this, but there are some really good extensions for Visual Studio Code if you're a Java developer.",[22,117950,117951],{},"The problem with getting up and running with a new language or framework is that there can be a lot of extensions that you need to install. Fortunately for us, there is a feature in VS Code called extension packs. This allows a single extension, the extension pack, to group multiple extensions into one. Instead of asking you to go out and install 10 extensions, you just need to install these two.",[915,117953,117954,117961],{},[37,117955,117956],{},[677,117957,117960],{"href":117958,"rel":117959},"https://marketplace.visualstudio.com/items?itemName=vscjava.vscode-java-pack",[681],"Java Extension Pack",[37,117962,117963],{},[677,117964,117967],{"href":117965,"rel":117966},"https://marketplace.visualstudio.com/items?itemName=Pivotal.vscode-boot-dev-pack",[681],"Spring Boot Extension Pack",[26,117969,117971],{"id":117970},"spring-boot-in-visual-studio-code-tutorial","Spring Boot in Visual Studio Code Tutorial",[22,117973,117974],{},"With Visual Studio Code and the proper extensions installed you are ready to create a new Spring Boot Application. In this tutorial, you will create a Spring Boot REST API and then a simple Vue application that talks to it.",[104354,117976],{"width":104356,"src":117977,"frameBorder":1130,"allow":117821,"allowFullScreen":13},"https://www.youtube.com/embed/5mpHejytgFE",[26,117979,1499],{"id":1498},[22,117981,117982],{},"I hope you found value in this tutorial. Are you creating Spring Boot Applications in Visual Studio Code? If you are and have any tips or tricks for my readers please leave them below. As always friends...",[22,117984,36004,117985,82545],{},[36006,117986],{},{"title":57,"searchDepth":76,"depth":76,"links":117988},[117989,117990,117991],{"id":117944,"depth":76,"text":117945},{"id":117970,"depth":76,"text":117971},{"id":1498,"depth":76,"text":1499},{"_id":117993,"path":117994,"title":117995,"description":117996,"meta":117997,"body":118002},"content/blog/2019/10/29/unit-testing-vue-part-03.md","/blog/2019/10/29/unit-testing-vue-part-03","A Beginner's Guide to Unit Testing in Vue: Part 3","In Part 3 of this series we learn how to write unit tests in Vue by testing more complex components.",{"slug":117998,"date":117999,"published":13,"tags":118000,"author":17,"cover":118001,"excerpt":-1},"unit-testing-vue-part-03","2019-10-29T15:30:00.000Z",[105335],"./unit-testing-vue-mastery-03-cover.jpg",{"type":19,"value":118003,"toc":118043},[118004,118007,118030,118033,118036,118039],[22,118005,118006],{},"This is the third and final article in the series I did for Vue Mastery. You can read all three of these articles over on Vue Mastery:",[915,118008,118009,118016,118023],{},[37,118010,118011],{},[677,118012,118015],{"href":118013,"rel":118014},"https://www.vuemastery.com/blog/unit-testing-vue-1",[681],"Unit Testing in Vue: What to test",[37,118017,118018],{},[677,118019,118022],{"href":118020,"rel":118021},"https://www.vuemastery.com/blog/Unit-Testing-in-Vue-Your-First-Test",[681],"Unit Testing in Vue: Writing your first Test",[37,118024,118025],{},[677,118026,118029],{"href":118027,"rel":118028},"https://www.vuemastery.com/blog/Unit-Testing-in%20Vue-More-complex-components",[681],"Unit Testing in Vue: Testing More Complex Components",[22,118031,118032],{},"In part one of the series we really focused on identifying what to test and more importantly what NOT to test in your components. From there we wrote our first test in part two and spent some time understanding the different libraries involved. In part three we learn through repetition by writing more unit tests with a couple of components that add some new wrinkles.",[22,118034,118035],{},"I want to thank Adam and Gregg over at Vue Mastery for giving me this opportunity to write for them. I have always looked up to them for their unique style of teaching and everything they do for the Vue community. I don't think this is the last time you will see me collaborating with them and I am looking forward to what we do next.",[22,118037,118038],{},"I put a lot of hard work into this series and if you enjoyed it please let them know. I hope you're having a wonderful day and as always friends....",[22,118040,36004,118041,82545],{},[36006,118042],{},{"title":57,"searchDepth":76,"depth":76,"links":118044},[],{"_id":118046,"path":118047,"title":118048,"description":118049,"meta":118050,"body":118055},"content/blog/2019/10/15/unit-testing-vue-part-02.md","/blog/2019/10/15/unit-testing-vue-part-02","A Beginner's Guide to Unit Testing in Vue: Part 2","In the 2nd part of this 3 part series we will look at writing and running your first unit test in Vue",{"slug":118051,"date":118052,"published":13,"tags":118053,"author":17,"cover":118054,"excerpt":-1},"unit-testing-vue-part-02","2019-10-15T12:33:53.592Z",[105335],"./unit-testing-part-02-cover.jpg",{"type":19,"value":118056,"toc":118116},[118057,118071,118079,118104],[22,118058,118059,118060,118065,118066,118070],{},"If you're interested in getting started with Unit Testing in Vue I have something that is really going to help you out. I have been working on a 3 part series for my friends over at ",[677,118061,118064],{"href":118062,"rel":118063},"https://www.vuemastery.com",[681],"VueMastery"," In ",[677,118067,118069],{"href":118013,"rel":118068},[681],"part 1 of the series"," we talked about what to test and more importantly what not to test.",[22,118072,118073,118074,118078],{},"I am happy to announce that ",[677,118075,118077],{"href":118020,"rel":118076},[681],"part 2 of this series"," was released yesterday. Now that you know what to test in your Vue components it's time to write some code. In this article, we dive into writing and running your first tests. In this article you will learn:",[915,118080,118081,118084,118087,118101],{},[37,118082,118083],{},"How to add Unit Testing to your project using the Vue CLI",[37,118085,118086],{},"What are the different libraries you will be using",[37,118088,118089,118090],{},"Writing your first test\n",[915,118091,118092,118095,118098],{},[37,118093,118094],{},"What is Jest?",[37,118096,118097],{},"Understanding assertions",[37,118099,118100],{},"Introduction to Vue Test Utils",[37,118102,118103],{},"Running your first test",[22,118105,118106,118107,118110,118111,118115],{},"I hope you enjoy this article and if you do please let me know on ",[677,118108,86336],{"href":86334,"rel":118109},[681]," and let the team over at ",[677,118112,118064],{"href":118113,"rel":118114},"https://twitter.com/VueMastery",[681]," know that you want me to keep writing for them 😉",{"title":57,"searchDepth":76,"depth":76,"links":118117},[],{"_id":118119,"path":118120,"title":118121,"description":118122,"meta":118123,"body":118128},"content/blog/2019/10/01/unit-testing-vue-part-01.md","/blog/2019/10/01/unit-testing-vue-part-01","A beginner's guide to Unit Testing in Vue: Part 1","Unit Testing in Vue: What to test?",{"slug":118124,"date":118125,"published":13,"tags":118126,"author":17,"cover":118127,"excerpt":-1},"unit-testing-vue-part-01","2019-10-01T17:00:00.000Z",[105335],"unit-testing-vue-mastery-01.png",{"type":19,"value":118129,"toc":118173},[118130,118139,118142,118153,118158,118161,118167],[22,118131,118132,118133,118138],{},"If you're ",[677,118134,118137],{"href":118135,"rel":118136},"https://www.danvega.dev/signup/",[681],"subscribed to my newsletter"," than you know I have been working on a series of articles for my friends over at Vue Mastery for a little while now. The series is titled \"A beginner's guide: Unit Testing in Vue\" and has been broken into 3 parts. I am really excited to announce that the first article in this series is now live.",[22,118140,118141],{},"In this first post we take a look at why you should test, what you should test and more importantly what not to test. In this article you will learn:",[915,118143,118144,118147,118150],{},[37,118145,118146],{},"Why are we writing tests to begin with? What are the goals of testing?",[37,118148,118149],{},"How to Identify what you should (and should NOT) be testing",[37,118151,118152],{},"What are the different tools used to write these tests",[22,118154,118155],{},[677,118156,118013],{"href":118013,"rel":118157},[681],[22,118159,118160],{},"A special thanks to Adam Jahr for the help in getting this article published. I also want to thank Vue Mastery and VueJS for these tweets.",[22,118162,118163],{},[677,118164,118165],{"href":118165,"rel":118166},"https://twitter.com/VueMastery/status/1177696674787012609",[681],[22,118168,118169],{},[677,118170,118171],{"href":118171,"rel":118172},"https://twitter.com/vuejs/status/1177701970305527809",[681],{"title":57,"searchDepth":76,"depth":76,"links":118174},[],{"_id":118176,"path":118177,"title":118178,"description":118179,"meta":118180,"body":118185},"content/blog/2019/08/08/css-grid-generator.md","/blog/2019/08/08/css-grid-generator","Learn how to use CSS Grid with a FREE tool CSS Grid Generator","Learn how to create a CSS Grid Layout with a free tool called CSS Grid Generator",{"slug":118181,"date":118182,"published":13,"tags":118183,"author":17,"cover":118184,"excerpt":-1},"css-grid-generator","2019-08-08T18:13:57.456Z",[32877,105335],"./css-grid-generator-cover.png",{"type":19,"value":118186,"toc":119693},[118187,118190,118193,118205,118208,118213,118219,118223,118237,118242,118246,118249,118254,118257,118271,118274,118279,118282,118287,118290,118301,118306,118313,118318,118327,118331,118334,118605,118612,118792,118795,119267,119277,119461,119471,119529,119532,119537,119546,119652,119655,119659,119662,119664,119677,119686,119690],[22,118188,118189],{},"As someone who has been developing web applications for almost 20 years now, I have seen it all. I started in FrontPage, moved to Dreamweaver, nested tables inside of tables and learned how to float and clear fix everything.",[22,118191,118192],{},"While you won't see any awards for my designs on my bookshelf I am able to create some very nice simple and clean layouts. That said I have never really enjoyed it because it has always been a huge pain in the a$$. I have always felt like I was just hacking away until my layout looked almost like what I had in mind.",[22,118194,118195,118196,51560,118200,118204],{},"This is why when tools like ",[677,118197,113493],{"href":118198,"rel":118199},"https://css-tricks.com/snippets/css/complete-guide-grid/",[681],[677,118201,113490],{"href":118202,"rel":118203},"https://css-tricks.com/snippets/css/a-guide-to-flexbox/",[681]," came along I was beyond excited about the possibilities of creating clean layouts. I think if you combine these 2 technologies along with just how far JavaScript has come it is what has really got me excited about front end development these days.",[22,118206,118207],{},"While I have a pretty good understanding of CSS Grid at this point I know it can be a little bit confusing to get started. That is why I am really excited to share with you a new tool that I came across that I really think can help you out if you're trying to understand CSS Grid.",[22,118209,118210],{},[653,118211],{"alt":57,"src":118212},"/images/blog/2019/08/08/undraw_web_developer_p3e5-bc5394ac-2ec5-4a11-894c-10168231520e.png",[22,118214,118215],{},[677,118216,118217],{"href":118217,"rel":118218},"https://undraw.co/",[681],[26,118220,118222],{"id":118221},"css-grid-generator-by-sarah-drasner","CSS Grid Generator by Sarah Drasner",[22,118224,118225,118230,118231,118236],{},[677,118226,118229],{"href":118227,"rel":118228},"https://cssgrid-generator.netlify.com/",[681],"CSS Grid Generator"," is a free tool created by the super talented ",[677,118232,118235],{"href":118233,"rel":118234},"https://twitter.com/sarah_edo",[681],"Sarah Drasner",". It is a visual design tool that allows you to create a basic grid layout and then copy the code that was used to create it. When you first open the tool you will be presented with a default layout but from here you can easily customize the layout to your needs.",[22,118238,118239],{},[653,118240],{"alt":57,"src":118241},"/images/blog/2019/08/08/css-grid-generator-default-layout.png",[636,118243,118245],{"id":118244},"css-grid-layout-example","CSS Grid Layout Example",[22,118247,118248],{},"When I am learning something I find the best way to learn is by building something practical with my shiny new toy. In this article, you are going to take a very simple layout and then use the CSS Grid Generator to create the code needed to use in a real project. This is the finished layout with a little bit of styling just so each section is apparent.",[22,118250,118251],{},[653,118252],{"alt":57,"src":118253},"/images/blog/2019/08/08/simple-layout.png",[22,118255,118256],{},"Now that you know what we are aiming for we can start building it out the layout. Start by updating the right hand side to the following.",[915,118258,118259,118262,118265,118268],{},[37,118260,118261],{},"Columns: 4",[37,118263,118264],{},"Rows: 3",[37,118266,118267],{},"Column Gap: 20",[37,118269,118270],{},"Row Gap: 20",[22,118272,118273],{},"The gaps allow us to have some margin between our content. I could have just used column gap but I wanted some margin after the header and before the footer so that is what the row gap is doing.",[22,118275,118276],{},[653,118277],{"alt":57,"src":118278},"/images/blog/2019/08/08/css-grid-generator-rows-columns.png",[22,118280,118281],{},"Next you will want to define the different areas of your application. In the CSS Grid Generator you can click and drag to create an area. You will want the header to span the entire grid, the sidebars to take up one cell, the main content area to span 2 columns and the footer to span 4 columns. If you do everything correctly you should end up with something that looks like this.",[22,118283,118284],{},[653,118285],{"alt":57,"src":118286},"/images/blog/2019/08/08/css-grid-generator-areas.png",[22,118288,118289],{},"This is starting to look more like our layout but you still need to define some sizes of different things. You will notice an input box next to each row and column that you can use to set a specific size.",[915,118291,118292,118295,118298],{},[37,118293,118294],{},"Header: 100px height",[37,118296,118297],{},"Sidebars: 200px width",[37,118299,118300],{},"Footer: 50px height",[22,118302,118303],{},[653,118304],{"alt":57,"src":118305},"/images/blog/2019/08/08/css-grid-generator-sizes.png",[22,118307,118308,118309,118312],{},"This is starting to look more like the layout we were going for but you might be asking what that ",[59,118310,118311],{},"1fr"," unit is.",[29685,118314,118315],{},[22,118316,118317],{},"Tracks can be defined using any length unit. Grid also introduces an additional length unit to help us create flexible grid tracks. The new fr unit represents a fraction of the available space in the grid container.",[22,118319,3521,118320,118322,118323,118326],{},[59,118321,118311],{}," in 2nd row will tell that area to take up the rest of the available space. If you set the container to ",[59,118324,118325],{},"100vh"," you can then have content that takes up the full page and same goes for the columns.",[636,118328,118330],{"id":118329},"css-grid-generated-code","CSS Grid Generated Code",[22,118332,118333],{},"If you look under the columns and row inputs on the right hand side you will see a button that says \"Please may I have some code\", click it. You should see some generated code that looks something like this. Click the copy button to copy the code and then head over to a text editor.",[52,118335,118339],{"className":118336,"code":118337,"language":118338,"meta":57,"style":57},"language-scss shiki shiki-themes github-light github-dark github-light",".parent {\n display: grid;\n grid-template-columns: 200px 1fr 1fr 200px;\n grid-template-rows: 100px 1fr 50px;\n grid-column-gap: 20px;\n grid-row-gap: 20px;\n .div1 {\n grid-area: 1 / 1 / 2 / 5;\n }\n .div2 {\n grid-area: 2 / 1 / 3 / 2;\n }\n .div3 {\n grid-area: 2 / 2 / 3 / 4;\n }\n .div4 {\n grid-area: 2 / 4 / 3 / 5;\n }\n .div5 {\n grid-area: 3 / 1 / 4 / 5;\n }\n}\n","scss",[59,118340,118341,118348,118359,118386,118408,118421,118434,118441,118465,118469,118476,118498,118502,118509,118531,118535,118542,118564,118568,118575,118597,118601],{"__ignoreMap":57},[62,118342,118343,118346],{"class":64,"line":65},[62,118344,118345],{"class":122},".parent",[62,118347,126],{"class":72},[62,118349,118350,118352,118354,118357],{"class":64,"line":76},[62,118351,113610],{"class":149},[62,118353,3696],{"class":72},[62,118355,118356],{"class":149},"grid",[62,118358,153],{"class":72},[62,118360,118361,118364,118366,118369,118371,118373,118376,118378,118380,118382,118384],{"class":64,"line":82},[62,118362,118363],{"class":149}," grid-template-columns",[62,118365,3696],{"class":72},[62,118367,118368],{"class":149},"200",[62,118370,30191],{"class":68},[62,118372,22038],{"class":149},[62,118374,118375],{"class":68},"fr",[62,118377,22038],{"class":149},[62,118379,118375],{"class":68},[62,118381,6821],{"class":149},[62,118383,30191],{"class":68},[62,118385,153],{"class":72},[62,118387,118388,118391,118393,118395,118397,118399,118401,118404,118406],{"class":64,"line":89},[62,118389,118390],{"class":149}," grid-template-rows",[62,118392,3696],{"class":72},[62,118394,102311],{"class":149},[62,118396,30191],{"class":68},[62,118398,22038],{"class":149},[62,118400,118375],{"class":68},[62,118402,118403],{"class":149}," 50",[62,118405,30191],{"class":68},[62,118407,153],{"class":72},[62,118409,118410,118413,118415,118417,118419],{"class":64,"line":95},[62,118411,118412],{"class":149}," grid-column-gap",[62,118414,3696],{"class":72},[62,118416,30188],{"class":149},[62,118418,30191],{"class":68},[62,118420,153],{"class":72},[62,118422,118423,118426,118428,118430,118432],{"class":64,"line":101},[62,118424,118425],{"class":149}," grid-row-gap",[62,118427,3696],{"class":72},[62,118429,30188],{"class":149},[62,118431,30191],{"class":68},[62,118433,153],{"class":72},[62,118435,118436,118439],{"class":64,"line":107},[62,118437,118438],{"class":122}," .div1",[62,118440,126],{"class":72},[62,118442,118443,118446,118448,118450,118453,118455,118457,118459,118461,118463],{"class":64,"line":113},[62,118444,118445],{"class":149}," grid-area",[62,118447,3696],{"class":72},[62,118449,6689],{"class":149},[62,118451,118452],{"class":68}," /",[62,118454,22038],{"class":149},[62,118456,118452],{"class":68},[62,118458,26797],{"class":149},[62,118460,118452],{"class":68},[62,118462,103300],{"class":149},[62,118464,153],{"class":72},[62,118466,118467],{"class":64,"line":129},[62,118468,3731],{"class":72},[62,118470,118471,118474],{"class":64,"line":134},[62,118472,118473],{"class":122}," .div2",[62,118475,126],{"class":72},[62,118477,118478,118480,118482,118484,118486,118488,118490,118492,118494,118496],{"class":64,"line":156},[62,118479,118445],{"class":149},[62,118481,3696],{"class":72},[62,118483,5219],{"class":149},[62,118485,118452],{"class":68},[62,118487,22038],{"class":149},[62,118489,118452],{"class":68},[62,118491,21946],{"class":149},[62,118493,118452],{"class":68},[62,118495,26797],{"class":149},[62,118497,153],{"class":72},[62,118499,118500],{"class":64,"line":161},[62,118501,3731],{"class":72},[62,118503,118504,118507],{"class":64,"line":167},[62,118505,118506],{"class":122}," .div3",[62,118508,126],{"class":72},[62,118510,118511,118513,118515,118517,118519,118521,118523,118525,118527,118529],{"class":64,"line":173},[62,118512,118445],{"class":149},[62,118514,3696],{"class":72},[62,118516,5219],{"class":149},[62,118518,118452],{"class":68},[62,118520,26797],{"class":149},[62,118522,118452],{"class":68},[62,118524,21946],{"class":149},[62,118526,118452],{"class":68},[62,118528,103235],{"class":149},[62,118530,153],{"class":72},[62,118532,118533],{"class":64,"line":179},[62,118534,3731],{"class":72},[62,118536,118537,118540],{"class":64,"line":185},[62,118538,118539],{"class":122}," .div4",[62,118541,126],{"class":72},[62,118543,118544,118546,118548,118550,118552,118554,118556,118558,118560,118562],{"class":64,"line":191},[62,118545,118445],{"class":149},[62,118547,3696],{"class":72},[62,118549,5219],{"class":149},[62,118551,118452],{"class":68},[62,118553,103235],{"class":149},[62,118555,118452],{"class":68},[62,118557,21946],{"class":149},[62,118559,118452],{"class":68},[62,118561,103300],{"class":149},[62,118563,153],{"class":72},[62,118565,118566],{"class":64,"line":209},[62,118567,3731],{"class":72},[62,118569,118570,118573],{"class":64,"line":220},[62,118571,118572],{"class":122}," .div5",[62,118574,126],{"class":72},[62,118576,118577,118579,118581,118583,118585,118587,118589,118591,118593,118595],{"class":64,"line":226},[62,118578,118445],{"class":149},[62,118580,3696],{"class":72},[62,118582,4472],{"class":149},[62,118584,118452],{"class":68},[62,118586,22038],{"class":149},[62,118588,118452],{"class":68},[62,118590,103235],{"class":149},[62,118592,118452],{"class":68},[62,118594,103300],{"class":149},[62,118596,153],{"class":72},[62,118598,118599],{"class":64,"line":231},[62,118600,3731],{"class":72},[62,118602,118603],{"class":64,"line":236},[62,118604,379],{"class":72},[22,118606,118607,118608,118611],{},"Create a new document ",[59,118609,118610],{},"simple-layout.htm"," and add the following code which will reset the margin and padding for the body.",[52,118613,118615],{"className":15773,"code":118614,"language":15775,"meta":57,"style":57},"\u003C!DOCTYPE html>\n\u003Chtml lang=\"en\">\n \u003Chead>\n \u003Cmeta charset=\"UTF-8\" />\n \u003Cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n \u003Cmeta http-equiv=\"X-UA-Compatible\" content=\"ie=edge\" />\n \u003Ctitle>Simple Layout\u003C/title>\n \u003Cstyle>\n body {\n margin: 0;\n padding: 0;\n }\n \u003C/style>\n \u003C/head>\n \u003Cbody>\u003C/body>\n\u003C/html>\n",[59,118616,118617,118627,118641,118649,118663,118683,118704,118717,118725,118731,118741,118752,118756,118764,118772,118784],{"__ignoreMap":57},[62,118618,118619,118621,118623,118625],{"class":64,"line":65},[62,118620,15801],{"class":72},[62,118622,15804],{"class":1780},[62,118624,15807],{"class":122},[62,118626,1784],{"class":72},[62,118628,118629,118631,118633,118635,118637,118639],{"class":64,"line":76},[62,118630,760],{"class":72},[62,118632,15775],{"class":1780},[62,118634,20679],{"class":122},[62,118636,146],{"class":72},[62,118638,20684],{"class":1675},[62,118640,1784],{"class":72},[62,118642,118643,118645,118647],{"class":64,"line":82},[62,118644,33056],{"class":72},[62,118646,15824],{"class":1780},[62,118648,1784],{"class":72},[62,118650,118651,118653,118655,118657,118659,118661],{"class":64,"line":89},[62,118652,1789],{"class":72},[62,118654,20701],{"class":1780},[62,118656,20704],{"class":122},[62,118658,146],{"class":72},[62,118660,20709],{"class":1675},[62,118662,67133],{"class":72},[62,118664,118665,118667,118669,118671,118673,118675,118677,118679,118681],{"class":64,"line":95},[62,118666,1789],{"class":72},[62,118668,20701],{"class":1780},[62,118670,16107],{"class":122},[62,118672,146],{"class":72},[62,118674,20724],{"class":1675},[62,118676,20727],{"class":122},[62,118678,146],{"class":72},[62,118680,20732],{"class":1675},[62,118682,67133],{"class":72},[62,118684,118685,118687,118689,118691,118693,118695,118697,118699,118702],{"class":64,"line":101},[62,118686,1789],{"class":72},[62,118688,20701],{"class":1780},[62,118690,60649],{"class":122},[62,118692,146],{"class":72},[62,118694,60654],{"class":1675},[62,118696,20727],{"class":122},[62,118698,146],{"class":72},[62,118700,118701],{"class":1675},"\"ie=edge\"",[62,118703,67133],{"class":72},[62,118705,118706,118708,118710,118713,118715],{"class":64,"line":107},[62,118707,1789],{"class":72},[62,118709,3196],{"class":1780},[62,118711,118712],{"class":72},">Simple Layout\u003C/",[62,118714,3196],{"class":1780},[62,118716,1784],{"class":72},[62,118718,118719,118721,118723],{"class":64,"line":113},[62,118720,1789],{"class":72},[62,118722,1527],{"class":1780},[62,118724,1784],{"class":72},[62,118726,118727,118729],{"class":64,"line":129},[62,118728,66937],{"class":1780},[62,118730,126],{"class":72},[62,118732,118733,118735,118737,118739],{"class":64,"line":134},[62,118734,66956],{"class":149},[62,118736,3696],{"class":72},[62,118738,1130],{"class":149},[62,118740,153],{"class":72},[62,118742,118743,118746,118748,118750],{"class":64,"line":156},[62,118744,118745],{"class":149}," padding",[62,118747,3696],{"class":72},[62,118749,1130],{"class":149},[62,118751,153],{"class":72},[62,118753,118754],{"class":64,"line":161},[62,118755,29042],{"class":72},[62,118757,118758,118760,118762],{"class":64,"line":167},[62,118759,1982],{"class":72},[62,118761,1527],{"class":1780},[62,118763,1784],{"class":72},[62,118765,118766,118768,118770],{"class":64,"line":173},[62,118767,33187],{"class":72},[62,118769,15824],{"class":1780},[62,118771,1784],{"class":72},[62,118773,118774,118776,118778,118780,118782],{"class":64,"line":179},[62,118775,33056],{"class":72},[62,118777,11414],{"class":1780},[62,118779,15857],{"class":72},[62,118781,11414],{"class":1780},[62,118783,1784],{"class":72},[62,118785,118786,118788,118790],{"class":64,"line":185},[62,118787,1818],{"class":72},[62,118789,15775],{"class":1780},[62,118791,1784],{"class":72},[22,118793,118794],{},"Next add in the CSS (the copied code was in SASS but you can pull the divs out)",[52,118796,118798],{"className":15773,"code":118797,"language":15775,"meta":57,"style":57},"\u003C!DOCTYPE html>\n\u003Chtml lang=\"en\">\n \u003Chead>\n \u003Cmeta charset=\"UTF-8\" />\n \u003Cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n \u003Cmeta http-equiv=\"X-UA-Compatible\" content=\"ie=edge\" />\n \u003Ctitle>Simple Layout\u003C/title>\n \u003Cstyle>\n body {\n margin: 0;\n padding: 0;\n }\n .parent {\n display: grid;\n grid-template-columns: 200px 1fr 1fr 200px;\n grid-template-rows: 100px 1fr 50px;\n grid-column-gap: 20px;\n grid-row-gap: 20px;\n height: 100vh;\n }\n\n .div1 {\n grid-area: 1 / 1 / 2 / 5;\n }\n\n .div2 {\n grid-area: 2 / 1 / 3 / 2;\n }\n\n .div3 {\n grid-area: 2 / 2 / 3 / 4;\n }\n\n .div4 {\n grid-area: 2 / 4 / 3 / 5;\n }\n\n .div5 {\n grid-area: 3 / 1 / 4 / 5;\n }\n \u003C/style>\n \u003C/head>\n \u003Cbody>\u003C/body>\n\u003C/html>\n",[59,118799,118800,118810,118824,118832,118846,118866,118886,118898,118906,118912,118922,118932,118936,118943,118954,118979,119000,119013,119026,119040,119044,119048,119055,119079,119083,119087,119094,119116,119120,119124,119131,119153,119157,119161,119168,119190,119194,119198,119205,119227,119231,119239,119247,119259],{"__ignoreMap":57},[62,118801,118802,118804,118806,118808],{"class":64,"line":65},[62,118803,15801],{"class":72},[62,118805,15804],{"class":1780},[62,118807,15807],{"class":122},[62,118809,1784],{"class":72},[62,118811,118812,118814,118816,118818,118820,118822],{"class":64,"line":76},[62,118813,760],{"class":72},[62,118815,15775],{"class":1780},[62,118817,20679],{"class":122},[62,118819,146],{"class":72},[62,118821,20684],{"class":1675},[62,118823,1784],{"class":72},[62,118825,118826,118828,118830],{"class":64,"line":82},[62,118827,33056],{"class":72},[62,118829,15824],{"class":1780},[62,118831,1784],{"class":72},[62,118833,118834,118836,118838,118840,118842,118844],{"class":64,"line":89},[62,118835,1789],{"class":72},[62,118837,20701],{"class":1780},[62,118839,20704],{"class":122},[62,118841,146],{"class":72},[62,118843,20709],{"class":1675},[62,118845,67133],{"class":72},[62,118847,118848,118850,118852,118854,118856,118858,118860,118862,118864],{"class":64,"line":95},[62,118849,1789],{"class":72},[62,118851,20701],{"class":1780},[62,118853,16107],{"class":122},[62,118855,146],{"class":72},[62,118857,20724],{"class":1675},[62,118859,20727],{"class":122},[62,118861,146],{"class":72},[62,118863,20732],{"class":1675},[62,118865,67133],{"class":72},[62,118867,118868,118870,118872,118874,118876,118878,118880,118882,118884],{"class":64,"line":101},[62,118869,1789],{"class":72},[62,118871,20701],{"class":1780},[62,118873,60649],{"class":122},[62,118875,146],{"class":72},[62,118877,60654],{"class":1675},[62,118879,20727],{"class":122},[62,118881,146],{"class":72},[62,118883,118701],{"class":1675},[62,118885,67133],{"class":72},[62,118887,118888,118890,118892,118894,118896],{"class":64,"line":107},[62,118889,1789],{"class":72},[62,118891,3196],{"class":1780},[62,118893,118712],{"class":72},[62,118895,3196],{"class":1780},[62,118897,1784],{"class":72},[62,118899,118900,118902,118904],{"class":64,"line":113},[62,118901,1789],{"class":72},[62,118903,1527],{"class":1780},[62,118905,1784],{"class":72},[62,118907,118908,118910],{"class":64,"line":129},[62,118909,66937],{"class":1780},[62,118911,126],{"class":72},[62,118913,118914,118916,118918,118920],{"class":64,"line":134},[62,118915,66956],{"class":149},[62,118917,3696],{"class":72},[62,118919,1130],{"class":149},[62,118921,153],{"class":72},[62,118923,118924,118926,118928,118930],{"class":64,"line":156},[62,118925,118745],{"class":149},[62,118927,3696],{"class":72},[62,118929,1130],{"class":149},[62,118931,153],{"class":72},[62,118933,118934],{"class":64,"line":161},[62,118935,29042],{"class":72},[62,118937,118938,118941],{"class":64,"line":167},[62,118939,118940],{"class":122}," .parent",[62,118942,126],{"class":72},[62,118944,118945,118948,118950,118952],{"class":64,"line":173},[62,118946,118947],{"class":149}," display",[62,118949,3696],{"class":72},[62,118951,118356],{"class":149},[62,118953,153],{"class":72},[62,118955,118956,118959,118961,118963,118965,118967,118969,118971,118973,118975,118977],{"class":64,"line":179},[62,118957,118958],{"class":149}," grid-template-columns",[62,118960,3696],{"class":72},[62,118962,118368],{"class":149},[62,118964,30191],{"class":68},[62,118966,22038],{"class":149},[62,118968,118375],{"class":68},[62,118970,22038],{"class":149},[62,118972,118375],{"class":68},[62,118974,6821],{"class":149},[62,118976,30191],{"class":68},[62,118978,153],{"class":72},[62,118980,118981,118984,118986,118988,118990,118992,118994,118996,118998],{"class":64,"line":185},[62,118982,118983],{"class":149}," grid-template-rows",[62,118985,3696],{"class":72},[62,118987,102311],{"class":149},[62,118989,30191],{"class":68},[62,118991,22038],{"class":149},[62,118993,118375],{"class":68},[62,118995,118403],{"class":149},[62,118997,30191],{"class":68},[62,118999,153],{"class":72},[62,119001,119002,119005,119007,119009,119011],{"class":64,"line":191},[62,119003,119004],{"class":149}," grid-column-gap",[62,119006,3696],{"class":72},[62,119008,30188],{"class":149},[62,119010,30191],{"class":68},[62,119012,153],{"class":72},[62,119014,119015,119018,119020,119022,119024],{"class":64,"line":209},[62,119016,119017],{"class":149}," grid-row-gap",[62,119019,3696],{"class":72},[62,119021,30188],{"class":149},[62,119023,30191],{"class":68},[62,119025,153],{"class":72},[62,119027,119028,119031,119033,119035,119038],{"class":64,"line":220},[62,119029,119030],{"class":149}," height",[62,119032,3696],{"class":72},[62,119034,102311],{"class":149},[62,119036,119037],{"class":68},"vh",[62,119039,153],{"class":72},[62,119041,119042],{"class":64,"line":226},[62,119043,29042],{"class":72},[62,119045,119046],{"class":64,"line":231},[62,119047,79],{"emptyLinePlaceholder":13},[62,119049,119050,119053],{"class":64,"line":236},[62,119051,119052],{"class":122}," .div1",[62,119054,126],{"class":72},[62,119056,119057,119060,119062,119064,119067,119069,119071,119073,119075,119077],{"class":64,"line":242},[62,119058,119059],{"class":149}," grid-area",[62,119061,3696],{"class":72},[62,119063,6689],{"class":149},[62,119065,119066],{"class":72}," / ",[62,119068,6689],{"class":149},[62,119070,119066],{"class":72},[62,119072,5219],{"class":149},[62,119074,119066],{"class":72},[62,119076,5198],{"class":149},[62,119078,153],{"class":72},[62,119080,119081],{"class":64,"line":247},[62,119082,29042],{"class":72},[62,119084,119085],{"class":64,"line":252},[62,119086,79],{"emptyLinePlaceholder":13},[62,119088,119089,119092],{"class":64,"line":257},[62,119090,119091],{"class":122}," .div2",[62,119093,126],{"class":72},[62,119095,119096,119098,119100,119102,119104,119106,119108,119110,119112,119114],{"class":64,"line":271},[62,119097,119059],{"class":149},[62,119099,3696],{"class":72},[62,119101,5219],{"class":149},[62,119103,119066],{"class":72},[62,119105,6689],{"class":149},[62,119107,119066],{"class":72},[62,119109,4472],{"class":149},[62,119111,119066],{"class":72},[62,119113,5219],{"class":149},[62,119115,153],{"class":72},[62,119117,119118],{"class":64,"line":281},[62,119119,29042],{"class":72},[62,119121,119122],{"class":64,"line":286},[62,119123,79],{"emptyLinePlaceholder":13},[62,119125,119126,119129],{"class":64,"line":291},[62,119127,119128],{"class":122}," .div3",[62,119130,126],{"class":72},[62,119132,119133,119135,119137,119139,119141,119143,119145,119147,119149,119151],{"class":64,"line":296},[62,119134,119059],{"class":149},[62,119136,3696],{"class":72},[62,119138,5219],{"class":149},[62,119140,119066],{"class":72},[62,119142,5219],{"class":149},[62,119144,119066],{"class":72},[62,119146,4472],{"class":149},[62,119148,119066],{"class":72},[62,119150,26724],{"class":149},[62,119152,153],{"class":72},[62,119154,119155],{"class":64,"line":302},[62,119156,29042],{"class":72},[62,119158,119159],{"class":64,"line":308},[62,119160,79],{"emptyLinePlaceholder":13},[62,119162,119163,119166],{"class":64,"line":314},[62,119164,119165],{"class":122}," .div4",[62,119167,126],{"class":72},[62,119169,119170,119172,119174,119176,119178,119180,119182,119184,119186,119188],{"class":64,"line":320},[62,119171,119059],{"class":149},[62,119173,3696],{"class":72},[62,119175,5219],{"class":149},[62,119177,119066],{"class":72},[62,119179,26724],{"class":149},[62,119181,119066],{"class":72},[62,119183,4472],{"class":149},[62,119185,119066],{"class":72},[62,119187,5198],{"class":149},[62,119189,153],{"class":72},[62,119191,119192],{"class":64,"line":326},[62,119193,29042],{"class":72},[62,119195,119196],{"class":64,"line":338},[62,119197,79],{"emptyLinePlaceholder":13},[62,119199,119200,119203],{"class":64,"line":343},[62,119201,119202],{"class":122}," .div5",[62,119204,126],{"class":72},[62,119206,119207,119209,119211,119213,119215,119217,119219,119221,119223,119225],{"class":64,"line":357},[62,119208,119059],{"class":149},[62,119210,3696],{"class":72},[62,119212,4472],{"class":149},[62,119214,119066],{"class":72},[62,119216,6689],{"class":149},[62,119218,119066],{"class":72},[62,119220,26724],{"class":149},[62,119222,119066],{"class":72},[62,119224,5198],{"class":149},[62,119226,153],{"class":72},[62,119228,119229],{"class":64,"line":366},[62,119230,29042],{"class":72},[62,119232,119233,119235,119237],{"class":64,"line":371},[62,119234,1982],{"class":72},[62,119236,1527],{"class":1780},[62,119238,1784],{"class":72},[62,119240,119241,119243,119245],{"class":64,"line":376},[62,119242,33187],{"class":72},[62,119244,15824],{"class":1780},[62,119246,1784],{"class":72},[62,119248,119249,119251,119253,119255,119257],{"class":64,"line":16333},[62,119250,33056],{"class":72},[62,119252,11414],{"class":1780},[62,119254,15857],{"class":72},[62,119256,11414],{"class":1780},[62,119258,1784],{"class":72},[62,119260,119261,119263,119265],{"class":64,"line":16349},[62,119262,1818],{"class":72},[62,119264,15775],{"class":1780},[62,119266,1784],{"class":72},[22,119268,119269,119270,119272,119273,119276],{},"What the tool gave you was the css needed to create this layout. You will need to add markup needed which by looking at the CSS is just a ",[59,119271,118345],{}," with 5 ",[59,119274,119275],{},".div"," nested. I am adding the text just so you can see each section.",[52,119278,119280],{"className":15773,"code":119279,"language":15775,"meta":57,"style":57},"\u003Cbody>\n \u003Cdiv class=\"parent\">\n \u003Cdiv class=\"div1\">\n Header\n \u003C/div>\n \u003Cdiv class=\"div2\">\n Left Sidebar\n \u003C/div>\n \u003Cdiv class=\"div3\">\n Main Content\n \u003C/div>\n \u003Cdiv class=\"div4\">\n Right Sidebar\n \u003C/div>\n \u003Cdiv class=\"div5\">\n Footer\n \u003C/div>\n \u003C/div>\n\u003C/body>\n",[59,119281,119282,119290,119305,119320,119325,119333,119348,119353,119361,119376,119381,119389,119404,119409,119417,119432,119437,119445,119453],{"__ignoreMap":57},[62,119283,119284,119286,119288],{"class":64,"line":65},[62,119285,760],{"class":72},[62,119287,11414],{"class":1780},[62,119289,1784],{"class":72},[62,119291,119292,119294,119296,119298,119300,119303],{"class":64,"line":76},[62,119293,33056],{"class":72},[62,119295,15944],{"class":1780},[62,119297,119],{"class":122},[62,119299,146],{"class":72},[62,119301,119302],{"class":1675},"\"parent\"",[62,119304,1784],{"class":72},[62,119306,119307,119309,119311,119313,119315,119318],{"class":64,"line":82},[62,119308,1789],{"class":72},[62,119310,15944],{"class":1780},[62,119312,119],{"class":122},[62,119314,146],{"class":72},[62,119316,119317],{"class":1675},"\"div1\"",[62,119319,1784],{"class":72},[62,119321,119322],{"class":64,"line":89},[62,119323,119324],{"class":72}," Header\n",[62,119326,119327,119329,119331],{"class":64,"line":95},[62,119328,1982],{"class":72},[62,119330,15944],{"class":1780},[62,119332,1784],{"class":72},[62,119334,119335,119337,119339,119341,119343,119346],{"class":64,"line":101},[62,119336,1789],{"class":72},[62,119338,15944],{"class":1780},[62,119340,119],{"class":122},[62,119342,146],{"class":72},[62,119344,119345],{"class":1675},"\"div2\"",[62,119347,1784],{"class":72},[62,119349,119350],{"class":64,"line":107},[62,119351,119352],{"class":72}," Left Sidebar\n",[62,119354,119355,119357,119359],{"class":64,"line":113},[62,119356,1982],{"class":72},[62,119358,15944],{"class":1780},[62,119360,1784],{"class":72},[62,119362,119363,119365,119367,119369,119371,119374],{"class":64,"line":129},[62,119364,1789],{"class":72},[62,119366,15944],{"class":1780},[62,119368,119],{"class":122},[62,119370,146],{"class":72},[62,119372,119373],{"class":1675},"\"div3\"",[62,119375,1784],{"class":72},[62,119377,119378],{"class":64,"line":134},[62,119379,119380],{"class":72}," Main Content\n",[62,119382,119383,119385,119387],{"class":64,"line":156},[62,119384,1982],{"class":72},[62,119386,15944],{"class":1780},[62,119388,1784],{"class":72},[62,119390,119391,119393,119395,119397,119399,119402],{"class":64,"line":161},[62,119392,1789],{"class":72},[62,119394,15944],{"class":1780},[62,119396,119],{"class":122},[62,119398,146],{"class":72},[62,119400,119401],{"class":1675},"\"div4\"",[62,119403,1784],{"class":72},[62,119405,119406],{"class":64,"line":167},[62,119407,119408],{"class":72}," Right Sidebar\n",[62,119410,119411,119413,119415],{"class":64,"line":173},[62,119412,1982],{"class":72},[62,119414,15944],{"class":1780},[62,119416,1784],{"class":72},[62,119418,119419,119421,119423,119425,119427,119430],{"class":64,"line":179},[62,119420,1789],{"class":72},[62,119422,15944],{"class":1780},[62,119424,119],{"class":122},[62,119426,146],{"class":72},[62,119428,119429],{"class":1675},"\"div5\"",[62,119431,1784],{"class":72},[62,119433,119434],{"class":64,"line":185},[62,119435,119436],{"class":72}," Footer\n",[62,119438,119439,119441,119443],{"class":64,"line":191},[62,119440,1982],{"class":72},[62,119442,15944],{"class":1780},[62,119444,1784],{"class":72},[62,119446,119447,119449,119451],{"class":64,"line":209},[62,119448,33187],{"class":72},[62,119450,15944],{"class":1780},[62,119452,1784],{"class":72},[62,119454,119455,119457,119459],{"class":64,"line":220},[62,119456,1818],{"class":72},[62,119458,11414],{"class":1780},[62,119460,1784],{"class":72},[22,119462,119463,119464,119467,119468],{},"Finally add the follow CSS that will just add some padding a background color for ",[59,119465,119466],{},".div1"," - ",[59,119469,119470],{},".div5",[52,119472,119474],{"className":32875,"code":119473,"language":32877,"meta":57,"style":57},"div:not(.parent) {\n padding: 10px;\n background-color: rgb(199, 199, 199);\n}\n",[59,119475,119476,119489,119501,119525],{"__ignoreMap":57},[62,119477,119478,119480,119483,119485,119487],{"class":64,"line":65},[62,119479,15944],{"class":1780},[62,119481,119482],{"class":122},":not",[62,119484,2109],{"class":72},[62,119486,118345],{"class":122},[62,119488,768],{"class":72},[62,119490,119491,119493,119495,119497,119499],{"class":64,"line":76},[62,119492,113728],{"class":149},[62,119494,3696],{"class":72},[62,119496,26752],{"class":149},[62,119498,30191],{"class":68},[62,119500,153],{"class":72},[62,119502,119503,119505,119507,119510,119512,119515,119517,119519,119521,119523],{"class":64,"line":82},[62,119504,113739],{"class":149},[62,119506,3696],{"class":72},[62,119508,119509],{"class":149},"rgb",[62,119511,2109],{"class":72},[62,119513,119514],{"class":149},"199",[62,119516,976],{"class":72},[62,119518,119514],{"class":149},[62,119520,976],{"class":72},[62,119522,119514],{"class":149},[62,119524,1133],{"class":72},[62,119526,119527],{"class":64,"line":89},[62,119528,379],{"class":72},[22,119530,119531],{},"If you were to run this you would end up with the following.",[22,119533,119534],{},[653,119535],{"alt":57,"src":119536},"/images/blog/2019/08/08/simple-layout-no-height.png",[22,119538,119539,119540,119543,119544,119],{},"This looks pretty good but you want this to take up the entire browser window. An easy way to fix this is to add ",[59,119541,119542],{},"height: 100vh"," to the ",[59,119545,118345],{},[52,119547,119549],{"className":32875,"code":119548,"language":32877,"meta":57,"style":57},".parent {\n display: grid;\n grid-template-columns: 200px 1fr 1fr 200px;\n grid-template-rows: 100px 1fr 50px;\n grid-column-gap: 20px;\n grid-row-gap: 20px;\n height: 100vh;\n}\n",[59,119550,119551,119557,119567,119591,119611,119623,119635,119648],{"__ignoreMap":57},[62,119552,119553,119555],{"class":64,"line":65},[62,119554,118345],{"class":122},[62,119556,126],{"class":72},[62,119558,119559,119561,119563,119565],{"class":64,"line":76},[62,119560,113610],{"class":149},[62,119562,3696],{"class":72},[62,119564,118356],{"class":149},[62,119566,153],{"class":72},[62,119568,119569,119571,119573,119575,119577,119579,119581,119583,119585,119587,119589],{"class":64,"line":82},[62,119570,118363],{"class":149},[62,119572,3696],{"class":72},[62,119574,118368],{"class":149},[62,119576,30191],{"class":68},[62,119578,22038],{"class":149},[62,119580,118375],{"class":68},[62,119582,22038],{"class":149},[62,119584,118375],{"class":68},[62,119586,6821],{"class":149},[62,119588,30191],{"class":68},[62,119590,153],{"class":72},[62,119592,119593,119595,119597,119599,119601,119603,119605,119607,119609],{"class":64,"line":89},[62,119594,118390],{"class":149},[62,119596,3696],{"class":72},[62,119598,102311],{"class":149},[62,119600,30191],{"class":68},[62,119602,22038],{"class":149},[62,119604,118375],{"class":68},[62,119606,118403],{"class":149},[62,119608,30191],{"class":68},[62,119610,153],{"class":72},[62,119612,119613,119615,119617,119619,119621],{"class":64,"line":95},[62,119614,118412],{"class":149},[62,119616,3696],{"class":72},[62,119618,30188],{"class":149},[62,119620,30191],{"class":68},[62,119622,153],{"class":72},[62,119624,119625,119627,119629,119631,119633],{"class":64,"line":101},[62,119626,118425],{"class":149},[62,119628,3696],{"class":72},[62,119630,30188],{"class":149},[62,119632,30191],{"class":68},[62,119634,153],{"class":72},[62,119636,119637,119640,119642,119644,119646],{"class":64,"line":107},[62,119638,119639],{"class":149}," height",[62,119641,3696],{"class":72},[62,119643,102311],{"class":149},[62,119645,119037],{"class":68},[62,119647,153],{"class":72},[62,119649,119650],{"class":64,"line":113},[62,119651,379],{"class":72},[22,119653,119654],{},"With that, you have the layout you were looking for!",[22,119656,119657],{},[653,119658],{"alt":57,"src":118253},[22,119660,119661],{},"This tool is meant to help you create a basic layout in CSS Grid really fast. From here it will be up to you to go out and learn more about CSS Grid so that you can start customizing and creating advanced layouts.",[26,119663,1499],{"id":1498},[22,119665,119666,119667,119670,119671,119676],{},"The user interface of CSS Grid Generator is beautiful, clean and easy to use. I ",[646,119668,119669],{},"love"," that the entire project was written in Vue and hosted on ",[677,119672,119675],{"href":119673,"rel":119674},"https://www.netlify.com/",[681],"Netlify",", 2 of my favorite technologies.",[22,119678,119679,119680,119685],{},"If you're ever curious how a project like this is built you can pop open the source code and take a look (Thanks Sarah!). There is a nice SVG animation in the upper right hand side that will take you to the ",[677,119681,119684],{"href":119682,"rel":119683},"https://github.com/sdras/cssgridgenerator",[681],"Github repository",". I hope you found this tool as useful as I have, as always...",[22,119687,36004,119688,82545],{},[36006,119689],{},[1527,119691,119692],{},"html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .suaqH, html code.shiki .suaqH{--shiki-default:#22863A;--shiki-dark:#85E89D;--shiki-sepia:#22863A}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}",{"title":57,"searchDepth":76,"depth":76,"links":119694},[119695,119699],{"id":118221,"depth":76,"text":118222,"children":119696},[119697,119698],{"id":118244,"depth":82,"text":118245},{"id":118329,"depth":82,"text":118330},{"id":1498,"depth":76,"text":1499},{"_id":119701,"path":119702,"title":119703,"description":119704,"meta":119705,"body":119711},"content/blog/2019/07/23/website-new-features-improvements.md","/blog/2019/07/23/website-new-features-improvements","New Features and Improvements to my website","In this article I am going to walk you through some of the new features I have been working on for my website",{"slug":119706,"date":119707,"published":13,"tags":119708,"author":17,"cover":119710,"excerpt":-1},"website-new-features-improvements","2019-07-23T11:20:30.619Z",[119709,105335,113419],"blog","./website-new-features-cover.png",{"type":19,"value":119712,"toc":121492},[119713,119716,119720,119723,119727,119733,119737,119743,119746,119749,119752,119759,119762,119768,119772,119781,119794,119816,119825,119894,119907,119912,119926,119929,119985,119988,120012,120016,120019,120098,120101,120311,120319,120665,120668,120674,120677,120683,120686,120689,120692,120713,120719,121177,121183,121186,121188,121191,121195,121198,121201,121207,121210,121216,121219,121316,121319,121462,121471,121477,121480,121482,121485,121489],[22,119714,119715],{},"I launched my new personal website earlier this year and I am really happy with how it's been working out. I believe in the \"ship it early and often\" philosophy so my first priority is to get something out there and then improve on it later. Well, it's later now, and I have built a list of features and improvements that I wanted to add to this website. In this article, I will tell you what I have been working on and why I made the few changes that I did.",[26,119717,119719],{"id":119718},"new-look-feel","New Look & Feel",[22,119721,119722],{},"In case you are reading this in a galaxy far far away and this site has completely changed here is what the previous landing page looked like and what it looks like now.",[636,119724,119726],{"id":119725},"before-january-2019","Before (January 2019)",[22,119728,119729],{},[653,119730],{"alt":119731,"src":119732},"Website Look & Feel Before","/images/blog/2019/07/23/before.png",[636,119734,119736],{"id":119735},"after-july-2019","After (July 2019)",[22,119738,119739],{},[653,119740],{"alt":119741,"src":119742},"Website Look & Feel After","/images/blog/2019/07/23/after.png",[22,119744,119745],{},"I know it's not a huge difference but it feels much cleaner to me and I added some features that are going really going to help improve my visitor's experience. Design is one of those things that I have just never been really good at.",[22,119747,119748],{},"I am able to put together very simple and clean layouts but trying to come up with the look is where I have struggled forever. The reason for this is I usually go into it without much of a plan. I would jump right into the code and try to start slapping elements together and hope for the best. After some amount of time with nothing to show for it, I would usually give up and move on.",[636,119750,113460],{"id":119751},"adobe-xd",[22,119753,119754,119755,119758],{},"I have the entire Adobe Creative Suite so I have been looking for a reason to jump in and learn how to use ",[677,119756,113460],{"href":113458,"rel":119757},[681]," and this was the perfect opportunity. If you aren't familiar with Adobe XD it is a design tool that lets you prototype out a design quickly and share it with the world.",[22,119760,119761],{},"What I really appreciate about this tool is just how non-designer friendly it was. I got up to speed in just a couple hours and I was able to to put together some ideas on what I wanted the home page to look like. I also love that I can quickly duplicate artboards and try something new. Now that I could actually see all of these options in front of me I knew what I wanted the new look and feel to look like. With that, I could now open a code editor and get straight to work knowing what I was going to build.",[22,119763,119764],{},[653,119765],{"alt":119766,"src":119767},"Adobe XD Home Page Layouts","/images/blog/2019/07/23/adobe-xd-layouts.png",[26,119769,119771],{"id":119770},"bulma-css","Bulma CSS",[22,119773,119774,119775,119780],{},"When I started this project it was the first time that I had ever used the ",[677,119776,119779],{"href":119777,"rel":119778},"https://bulma.io/",[681],"Bulma CSS Framework",". The best way to learn something new is to build something with it so I decided to give it a shot and I am glad I did. I have really enjoyed working with Bulma but as I started to work on the new layout one thing became apparent, I was using Bulma wrong.",[22,119782,119783,119784,119789,119790,119793],{},"The first thing you need to understand is how to create layouts in Bulma. The ",[677,119785,119788],{"href":119786,"rel":119787},"https://bulma.io/documentation/layout/container/",[681],"container class"," is great for creating a simple container that centers your content horizontally. The ",[59,119791,119792],{},".container"," class can be used in any context, but mostly as a direct child of either:",[915,119795,119796,119801,119806,119811],{},[37,119797,119798],{},[59,119799,119800],{},".navbar",[37,119802,119803],{},[59,119804,119805],{},".hero",[37,119807,119808],{},[59,119809,119810],{},".section",[37,119812,119813],{},[59,119814,119815],{},".footer",[22,119817,119818,119819,119821,119822,119824],{},"This is where I messed up the first time by not using the top level layouts and container correctly and it was causing some issues. When you want to divide your page into sections you can use the ",[59,119820,119810],{}," class along with ",[59,119823,119792],{}," to create a nice looking responsive section.",[52,119826,119828],{"className":15773,"code":119827,"language":15775,"meta":57,"style":57},"\u003Csection class=\"section\">\n \u003Cdiv class=\"container\">\n \u003Ch1 class=\"title\">Section\u003C/h1>\n \u003C/div>\n\u003C/section>\n",[59,119829,119830,119845,119859,119878,119886],{"__ignoreMap":57},[62,119831,119832,119834,119836,119838,119840,119843],{"class":64,"line":65},[62,119833,760],{"class":72},[62,119835,30796],{"class":1780},[62,119837,119],{"class":122},[62,119839,146],{"class":72},[62,119841,119842],{"class":1675},"\"section\"",[62,119844,1784],{"class":72},[62,119846,119847,119849,119851,119853,119855,119857],{"class":64,"line":76},[62,119848,33056],{"class":72},[62,119850,15944],{"class":1780},[62,119852,119],{"class":122},[62,119854,146],{"class":72},[62,119856,30786],{"class":1675},[62,119858,1784],{"class":72},[62,119860,119861,119863,119865,119867,119869,119871,119874,119876],{"class":64,"line":82},[62,119862,1789],{"class":72},[62,119864,4168],{"class":1780},[62,119866,119],{"class":122},[62,119868,146],{"class":72},[62,119870,42161],{"class":1675},[62,119872,119873],{"class":72},">Section\u003C/",[62,119875,4168],{"class":1780},[62,119877,1784],{"class":72},[62,119879,119880,119882,119884],{"class":64,"line":89},[62,119881,33187],{"class":72},[62,119883,15944],{"class":1780},[62,119885,1784],{"class":72},[62,119887,119888,119890,119892],{"class":64,"line":95},[62,119889,1818],{"class":72},[62,119891,30796],{"class":1780},[62,119893,1784],{"class":72},[22,119895,119896,119897,119900,119901,119906],{},"The other problem I was having was with how I was importing Bulma. In my ",[59,119898,119899],{},"/src/main.js"," I was just importing Bulma and this approach wasn't working because I wasn't able to customize my ",[677,119902,119905],{"href":119903,"rel":119904},"https://bulma.io/documentation/customize/variables/",[681],"Bulma variables",". I needed the ability to do this so I could turn off the widescreen and HD breakpoints in the responsive system.",[22,119908,119909,119910],{},"I created a new stylesheet and I import that in ",[59,119911,119899],{},[52,119913,119915],{"className":105863,"code":119914,"language":105865,"meta":57,"style":57},"import '~/assets/style/index.scss';\n",[59,119916,119917],{"__ignoreMap":57},[62,119918,119919,119921,119924],{"class":64,"line":65},[62,119920,27875],{"class":68},[62,119922,119923],{"class":1675}," '~/assets/style/index.scss'",[62,119925,153],{"class":72},[22,119927,119928],{},"In there I import a custom stylesheet called variables before I import Bulma and this allows me to customize everything.",[52,119930,119932],{"className":32875,"code":119931,"language":32877,"meta":57,"style":57},"/** Import the Bulma variables */\n@import \"variables\";\n\n/** Import Bulma */\n@import \"~bulma\";\n\n/** Import overrides */\n@import \"overrides\";\n",[59,119933,119934,119939,119949,119953,119958,119967,119971,119976],{"__ignoreMap":57},[62,119935,119936],{"class":64,"line":65},[62,119937,119938],{"class":85},"/** Import the Bulma variables */\n",[62,119940,119941,119944,119947],{"class":64,"line":76},[62,119942,119943],{"class":68},"@import",[62,119945,119946],{"class":1675}," \"variables\"",[62,119948,153],{"class":72},[62,119950,119951],{"class":64,"line":82},[62,119952,79],{"emptyLinePlaceholder":13},[62,119954,119955],{"class":64,"line":89},[62,119956,119957],{"class":85},"/** Import Bulma */\n",[62,119959,119960,119962,119965],{"class":64,"line":95},[62,119961,119943],{"class":68},[62,119963,119964],{"class":1675}," \"~bulma\"",[62,119966,153],{"class":72},[62,119968,119969],{"class":64,"line":101},[62,119970,79],{"emptyLinePlaceholder":13},[62,119972,119973],{"class":64,"line":107},[62,119974,119975],{"class":85},"/** Import overrides */\n",[62,119977,119978,119980,119983],{"class":64,"line":113},[62,119979,119943],{"class":68},[62,119981,119982],{"class":1675}," \"overrides\"",[62,119984,153],{"class":72},[22,119986,119987],{},"Now I can turn off those break points in Bulma.",[52,119989,119991],{"className":118336,"code":119990,"language":118338,"meta":57,"style":57},"$widescreen-enabled: false;\n\n$fullhd-enabled: false;\n",[59,119992,119993,120001,120005],{"__ignoreMap":57},[62,119994,119995,119998],{"class":64,"line":65},[62,119996,119997],{"class":889},"$widescreen-enabled",[62,119999,120000],{"class":72},": false;\n",[62,120002,120003],{"class":64,"line":76},[62,120004,79],{"emptyLinePlaceholder":13},[62,120006,120007,120010],{"class":64,"line":82},[62,120008,120009],{"class":889},"$fullhd-enabled",[62,120011,120000],{"class":72},[26,120013,120015],{"id":120014},"recent-posts-grid","Recent Posts Grid",[22,120017,120018],{},"The biggest change and challenge was adding a new layout for recent posts. The first thing I needed to do was grab the 9 latest articles and with GraphQL this is pretty easy.",[52,120020,120022],{"className":8822,"code":120021,"language":8824,"meta":57,"style":57},"\u003Cpage-query>\nquery Posts {\n recentPosts: allPost(perPage: 9) {\n edges {\n node {\n id\n title\n cover\n path,\n date(format: \"MMMM DD, YYYY\"),\n timeToRead\n }\n }\n }\n}\n\u003C/page-query>\n",[59,120023,120024,120029,120034,120039,120044,120049,120053,120057,120062,120067,120072,120077,120081,120085,120089,120093],{"__ignoreMap":57},[62,120025,120026],{"class":64,"line":65},[62,120027,120028],{},"\u003Cpage-query>\n",[62,120030,120031],{"class":64,"line":76},[62,120032,120033],{},"query Posts {\n",[62,120035,120036],{"class":64,"line":82},[62,120037,120038],{}," recentPosts: allPost(perPage: 9) {\n",[62,120040,120041],{"class":64,"line":89},[62,120042,120043],{}," edges {\n",[62,120045,120046],{"class":64,"line":95},[62,120047,120048],{}," node {\n",[62,120050,120051],{"class":64,"line":101},[62,120052,9197],{},[62,120054,120055],{"class":64,"line":107},[62,120056,9202],{},[62,120058,120059],{"class":64,"line":113},[62,120060,120061],{}," cover\n",[62,120063,120064],{"class":64,"line":129},[62,120065,120066],{}," path,\n",[62,120068,120069],{"class":64,"line":134},[62,120070,120071],{}," date(format: \"MMMM DD, YYYY\"),\n",[62,120073,120074],{"class":64,"line":156},[62,120075,120076],{}," timeToRead\n",[62,120078,120079],{"class":64,"line":161},[62,120080,29042],{},[62,120082,120083],{"class":64,"line":167},[62,120084,223],{},[62,120086,120087],{"class":64,"line":173},[62,120088,3731],{},[62,120090,120091],{"class":64,"line":179},[62,120092,379],{},[62,120094,120095],{"class":64,"line":185},[62,120096,120097],{},"\u003C/page-query>\n",[22,120099,120100],{},"Now that I have my posts I can loop over them and display them.",[52,120102,120104],{"className":15773,"code":120103,"language":15775,"meta":57,"style":57},"\u003Cdiv class=\"posts\">\n \u003Cdiv v-for=\"post in $page.recentPosts.edges\" :key=\"post.node.id\" class=\"post\">\n \u003Cg-link :to=\"post.node.path\" :aria-label=\"post.node.title\">\n \u003Cdiv class=\"overlay\">\u003C/div>\n \u003Cg-image :src=\"post.node.cover.src\" class=\"post-img\" :alt=\"post.node.title\" />\n \u003Cdiv class=\"post-details fadeIn-bottom\">\n \u003Ch3 class=\"post-title\">{{ post.node.title }}\u003C/h3>\n \u003Cp class=\"post-text\">{{ post.node.date }} • ☕️ {{ post.node.timeToRead }} min read\u003C/p>\n \u003C/div>\n \u003C/g-link>\n \u003C/div>\n\u003C/div>\n",[59,120105,120106,120120,120150,120174,120193,120224,120239,120259,120279,120287,120295,120303],{"__ignoreMap":57},[62,120107,120108,120110,120112,120114,120116,120118],{"class":64,"line":65},[62,120109,760],{"class":72},[62,120111,15944],{"class":1780},[62,120113,119],{"class":122},[62,120115,146],{"class":72},[62,120117,68226],{"class":1675},[62,120119,1784],{"class":72},[62,120121,120122,120124,120126,120129,120131,120134,120137,120139,120142,120144,120146,120148],{"class":64,"line":76},[62,120123,33056],{"class":72},[62,120125,15944],{"class":1780},[62,120127,120128],{"class":122}," v-for",[62,120130,146],{"class":72},[62,120132,120133],{"class":1675},"\"post in $page.recentPosts.edges\"",[62,120135,120136],{"class":122}," :key",[62,120138,146],{"class":72},[62,120140,120141],{"class":1675},"\"post.node.id\"",[62,120143,119],{"class":122},[62,120145,146],{"class":72},[62,120147,22654],{"class":1675},[62,120149,1784],{"class":72},[62,120151,120152,120154,120157,120159,120161,120164,120167,120169,120172],{"class":64,"line":82},[62,120153,1789],{"class":72},[62,120155,120156],{"class":1780},"g-link",[62,120158,85751],{"class":122},[62,120160,146],{"class":72},[62,120162,120163],{"class":1675},"\"post.node.path\"",[62,120165,120166],{"class":122}," :aria-label",[62,120168,146],{"class":72},[62,120170,120171],{"class":1675},"\"post.node.title\"",[62,120173,1784],{"class":72},[62,120175,120176,120178,120180,120182,120184,120187,120189,120191],{"class":64,"line":89},[62,120177,92881],{"class":72},[62,120179,15944],{"class":1780},[62,120181,119],{"class":122},[62,120183,146],{"class":72},[62,120185,120186],{"class":1675},"\"overlay\"",[62,120188,15857],{"class":72},[62,120190,15944],{"class":1780},[62,120192,1784],{"class":72},[62,120194,120195,120197,120200,120203,120205,120208,120210,120212,120215,120218,120220,120222],{"class":64,"line":95},[62,120196,92881],{"class":72},[62,120198,120199],{"class":1780},"g-image",[62,120201,120202],{"class":122}," :src",[62,120204,146],{"class":72},[62,120206,120207],{"class":1675},"\"post.node.cover.src\"",[62,120209,119],{"class":122},[62,120211,146],{"class":72},[62,120213,120214],{"class":1675},"\"post-img\"",[62,120216,120217],{"class":122}," :alt",[62,120219,146],{"class":72},[62,120221,120171],{"class":1675},[62,120223,67133],{"class":72},[62,120225,120226,120228,120230,120232,120234,120237],{"class":64,"line":101},[62,120227,92881],{"class":72},[62,120229,15944],{"class":1780},[62,120231,119],{"class":122},[62,120233,146],{"class":72},[62,120235,120236],{"class":1675},"\"post-details fadeIn-bottom\"",[62,120238,1784],{"class":72},[62,120240,120241,120243,120245,120247,120249,120252,120255,120257],{"class":64,"line":107},[62,120242,1896],{"class":72},[62,120244,636],{"class":1780},[62,120246,119],{"class":122},[62,120248,146],{"class":72},[62,120250,120251],{"class":1675},"\"post-title\"",[62,120253,120254],{"class":72},">{{ post.node.title }}\u003C/",[62,120256,636],{"class":1780},[62,120258,1784],{"class":72},[62,120260,120261,120263,120265,120267,120269,120272,120275,120277],{"class":64,"line":113},[62,120262,1896],{"class":72},[62,120264,22],{"class":1780},[62,120266,119],{"class":122},[62,120268,146],{"class":72},[62,120270,120271],{"class":1675},"\"post-text\"",[62,120273,120274],{"class":72},">{{ post.node.date }} • ☕️ {{ post.node.timeToRead }} min read\u003C/",[62,120276,22],{"class":1780},[62,120278,1784],{"class":72},[62,120280,120281,120283,120285],{"class":64,"line":129},[62,120282,92989],{"class":72},[62,120284,15944],{"class":1780},[62,120286,1784],{"class":72},[62,120288,120289,120291,120293],{"class":64,"line":134},[62,120290,1982],{"class":72},[62,120292,120156],{"class":1780},[62,120294,1784],{"class":72},[62,120296,120297,120299,120301],{"class":64,"line":156},[62,120298,33187],{"class":72},[62,120300,15944],{"class":1780},[62,120302,1784],{"class":72},[62,120304,120305,120307,120309],{"class":64,"line":161},[62,120306,1818],{"class":72},[62,120308,15944],{"class":1780},[62,120310,1784],{"class":72},[22,120312,120313,120314,2755],{},"The toughest part was with the grid being so dynamic how would I be able to get the sizes I wanted from specific grid items. It turns out this was easier than I thought it would be using CSS",[677,120315,120318],{"href":120316,"rel":120317},"https://developer.mozilla.org/en-US/docs/Web/CSS/:nth-child",[681],": nth-child() selector",[52,120320,120322],{"className":32875,"code":120321,"language":32877,"meta":57,"style":57},".posts {\n display: grid;\n grid-template-columns: repeat(3, 1fr);\n grid-gap: 10px;\n margin-bottom: 30px;\n}\n\n.post {\n position: relative;\n overflow: hidden;\n}\n.post:nth-child(1),\n.post:nth-child(6),\n.post:nth-child(7) {\n grid-column: span 2;\n grid-row: span 2;\n}\n.post:nth-child(6) {\n grid-column: 2/4;\n grid-row: 3/5;\n}\n.post:nth-child(4),\n.post:nth-child(5) {\n grid-column: 1;\n}\n.post:nth-child(3),\n.post:nth-child(5),\n.post:nth-child(9) {\n display: flex;\n align-self: flex-end;\n}\n.post-img {\n width: 100%;\n height: 100%;\n object-fit: cover;\n}\n",[59,120323,120324,120331,120341,120362,120375,120389,120393,120397,120403,120413,120423,120427,120438,120448,120458,120470,120481,120485,120495,120509,120523,120527,120537,120547,120557,120561,120571,120581,120591,120601,120613,120617,120624,120637,120649,120661],{"__ignoreMap":57},[62,120325,120326,120329],{"class":64,"line":65},[62,120327,120328],{"class":122},".posts",[62,120330,126],{"class":72},[62,120332,120333,120335,120337,120339],{"class":64,"line":76},[62,120334,113610],{"class":149},[62,120336,3696],{"class":72},[62,120338,118356],{"class":149},[62,120340,153],{"class":72},[62,120342,120343,120345,120347,120350,120352,120354,120356,120358,120360],{"class":64,"line":82},[62,120344,118363],{"class":149},[62,120346,3696],{"class":72},[62,120348,120349],{"class":149},"repeat",[62,120351,2109],{"class":72},[62,120353,4472],{"class":149},[62,120355,976],{"class":72},[62,120357,6689],{"class":149},[62,120359,118375],{"class":68},[62,120361,1133],{"class":72},[62,120363,120364,120367,120369,120371,120373],{"class":64,"line":89},[62,120365,120366],{"class":149}," grid-gap",[62,120368,3696],{"class":72},[62,120370,26752],{"class":149},[62,120372,30191],{"class":68},[62,120374,153],{"class":72},[62,120376,120377,120380,120382,120385,120387],{"class":64,"line":95},[62,120378,120379],{"class":149}," margin-bottom",[62,120381,3696],{"class":72},[62,120383,120384],{"class":149},"30",[62,120386,30191],{"class":68},[62,120388,153],{"class":72},[62,120390,120391],{"class":64,"line":101},[62,120392,379],{"class":72},[62,120394,120395],{"class":64,"line":107},[62,120396,79],{"emptyLinePlaceholder":13},[62,120398,120399,120401],{"class":64,"line":113},[62,120400,70651],{"class":122},[62,120402,126],{"class":72},[62,120404,120405,120407,120409,120411],{"class":64,"line":129},[62,120406,113543],{"class":149},[62,120408,3696],{"class":72},[62,120410,113548],{"class":149},[62,120412,153],{"class":72},[62,120414,120415,120417,120419,120421],{"class":64,"line":134},[62,120416,113583],{"class":149},[62,120418,3696],{"class":72},[62,120420,30177],{"class":149},[62,120422,153],{"class":72},[62,120424,120425],{"class":64,"line":156},[62,120426,379],{"class":72},[62,120428,120429,120432,120434,120436],{"class":64,"line":161},[62,120430,120431],{"class":122},".post:nth-child",[62,120433,2109],{"class":72},[62,120435,6689],{"class":149},[62,120437,3324],{"class":72},[62,120439,120440,120442,120444,120446],{"class":64,"line":167},[62,120441,120431],{"class":122},[62,120443,2109],{"class":72},[62,120445,26733],{"class":149},[62,120447,3324],{"class":72},[62,120449,120450,120452,120454,120456],{"class":64,"line":173},[62,120451,120431],{"class":122},[62,120453,2109],{"class":72},[62,120455,26738],{"class":149},[62,120457,768],{"class":72},[62,120459,120460,120463,120466,120468],{"class":64,"line":179},[62,120461,120462],{"class":149}," grid-column",[62,120464,120465],{"class":72},": span ",[62,120467,5219],{"class":149},[62,120469,153],{"class":72},[62,120471,120472,120475,120477,120479],{"class":64,"line":185},[62,120473,120474],{"class":149}," grid-row",[62,120476,120465],{"class":72},[62,120478,5219],{"class":149},[62,120480,153],{"class":72},[62,120482,120483],{"class":64,"line":191},[62,120484,379],{"class":72},[62,120486,120487,120489,120491,120493],{"class":64,"line":209},[62,120488,120431],{"class":122},[62,120490,2109],{"class":72},[62,120492,26733],{"class":149},[62,120494,768],{"class":72},[62,120496,120497,120499,120501,120503,120505,120507],{"class":64,"line":220},[62,120498,120462],{"class":149},[62,120500,3696],{"class":72},[62,120502,5219],{"class":149},[62,120504,6936],{"class":72},[62,120506,26724],{"class":149},[62,120508,153],{"class":72},[62,120510,120511,120513,120515,120517,120519,120521],{"class":64,"line":226},[62,120512,120474],{"class":149},[62,120514,3696],{"class":72},[62,120516,4472],{"class":149},[62,120518,6936],{"class":72},[62,120520,5198],{"class":149},[62,120522,153],{"class":72},[62,120524,120525],{"class":64,"line":231},[62,120526,379],{"class":72},[62,120528,120529,120531,120533,120535],{"class":64,"line":236},[62,120530,120431],{"class":122},[62,120532,2109],{"class":72},[62,120534,26724],{"class":149},[62,120536,3324],{"class":72},[62,120538,120539,120541,120543,120545],{"class":64,"line":242},[62,120540,120431],{"class":122},[62,120542,2109],{"class":72},[62,120544,5198],{"class":149},[62,120546,768],{"class":72},[62,120548,120549,120551,120553,120555],{"class":64,"line":247},[62,120550,120462],{"class":149},[62,120552,3696],{"class":72},[62,120554,6689],{"class":149},[62,120556,153],{"class":72},[62,120558,120559],{"class":64,"line":252},[62,120560,379],{"class":72},[62,120562,120563,120565,120567,120569],{"class":64,"line":257},[62,120564,120431],{"class":122},[62,120566,2109],{"class":72},[62,120568,4472],{"class":149},[62,120570,3324],{"class":72},[62,120572,120573,120575,120577,120579],{"class":64,"line":271},[62,120574,120431],{"class":122},[62,120576,2109],{"class":72},[62,120578,5198],{"class":149},[62,120580,3324],{"class":72},[62,120582,120583,120585,120587,120589],{"class":64,"line":281},[62,120584,120431],{"class":122},[62,120586,2109],{"class":72},[62,120588,5021],{"class":149},[62,120590,768],{"class":72},[62,120592,120593,120595,120597,120599],{"class":64,"line":286},[62,120594,113610],{"class":149},[62,120596,3696],{"class":72},[62,120598,30374],{"class":149},[62,120600,153],{"class":72},[62,120602,120603,120606,120608,120611],{"class":64,"line":291},[62,120604,120605],{"class":149}," align-self",[62,120607,3696],{"class":72},[62,120609,120610],{"class":149},"flex-end",[62,120612,153],{"class":72},[62,120614,120615],{"class":64,"line":296},[62,120616,379],{"class":72},[62,120618,120619,120622],{"class":64,"line":302},[62,120620,120621],{"class":122},".post-img",[62,120623,126],{"class":72},[62,120625,120626,120629,120631,120633,120635],{"class":64,"line":308},[62,120627,120628],{"class":149}," width",[62,120630,3696],{"class":72},[62,120632,102311],{"class":149},[62,120634,26794],{"class":68},[62,120636,153],{"class":72},[62,120638,120639,120641,120643,120645,120647],{"class":64,"line":314},[62,120640,119639],{"class":149},[62,120642,3696],{"class":72},[62,120644,102311],{"class":149},[62,120646,26794],{"class":68},[62,120648,153],{"class":72},[62,120650,120651,120654,120656,120659],{"class":64,"line":320},[62,120652,120653],{"class":149}," object-fit",[62,120655,3696],{"class":72},[62,120657,120658],{"class":149},"cover",[62,120660,153],{"class":72},[62,120662,120663],{"class":64,"line":326},[62,120664,379],{"class":72},[22,120666,120667],{},"Which gives us this really nice grid layout.",[22,120669,120670],{},[653,120671],{"alt":120672,"src":120673},"CSS Grid Layout","/images/blog/2019/07/23/grid-layout.png",[22,120675,120676],{},"The other thing to think about is what is this going to look like for mobile users. In this case, I decided to use a media query to just stack them when we hit a certain breakpoint.",[22,120678,120679],{},[653,120680],{"alt":120681,"src":120682},"CSS Grid Layout on Mobile","/images/blog/2019/07/23/grid-layout-mobile.png",[26,120684,120685],{"id":10653},"Search",[22,120687,120688],{},"When I first started this blog I did so without migrating posts from my current blog which had close to 1000 articles on it. Because I only had a few I had no real need for search at the time. Now that I have been writing for a while and I have migrated some of my posts over I have a real need for it.",[22,120690,120691],{},"My first pass on this was to use Algolia Search which I have to say is a really great product. I actually had it working pretty well but it did take me awhile and what I found out in the end is that it was overkill for what I needed to do.",[22,120693,120694,120695,120700,120701,120706,120707,120712],{},"I saw a ",[677,120696,120699],{"href":120697,"rel":120698},"https://www.youtube.com/watch?v=6i8D8j5Gkk8",[681],"presentation from Andre Madarang"," on VueJS Search with ",[677,120702,120705],{"href":120703,"rel":120704},"https://github.com/shayneo/vue-fuse",[681],"vue-fuse",". This was the first time I heard of vue-fuse and I was really excited about the idea. Vue Fuse is a wrappper for ",[677,120708,120711],{"href":120709,"rel":120710},"https://fusejs.io/",[681],"Fuse.js"," library which is a lightweight fuzzy search library.",[22,120714,120715,120716],{},"I pretty much followed Andre's tutorial to get the search component setup and then realized that for this to work we need to build a json file of blog posts each time we build as the source for the search. In Gridsome you can do this in the ",[59,120717,120718],{},"gridsome.server.js",[52,120720,120722],{"className":105863,"code":120721,"language":105865,"meta":57,"style":57},"const fs = require('fs');\nconst path = require('path');\nconst pick = require('lodash.pick');\n\nmodule.exports = function (api, options) {\n api.loadSource(store => {\n // Use the Data store API here: https://gridsome.org/docs/data-store-api\n })\n\n api.beforeBuild(({ config, store }) => {\n\n // Generate an index file for Fuse to search Posts\n const { collection } = store.getContentType('Post');\n\n const posts = collection.data.map(post => {\n return pick(post, ['title', 'path', 'excerpt']);\n });\n\n const output = {\n dir: './static',\n name: 'search.json',\n ...options.output\n }\n\n const outputPath = path.resolve(process.cwd(), output.dir)\n const outputPathExists = fs.existsSync(outputPath)\n const fileName = output.name.endsWith('.json')\n ? output.name\n : `${output.name}.json`\n\n if (outputPathExists) {\n fs.writeFileSync(path.resolve(process.cwd(), output.dir, fileName), JSON.stringify(posts))\n } else {\n fs.mkdirSync(outputPath)\n fs.writeFileSync(path.resolve(process.cwd(), output.dir, fileName), JSON.stringify(posts))\n }\n })\n}\n",[59,120723,120724,120743,120760,120778,120782,120805,120822,120827,120832,120836,120859,120863,120868,120894,120898,120920,120942,120946,120950,120960,120970,120980,120987,120991,120995,121018,121035,121057,121065,121083,121087,121094,121123,121132,121141,121165,121169,121173],{"__ignoreMap":57},[62,120725,120726,120728,120731,120733,120736,120738,120741],{"class":64,"line":65},[62,120727,110541],{"class":68},[62,120729,120730],{"class":149}," fs",[62,120732,2556],{"class":68},[62,120734,120735],{"class":122}," require",[62,120737,2109],{"class":72},[62,120739,120740],{"class":1675},"'fs'",[62,120742,1133],{"class":72},[62,120744,120745,120747,120749,120751,120753,120755,120758],{"class":64,"line":76},[62,120746,110541],{"class":68},[62,120748,60383],{"class":149},[62,120750,2556],{"class":68},[62,120752,120735],{"class":122},[62,120754,2109],{"class":72},[62,120756,120757],{"class":1675},"'path'",[62,120759,1133],{"class":72},[62,120761,120762,120764,120767,120769,120771,120773,120776],{"class":64,"line":82},[62,120763,110541],{"class":68},[62,120765,120766],{"class":149}," pick",[62,120768,2556],{"class":68},[62,120770,120735],{"class":122},[62,120772,2109],{"class":72},[62,120774,120775],{"class":1675},"'lodash.pick'",[62,120777,1133],{"class":72},[62,120779,120780],{"class":64,"line":89},[62,120781,79],{"emptyLinePlaceholder":13},[62,120783,120784,120786,120788,120790,120792,120794,120796,120798,120800,120803],{"class":64,"line":95},[62,120785,32813],{"class":149},[62,120787,2755],{"class":72},[62,120789,32818],{"class":149},[62,120791,2556],{"class":68},[62,120793,21929],{"class":68},[62,120795,744],{"class":72},[62,120797,55260],{"class":889},[62,120799,976],{"class":72},[62,120801,120802],{"class":889},"options",[62,120804,768],{"class":72},[62,120806,120807,120810,120813,120815,120818,120820],{"class":64,"line":101},[62,120808,120809],{"class":72}," api.",[62,120811,120812],{"class":122},"loadSource",[62,120814,2109],{"class":72},[62,120816,120817],{"class":889},"store",[62,120819,85402],{"class":68},[62,120821,126],{"class":72},[62,120823,120824],{"class":64,"line":107},[62,120825,120826],{"class":85}," // Use the Data store API here: https://gridsome.org/docs/data-store-api\n",[62,120828,120829],{"class":64,"line":113},[62,120830,120831],{"class":72}," })\n",[62,120833,120834],{"class":64,"line":129},[62,120835,79],{"emptyLinePlaceholder":13},[62,120837,120838,120840,120843,120846,120848,120850,120852,120855,120857],{"class":64,"line":134},[62,120839,120809],{"class":72},[62,120841,120842],{"class":122},"beforeBuild",[62,120844,120845],{"class":72},"(({ ",[62,120847,48865],{"class":889},[62,120849,976],{"class":72},[62,120851,120817],{"class":889},[62,120853,120854],{"class":72}," }) ",[62,120856,21525],{"class":68},[62,120858,126],{"class":72},[62,120860,120861],{"class":64,"line":156},[62,120862,79],{"emptyLinePlaceholder":13},[62,120864,120865],{"class":64,"line":161},[62,120866,120867],{"class":85}," // Generate an index file for Fuse to search Posts\n",[62,120869,120870,120872,120874,120877,120879,120881,120884,120887,120889,120892],{"class":64,"line":167},[62,120871,36857],{"class":68},[62,120873,21785],{"class":72},[62,120875,120876],{"class":149},"collection",[62,120878,21795],{"class":72},[62,120880,146],{"class":68},[62,120882,120883],{"class":72}," store.",[62,120885,120886],{"class":122},"getContentType",[62,120888,2109],{"class":72},[62,120890,120891],{"class":1675},"'Post'",[62,120893,1133],{"class":72},[62,120895,120896],{"class":64,"line":173},[62,120897,79],{"emptyLinePlaceholder":13},[62,120899,120900,120902,120905,120907,120910,120912,120914,120916,120918],{"class":64,"line":179},[62,120901,36857],{"class":68},[62,120903,120904],{"class":149}," posts",[62,120906,2556],{"class":68},[62,120908,120909],{"class":72}," collection.data.",[62,120911,4200],{"class":122},[62,120913,2109],{"class":72},[62,120915,38838],{"class":889},[62,120917,85402],{"class":68},[62,120919,126],{"class":72},[62,120921,120922,120924,120926,120929,120931,120933,120935,120937,120940],{"class":64,"line":185},[62,120923,28884],{"class":68},[62,120925,120766],{"class":122},[62,120927,120928],{"class":72},"(post, [",[62,120930,54429],{"class":1675},[62,120932,976],{"class":72},[62,120934,120757],{"class":1675},[62,120936,976],{"class":72},[62,120938,120939],{"class":1675},"'excerpt'",[62,120941,87836],{"class":72},[62,120943,120944],{"class":64,"line":191},[62,120945,906],{"class":72},[62,120947,120948],{"class":64,"line":209},[62,120949,79],{"emptyLinePlaceholder":13},[62,120951,120952,120954,120956,120958],{"class":64,"line":220},[62,120953,36857],{"class":68},[62,120955,111940],{"class":149},[62,120957,2556],{"class":68},[62,120959,126],{"class":72},[62,120961,120962,120965,120968],{"class":64,"line":226},[62,120963,120964],{"class":72}," dir: ",[62,120966,120967],{"class":1675},"'./static'",[62,120969,3338],{"class":72},[62,120971,120972,120975,120978],{"class":64,"line":231},[62,120973,120974],{"class":72}," name: ",[62,120976,120977],{"class":1675},"'search.json'",[62,120979,3338],{"class":72},[62,120981,120982,120984],{"class":64,"line":236},[62,120983,116745],{"class":68},[62,120985,120986],{"class":72},"options.output\n",[62,120988,120989],{"class":64,"line":242},[62,120990,223],{"class":72},[62,120992,120993],{"class":64,"line":247},[62,120994,79],{"emptyLinePlaceholder":13},[62,120996,120997,120999,121002,121004,121007,121009,121012,121015],{"class":64,"line":252},[62,120998,36857],{"class":68},[62,121000,121001],{"class":149}," outputPath",[62,121003,2556],{"class":68},[62,121005,121006],{"class":72}," path.",[62,121008,85593],{"class":122},[62,121010,121011],{"class":72},"(process.",[62,121013,121014],{"class":122},"cwd",[62,121016,121017],{"class":72},"(), output.dir)\n",[62,121019,121020,121022,121025,121027,121029,121032],{"class":64,"line":257},[62,121021,36857],{"class":68},[62,121023,121024],{"class":149}," outputPathExists",[62,121026,2556],{"class":68},[62,121028,85617],{"class":72},[62,121030,121031],{"class":122},"existsSync",[62,121033,121034],{"class":72},"(outputPath)\n",[62,121036,121037,121039,121042,121044,121047,121050,121052,121055],{"class":64,"line":271},[62,121038,36857],{"class":68},[62,121040,121041],{"class":149}," fileName",[62,121043,2556],{"class":68},[62,121045,121046],{"class":72}," output.name.",[62,121048,121049],{"class":122},"endsWith",[62,121051,2109],{"class":72},[62,121053,121054],{"class":1675},"'.json'",[62,121056,2212],{"class":72},[62,121058,121059,121062],{"class":64,"line":281},[62,121060,121061],{"class":68}," ?",[62,121063,121064],{"class":72}," output.name\n",[62,121066,121067,121070,121073,121076,121078,121080],{"class":64,"line":286},[62,121068,121069],{"class":68}," :",[62,121071,121072],{"class":1675}," `${",[62,121074,121075],{"class":72},"output",[62,121077,2755],{"class":1675},[62,121079,3107],{"class":72},[62,121081,121082],{"class":1675},"}.json`\n",[62,121084,121085],{"class":64,"line":291},[62,121086,79],{"emptyLinePlaceholder":13},[62,121088,121089,121091],{"class":64,"line":296},[62,121090,14187],{"class":68},[62,121092,121093],{"class":72}," (outputPathExists) {\n",[62,121095,121096,121099,121102,121105,121107,121109,121111,121114,121116,121118,121120],{"class":64,"line":302},[62,121097,121098],{"class":72}," fs.",[62,121100,121101],{"class":122},"writeFileSync",[62,121103,121104],{"class":72},"(path.",[62,121106,85593],{"class":122},[62,121108,121011],{"class":72},[62,121110,121014],{"class":122},[62,121112,121113],{"class":72},"(), output.dir, fileName), ",[62,121115,112334],{"class":149},[62,121117,2755],{"class":72},[62,121119,112339],{"class":122},[62,121121,121122],{"class":72},"(posts))\n",[62,121124,121125,121128,121130],{"class":64,"line":308},[62,121126,121127],{"class":72}," } ",[62,121129,12783],{"class":68},[62,121131,126],{"class":72},[62,121133,121134,121136,121139],{"class":64,"line":314},[62,121135,121098],{"class":72},[62,121137,121138],{"class":122},"mkdirSync",[62,121140,121034],{"class":72},[62,121142,121143,121145,121147,121149,121151,121153,121155,121157,121159,121161,121163],{"class":64,"line":320},[62,121144,121098],{"class":72},[62,121146,121101],{"class":122},[62,121148,121104],{"class":72},[62,121150,85593],{"class":122},[62,121152,121011],{"class":72},[62,121154,121014],{"class":122},[62,121156,121113],{"class":72},[62,121158,112334],{"class":149},[62,121160,2755],{"class":72},[62,121162,112339],{"class":122},[62,121164,121122],{"class":72},[62,121166,121167],{"class":64,"line":326},[62,121168,223],{"class":72},[62,121170,121171],{"class":64,"line":338},[62,121172,120831],{"class":72},[62,121174,121175],{"class":64,"line":343},[62,121176,379],{"class":72},[22,121178,121179],{},[653,121180],{"alt":121181,"src":121182},"Search Results","/images/blog/2019/07/23/search-results.png",[22,121184,121185],{},"The styles don't look great on this right now but that is something I can come back and fix up later.",[26,121187,37673],{"id":33895},[22,121189,121190],{},"Another one of the main reasons for the new look and feel is because I wanted to get a call to action front and center when you visited my home page. I am working on plans to actually start writing a newsletter every Sunday so I am trying to build my list up.",[26,121192,121194],{"id":121193},"video-covers","Video Covers",[22,121196,121197],{},"There are a few blog posts that I have screencasts of and in those cases, I want to make the visitor aware of them. Some people (heck probably most) would prefer to watch a video over reading a post so I want to start offering this to visitors.",[22,121199,121200],{},"I went back to Adobe XD and tried to come up with a solution to this problem. My first and only idea was to just put a link below the cover image if there was a video. This functionality would work but didn't look all that great and didn't feature the video the way I wanted it to.",[22,121202,121203],{},[653,121204],{"alt":121205,"src":121206},"Video Cover Mockup","/images/blog/2019/07/23/video-cover-mockup.png",[22,121208,121209],{},"I asked for some advice on Twitter and Ryan Edgar replied with an awesome suggestion.",[22,121211,121212],{},[677,121213,121214],{"href":121214,"rel":121215},"https://twitter.com/ryanedg/status/1147426471633440768",[681],[22,121217,121218],{},"I immediately got to work and to get started I added a new property to the front matter for this blog post.",[52,121220,121222],{"className":10993,"code":121221,"language":10995,"meta":57,"style":57},"---\nslug: 'triggering-events-router-vue'\ntitle: 'Triggering events from Vue Router views'\ndate: '2019-06-05T20:13:17.445Z'\npublished: true\ndescription: 'In this article, I will show you how to trigger events from views using the Router View component.'\nauthor: 'Dan Vega'\ntags: ['vue']\ncover: ./triggering-events-router-vue-cover.png\nvideo: https://www.youtube.com/embed/JwccQYpsE2Q\n---\n",[59,121223,121224,121229,121238,121247,121256,121265,121274,121282,121293,121302,121312],{"__ignoreMap":57},[62,121225,121226],{"class":64,"line":65},[62,121227,121228],{"class":122},"---\n",[62,121230,121231,121233,121235],{"class":64,"line":76},[62,121232,41300],{"class":1780},[62,121234,3696],{"class":72},[62,121236,121237],{"class":1675},"'triggering-events-router-vue'\n",[62,121239,121240,121242,121244],{"class":64,"line":82},[62,121241,3196],{"class":1780},[62,121243,3696],{"class":72},[62,121245,121246],{"class":1675},"'Triggering events from Vue Router views'\n",[62,121248,121249,121251,121253],{"class":64,"line":89},[62,121250,42475],{"class":1780},[62,121252,3696],{"class":72},[62,121254,121255],{"class":1675},"'2019-06-05T20:13:17.445Z'\n",[62,121257,121258,121261,121263],{"class":64,"line":95},[62,121259,121260],{"class":1780},"published",[62,121262,3696],{"class":72},[62,121264,51914],{"class":149},[62,121266,121267,121269,121271],{"class":64,"line":101},[62,121268,3117],{"class":1780},[62,121270,3696],{"class":72},[62,121272,121273],{"class":1675},"'In this article, I will show you how to trigger events from views using the Router View component.'\n",[62,121275,121276,121278,121280],{"class":64,"line":107},[62,121277,8585],{"class":1780},[62,121279,3696],{"class":72},[62,121281,54078],{"class":1675},[62,121283,121284,121286,121288,121291],{"class":64,"line":113},[62,121285,41306],{"class":1780},[62,121287,36721],{"class":72},[62,121289,121290],{"class":1675},"'vue'",[62,121292,8959],{"class":72},[62,121294,121295,121297,121299],{"class":64,"line":129},[62,121296,120658],{"class":1780},[62,121298,3696],{"class":72},[62,121300,121301],{"class":1675},"./triggering-events-router-vue-cover.png\n",[62,121303,121304,121307,121309],{"class":64,"line":134},[62,121305,121306],{"class":1780},"video",[62,121308,3696],{"class":72},[62,121310,121311],{"class":1675},"https://www.youtube.com/embed/JwccQYpsE2Q\n",[62,121313,121314],{"class":64,"line":156},[62,121315,121228],{"class":122},[22,121317,121318],{},"Now in my post template I could check to see if the post had a video and if did I would display that an not the cover.",[52,121320,121322],{"className":15773,"code":121321,"language":15775,"meta":57,"style":57},"\u003Cdiv class=\"embed-container\" v-if=\"$page.post.video\">\n \u003Ciframe\n width=\"1000\"\n height=\"563\"\n :src=\"$page.post.video\"\n frameborder=\"0\"\n allow=\"accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture\"\n allowfullscreen\n v-if=\"$page.post.video\"\n >\u003C/iframe>\n\u003C/div>\n\u003Cg-image v-if=\"!$page.post.video && $page.post.cover\" :src=\"$page.post.cover\" class=\"cover\" />\n",[59,121323,121324,121346,121353,121363,121373,121382,121392,121402,121407,121416,121425,121433],{"__ignoreMap":57},[62,121325,121326,121328,121330,121332,121334,121337,121339,121341,121344],{"class":64,"line":65},[62,121327,760],{"class":72},[62,121329,15944],{"class":1780},[62,121331,119],{"class":122},[62,121333,146],{"class":72},[62,121335,121336],{"class":1675},"\"embed-container\"",[62,121338,107033],{"class":122},[62,121340,146],{"class":72},[62,121342,121343],{"class":1675},"\"$page.post.video\"",[62,121345,1784],{"class":72},[62,121347,121348,121350],{"class":64,"line":76},[62,121349,33056],{"class":72},[62,121351,121352],{"class":1780},"iframe\n",[62,121354,121355,121358,121360],{"class":64,"line":82},[62,121356,121357],{"class":122}," width",[62,121359,146],{"class":72},[62,121361,121362],{"class":1675},"\"1000\"\n",[62,121364,121365,121368,121370],{"class":64,"line":89},[62,121366,121367],{"class":122}," height",[62,121369,146],{"class":72},[62,121371,121372],{"class":1675},"\"563\"\n",[62,121374,121375,121377,121379],{"class":64,"line":95},[62,121376,85770],{"class":122},[62,121378,146],{"class":72},[62,121380,121381],{"class":1675},"\"$page.post.video\"\n",[62,121383,121384,121387,121389],{"class":64,"line":101},[62,121385,121386],{"class":122}," frameborder",[62,121388,146],{"class":72},[62,121390,121391],{"class":1675},"\"0\"\n",[62,121393,121394,121397,121399],{"class":64,"line":107},[62,121395,121396],{"class":122}," allow",[62,121398,146],{"class":72},[62,121400,121401],{"class":1675},"\"accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture\"\n",[62,121403,121404],{"class":64,"line":113},[62,121405,121406],{"class":122}," allowfullscreen\n",[62,121408,121409,121412,121414],{"class":64,"line":129},[62,121410,121411],{"class":122}," v-if",[62,121413,146],{"class":72},[62,121415,121381],{"class":1675},[62,121417,121418,121421,121423],{"class":64,"line":134},[62,121419,121420],{"class":72}," >\u003C/",[62,121422,104354],{"class":1780},[62,121424,1784],{"class":72},[62,121426,121427,121429,121431],{"class":64,"line":156},[62,121428,1818],{"class":72},[62,121430,15944],{"class":1780},[62,121432,1784],{"class":72},[62,121434,121435,121437,121439,121441,121443,121446,121448,121450,121453,121455,121457,121460],{"class":64,"line":161},[62,121436,760],{"class":72},[62,121438,120199],{"class":1780},[62,121440,107033],{"class":122},[62,121442,146],{"class":72},[62,121444,121445],{"class":1675},"\"!$page.post.video && $page.post.cover\"",[62,121447,120202],{"class":122},[62,121449,146],{"class":72},[62,121451,121452],{"class":1675},"\"$page.post.cover\"",[62,121454,119],{"class":122},[62,121456,146],{"class":72},[62,121458,121459],{"class":1675},"\"cover\"",[62,121461,67133],{"class":72},[22,121463,121464,121465,121470],{},"Now when you go to a ",[677,121466,121469],{"href":121467,"rel":121468},"https://www.danvega.dev/blog/2019/06/05/triggering-events-router-vue",[681],"post that has a video"," it will display the YouTube embed instead of the cover image.",[22,121472,121473],{},[653,121474],{"alt":121475,"src":121476},"Blog Post Video Cover","/images/blog/2019/07/23/video-cover.png",[22,121478,121479],{},"If you're going to do something similar it's important to remember that you still need a cover image even if you aren't displaying one. This is because you will still use that image for social media sharing and you don't want to lose that.",[26,121481,1499],{"id":1498},[22,121483,121484],{},"I have a lot of fun working on my personal website because I am constantly learning new things. If you can find side projects that are fun and allow you to learn you are getting the best of both worlds. This was meant to be more of an overview of features I have been working on. If there is anything in the post that I talked about and you would like me to go more in-depth, please reach out and let me know. As always friends...",[22,121486,36004,121487,82545],{},[36006,121488],{},[1527,121490,121491],{},"html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .suaqH, html code.shiki .suaqH{--shiki-default:#22863A;--shiki-dark:#85E89D;--shiki-sepia:#22863A}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .s4-Ip, html code.shiki .s4-Ip{--shiki-default:#6A737D;--shiki-dark:#6A737D;--shiki-sepia:#6A737D}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}html pre.shiki code .spoOn, html code.shiki .spoOn{--shiki-default:#E36209;--shiki-dark:#FFAB70;--shiki-sepia:#E36209}",{"title":57,"searchDepth":76,"depth":76,"links":121493},[121494,121499,121500,121501,121502,121503,121504],{"id":119718,"depth":76,"text":119719,"children":121495},[121496,121497,121498],{"id":119725,"depth":82,"text":119726},{"id":119735,"depth":82,"text":119736},{"id":119751,"depth":82,"text":113460},{"id":119770,"depth":76,"text":119771},{"id":120014,"depth":76,"text":120015},{"id":10653,"depth":76,"text":120685},{"id":33895,"depth":76,"text":37673},{"id":121193,"depth":76,"text":121194},{"id":1498,"depth":76,"text":1499},{"_id":121506,"path":121507,"title":121508,"description":121509,"meta":121510,"body":121516},"content/blog/2019/06/13/vuepress-cookies.md","/blog/2019/06/13/vuepress-cookies","How to use cookies in VuePress","In this article, I am going to talk about a recent documentation site to VuePress and how I was able to solve a problem I ran across.",{"slug":121511,"date":121512,"published":13,"tags":121513,"author":17,"cover":121515,"excerpt":-1},"vuepress-cookies","2019-06-13T10:27:46.175Z",[105335,121514],"vuepress","./vuepress-cookies-cover.png",{"type":19,"value":121517,"toc":123270},[121518,121521,121524,121527,121531,121534,121537,121670,121673,121676,121680,121685,121688,121691,121695,121701,121715,121722,121820,121826,121830,121837,121901,121907,121917,122042,122049,122053,122062,122065,122138,122144,122147,122151,122154,122164,122218,122221,122420,122423,122907,122911,122914,122973,122980,123258,123260,123263,123267],[22,121519,121520],{},"Lately, I have been working on a large migration of documentation from Gitbook to VuePress. If you're curious about how to get started with VuePress or the process I used to determine if it would work for us please reach out to me and let me know.",[22,121522,121523],{},"This article is going to assume you have some experience working with VuePress so we can skip over some getting started stuff and move right into the problem at hand. We are going to cover this in detail but in short, we are going to learn How to use cookies in VuePress.",[22,121525,121526],{},"I had a few requirements that I needed to make sure VuePress could handle. In this article, I am going to talk about one of those requirements, the problems I ran into and how I eventually solved it.",[26,121528,121530],{"id":121529},"gitbook-migration","Gitbook Migration",[22,121532,121533],{},"Before we dive into some code we need to talk about the requirements and the problems I ran into. In the current version of the documentation we actually built multiple versions based on the programming language. We might have a concept that we explain and then show off code sample in any number of languages.",[22,121535,121536],{},"In Gitbook you can create a variable, set a default and then use that variable within your markdown templates like this:",[52,121538,121540],{"className":29690,"code":121539,"language":29692,"meta":57,"style":57},"{% if book.language === \"JavaScript\" %}\n\n```js\nclass Greeter {\n constructor(message) {\n this.message = message;\n }\n greet() {\n return `Hello, ${this.message}`;\n }\n}\n```\n\n{% elif book.language === 'TypeScript' %}\n\n```ts\nclass Greeter {\n greeting: string;\n constructor(message: string) {\n this.greeting = message;\n }\n greet() {\n return \"Hello, \" + this.greeting;\n }\n}\n```\n\n{% endif %}\n",[59,121541,121542,121547,121551,121556,121561,121566,121571,121575,121580,121585,121589,121593,121598,121602,121607,121611,121616,121620,121625,121630,121635,121639,121644,121649,121653,121657,121661,121665],{"__ignoreMap":57},[62,121543,121544],{"class":64,"line":65},[62,121545,121546],{},"{% if book.language === \"JavaScript\" %}\n",[62,121548,121549],{"class":64,"line":76},[62,121550,79],{"emptyLinePlaceholder":13},[62,121552,121553],{"class":64,"line":82},[62,121554,121555],{},"```js\n",[62,121557,121558],{"class":64,"line":89},[62,121559,121560],{},"class Greeter {\n",[62,121562,121563],{"class":64,"line":95},[62,121564,121565],{}," constructor(message) {\n",[62,121567,121568],{"class":64,"line":101},[62,121569,121570],{}," this.message = message;\n",[62,121572,121573],{"class":64,"line":107},[62,121574,3731],{},[62,121576,121577],{"class":64,"line":113},[62,121578,121579],{}," greet() {\n",[62,121581,121582],{"class":64,"line":129},[62,121583,121584],{}," return `Hello, ${this.message}`;\n",[62,121586,121587],{"class":64,"line":134},[62,121588,3731],{},[62,121590,121591],{"class":64,"line":156},[62,121592,379],{},[62,121594,121595],{"class":64,"line":161},[62,121596,121597],{},"```\n",[62,121599,121600],{"class":64,"line":167},[62,121601,79],{"emptyLinePlaceholder":13},[62,121603,121604],{"class":64,"line":173},[62,121605,121606],{},"{% elif book.language === 'TypeScript' %}\n",[62,121608,121609],{"class":64,"line":179},[62,121610,79],{"emptyLinePlaceholder":13},[62,121612,121613],{"class":64,"line":185},[62,121614,121615],{},"```ts\n",[62,121617,121618],{"class":64,"line":191},[62,121619,121560],{},[62,121621,121622],{"class":64,"line":209},[62,121623,121624],{}," greeting: string;\n",[62,121626,121627],{"class":64,"line":220},[62,121628,121629],{}," constructor(message: string) {\n",[62,121631,121632],{"class":64,"line":226},[62,121633,121634],{}," this.greeting = message;\n",[62,121636,121637],{"class":64,"line":231},[62,121638,223],{},[62,121640,121641],{"class":64,"line":236},[62,121642,121643],{}," greet() {\n",[62,121645,121646],{"class":64,"line":242},[62,121647,121648],{}," return \"Hello, \" + this.greeting;\n",[62,121650,121651],{"class":64,"line":247},[62,121652,223],{},[62,121654,121655],{"class":64,"line":252},[62,121656,379],{},[62,121658,121659],{"class":64,"line":257},[62,121660,121597],{},[62,121662,121663],{"class":64,"line":271},[62,121664,79],{"emptyLinePlaceholder":13},[62,121666,121667],{"class":64,"line":281},[62,121668,121669],{},"{% endif %}\n",[22,121671,121672],{},"When you visited the generated HTML we would only show the version you were interested in seeing. This could have been solved using tabs but there were cases where we conditionally would show entire sections so it wasn't just code.",[22,121674,121675],{},"When you ran the build you would pass in the language as an argument and build the documentation for that language. This worked but having multiple versions of the same documentation meant slow build times and unneeded duplication in production.",[26,121677,121679],{"id":121678},"mmmmmmmmmm-cookies","MMMMMMMMMM Cookies",[22,121681,121682],{},[653,121683],{"alt":57,"src":121684},"/images/blog/2019/06/13/AdobeStock_113771716-12be091c-ec88-49e5-b21f-6fe1a147485c.jpeg",[22,121686,121687],{},"I don't know about you but when I started thinking about this problem I immediately thought about cookies. This was in part because I was hungry at the time but I also knew this to be a good solution to my problem.",[22,121689,121690],{},"To me this is a visitor preference and something they can change at anytime. Just like the Gitbook solution I would be able to create a default value by dropping a cookie on the users machine the first time they visited the site. Then I would give them the opportunity to change this at anytime.",[636,121692,121694],{"id":121693},"vue-cookies","Vue Cookies",[22,121696,121697,121698,121700],{},"In my first attempt to solve this problem I brought in a package called ",[59,121699,121693],{},". I realize that creating a cookie isn't that difficult but having a nice API to manage everything that goes along with cookies is a nice to have. After a quick look at the documentation it looked really easy to setup so I went ahead and added it to my project.",[52,121702,121704],{"className":1663,"code":121703,"language":1665,"meta":57,"style":57},"npm install vue-cookies\n",[59,121705,121706],{"__ignoreMap":57},[62,121707,121708,121710,121712],{"class":64,"line":65},[62,121709,32645],{"class":122},[62,121711,32750],{"class":1675},[62,121713,121714],{"class":1675}," vue-cookies\n",[22,121716,121717,121718,121721],{},"In a normal Vue application I would jump into ",[59,121719,121720],{},"main.js"," and add the following.",[52,121723,121725],{"className":105863,"code":121724,"language":105865,"meta":57,"style":57},"import Vue from 'vue'\nimport VueCookies from 'vue-cookies'\n\n// install the plugin\nVue.use(VueCookies)\n\n// we want this cookie to last for 120 days\nVueCookies.config('120d')\n\n// set global cookie\nVueCookies.set('language','JavaScript');\n",[59,121726,121727,121739,121751,121755,121760,121770,121774,121779,121793,121797,121802],{"__ignoreMap":57},[62,121728,121729,121731,121734,121736],{"class":64,"line":65},[62,121730,27875],{"class":68},[62,121732,121733],{"class":72}," Vue ",[62,121735,3507],{"class":68},[62,121737,121738],{"class":1675}," 'vue'\n",[62,121740,121741,121743,121746,121748],{"class":64,"line":76},[62,121742,27875],{"class":68},[62,121744,121745],{"class":72}," VueCookies ",[62,121747,3507],{"class":68},[62,121749,121750],{"class":1675}," 'vue-cookies'\n",[62,121752,121753],{"class":64,"line":82},[62,121754,79],{"emptyLinePlaceholder":13},[62,121756,121757],{"class":64,"line":89},[62,121758,121759],{"class":85},"// install the plugin\n",[62,121761,121762,121765,121767],{"class":64,"line":95},[62,121763,121764],{"class":72},"Vue.",[62,121766,115169],{"class":122},[62,121768,121769],{"class":72},"(VueCookies)\n",[62,121771,121772],{"class":64,"line":101},[62,121773,79],{"emptyLinePlaceholder":13},[62,121775,121776],{"class":64,"line":107},[62,121777,121778],{"class":85},"// we want this cookie to last for 120 days\n",[62,121780,121781,121784,121786,121788,121791],{"class":64,"line":113},[62,121782,121783],{"class":72},"VueCookies.",[62,121785,48865],{"class":122},[62,121787,2109],{"class":72},[62,121789,121790],{"class":1675},"'120d'",[62,121792,2212],{"class":72},[62,121794,121795],{"class":64,"line":129},[62,121796,79],{"emptyLinePlaceholder":13},[62,121798,121799],{"class":64,"line":134},[62,121800,121801],{"class":85},"// set global cookie\n",[62,121803,121804,121806,121808,121810,121813,121815,121818],{"class":64,"line":156},[62,121805,121783],{"class":72},[62,121807,47595],{"class":122},[62,121809,2109],{"class":72},[62,121811,121812],{"class":1675},"'language'",[62,121814,32225],{"class":72},[62,121816,121817],{"class":1675},"'JavaScript'",[62,121819,1133],{"class":72},[22,121821,121822,121823,121825],{},"But this is VuePress and I don't have a ",[59,121824,121720],{}," so how can I hook into the existing Vue instance?",[636,121827,121829],{"id":121828},"app-level-enhancements","App Level Enhancements",[22,121831,121832,121833,121836],{},"Since the VuePress app is a standard Vue app, you can apply app-level enhancements by creating a file ",[59,121834,121835],{},".vuepress/enhanceApp.js",", which will be imported into the app if it is present. The file should export default a hook function which will receive an object containing some app level values. You can use this hook to install additional Vue plugins, register global components, or add additional router hooks:",[52,121838,121840],{"className":105863,"code":121839,"language":105865,"meta":57,"style":57},"export default ({\n Vue, // the version of Vue being used in the VuePress app\n options, // the options for the root Vue instance\n router, // the router instance for the app\n siteData // site metadata\n}) => {\n // ...apply enhancements to the app\n}\n",[59,121841,121842,121851,121859,121867,121875,121883,121892,121897],{"__ignoreMap":57},[62,121843,121844,121846,121848],{"class":64,"line":65},[62,121845,14767],{"class":68},[62,121847,106045],{"class":68},[62,121849,121850],{"class":72}," ({\n",[62,121852,121853,121856],{"class":64,"line":76},[62,121854,121855],{"class":72}," Vue, ",[62,121857,121858],{"class":85},"// the version of Vue being used in the VuePress app\n",[62,121860,121861,121864],{"class":64,"line":82},[62,121862,121863],{"class":72}," options, ",[62,121865,121866],{"class":85},"// the options for the root Vue instance\n",[62,121868,121869,121872],{"class":64,"line":89},[62,121870,121871],{"class":72}," router, ",[62,121873,121874],{"class":85},"// the router instance for the app\n",[62,121876,121877,121880],{"class":64,"line":95},[62,121878,121879],{"class":72}," siteData ",[62,121881,121882],{"class":85},"// site metadata\n",[62,121884,121885,121888,121890],{"class":64,"line":101},[62,121886,121887],{"class":72},"}) ",[62,121889,21525],{"class":68},[62,121891,126],{"class":72},[62,121893,121894],{"class":64,"line":107},[62,121895,121896],{"class":85}," // ...apply enhancements to the app\n",[62,121898,121899],{"class":64,"line":113},[62,121900,379],{"class":72},[22,121902,121903],{},[677,121904,121905],{"href":121905,"rel":121906},"https://vuepress.vuejs.org/guide/basic-config.html#app-level-enhancements",[681],[22,121908,121909,121910,121912,121913,121916],{},"This sounds exactly like what I needed so I quickly setup ",[59,121911,121835],{}," and added the following code. This allows me to call ",[59,121914,121915],{},"Vue.use()"," to install the plugin and set a default cookie if one doesn't exist.",[52,121918,121920],{"className":105863,"code":121919,"language":105865,"meta":57,"style":57},"import VueCookies from 'vue-cookies'\n\nexport default ({ Vue, options, router, siteData }) => {\n\n Vue.use(VueCookies)\n VueCookies.config('120d')\n if( !$cookies.isKey('language') ) {\n VueCookies.set('language','JavaScript');\n }\n\n}\n",[59,121921,121922,121932,121936,121967,121971,121980,121993,122013,122030,122034,122038],{"__ignoreMap":57},[62,121923,121924,121926,121928,121930],{"class":64,"line":65},[62,121925,27875],{"class":68},[62,121927,121745],{"class":72},[62,121929,3507],{"class":68},[62,121931,121750],{"class":1675},[62,121933,121934],{"class":64,"line":76},[62,121935,79],{"emptyLinePlaceholder":13},[62,121937,121938,121940,121942,121945,121947,121949,121951,121953,121956,121958,121961,121963,121965],{"class":64,"line":82},[62,121939,14767],{"class":68},[62,121941,106045],{"class":68},[62,121943,121944],{"class":72}," ({ ",[62,121946,105462],{"class":889},[62,121948,976],{"class":72},[62,121950,120802],{"class":889},[62,121952,976],{"class":72},[62,121954,121955],{"class":889},"router",[62,121957,976],{"class":72},[62,121959,121960],{"class":889},"siteData",[62,121962,120854],{"class":72},[62,121964,21525],{"class":68},[62,121966,126],{"class":72},[62,121968,121969],{"class":64,"line":89},[62,121970,79],{"emptyLinePlaceholder":13},[62,121972,121973,121976,121978],{"class":64,"line":95},[62,121974,121975],{"class":72}," Vue.",[62,121977,115169],{"class":122},[62,121979,121769],{"class":72},[62,121981,121982,121985,121987,121989,121991],{"class":64,"line":101},[62,121983,121984],{"class":72}," VueCookies.",[62,121986,48865],{"class":122},[62,121988,2109],{"class":72},[62,121990,121790],{"class":1675},[62,121992,2212],{"class":72},[62,121994,121995,121997,121999,122001,122004,122007,122009,122011],{"class":64,"line":107},[62,121996,107558],{"class":68},[62,121998,52630],{"class":72},[62,122000,6277],{"class":68},[62,122002,122003],{"class":72},"$cookies.",[62,122005,122006],{"class":122},"isKey",[62,122008,2109],{"class":72},[62,122010,121812],{"class":1675},[62,122012,63585],{"class":72},[62,122014,122015,122018,122020,122022,122024,122026,122028],{"class":64,"line":113},[62,122016,122017],{"class":72}," VueCookies.",[62,122019,47595],{"class":122},[62,122021,2109],{"class":72},[62,122023,121812],{"class":1675},[62,122025,32225],{"class":72},[62,122027,121817],{"class":1675},[62,122029,1133],{"class":72},[62,122031,122032],{"class":64,"line":129},[62,122033,3731],{"class":72},[62,122035,122036],{"class":64,"line":134},[62,122037,79],{"emptyLinePlaceholder":13},[62,122039,122040],{"class":64,"line":156},[62,122041,379],{"class":72},[22,122043,122044,122045,122048],{},"This actually worked out really well and I was really happy with the solution. That was, until I went to build a production version of the documentation as a test. When I ran ",[59,122046,122047],{},"vuepress build"," I started getting some errors saying that window was not defined and I knew right away that I forgot about an important detail.",[636,122050,122052],{"id":122051},"browser-api-restrictions","Browser API Restrictions",[22,122054,122055,122056,122061],{},"Because VuePress applications are server-rendered in Node.js when generating static builds, any Vue usage must conform to the ",[677,122057,122060],{"href":122058,"rel":122059},"https://ssr.vuejs.org/en/universal.html",[681],"universal code requirements",". In short, make sure to only access Browser / DOM APIs in beforeMount or mounted hooks.",[22,122063,122064],{},"In order to use code that assumes a browser environment on import, you need to dynamically import them in proper lifecycle hooks:",[52,122066,122068],{"className":105863,"code":122067,"language":105865,"meta":57,"style":57},"\u003Cscript>\nexport default {\n mounted () {\n import('./lib-that-access-window-on-import').then(module => {\n // use code\n })\n }\n}\n\u003C/script>\n",[59,122069,122070,122078,122083,122090,122112,122117,122122,122126,122130],{"__ignoreMap":57},[62,122071,122072,122074,122076],{"class":64,"line":65},[62,122073,760],{"class":72},[62,122075,15846],{"class":1780},[62,122077,1784],{"class":72},[62,122079,122080],{"class":64,"line":76},[62,122081,122082],{"class":72},"export default {\n",[62,122084,122085,122087],{"class":64,"line":82},[62,122086,106219],{"class":122},[62,122088,122089],{"class":72}," () {\n",[62,122091,122092,122095,122097,122100,122102,122104,122106,122108,122110],{"class":64,"line":89},[62,122093,122094],{"class":122}," import",[62,122096,2109],{"class":72},[62,122098,122099],{"class":1675},"'./lib-that-access-window-on-import'",[62,122101,15503],{"class":72},[62,122103,36912],{"class":122},[62,122105,2109],{"class":72},[62,122107,32813],{"class":889},[62,122109,85402],{"class":68},[62,122111,126],{"class":72},[62,122113,122114],{"class":64,"line":95},[62,122115,122116],{"class":85}," // use code\n",[62,122118,122119],{"class":64,"line":101},[62,122120,122121],{"class":72}," })\n",[62,122123,122124],{"class":64,"line":107},[62,122125,3731],{"class":72},[62,122127,122128],{"class":64,"line":113},[62,122129,379],{"class":72},[62,122131,122132,122134,122136],{"class":64,"line":129},[62,122133,1818],{"class":72},[62,122135,15846],{"class":1780},[62,122137,1784],{"class":72},[22,122139,122140],{},[677,122141,122142],{"href":122142,"rel":122143},"https://v1.vuepress.vuejs.org/guide/using-vue.html#browser-api-access-restrictions",[681],[22,122145,122146],{},"Armed with this knowledge I tried to hack a few things together but fell flat on my face. After banging my head against the desk a few more times I realized that I needed to look at this from a different angle so it was back to the drawing board for me.",[636,122148,122150],{"id":122149},"rethinking-the-solution","Rethinking the Solution",[22,122152,122153],{},"When I started to think about the problem more I realized that I only needed a script to run once when the application loads for the visitor. At that point I can check to see if there is a cookie and if there isn't, create a default one. From there I can always give the user the option to change the language from another page.",[22,122155,122156,122157,122160,122161,113306],{},"So now the question was how could I add a script to the application where I could perform this logic. I remember from earlier that in ",[59,122158,122159],{},".vuepress/config.js"," I was adding a favicon to the site using the following code. I did some digging around and sure enough I could push a script here as well by dropping it into the ",[59,122162,122163],{},".vuepress/public/scripts",[52,122165,122167],{"className":105863,"code":122166,"language":105865,"meta":57,"style":57},"head: [\n ['link', { rel: 'icon', href: '/favicon.png' }],\n ['script', { src: '/scripts/cookies.js' }]\n],\n",[59,122168,122169,122175,122198,122214],{"__ignoreMap":57},[62,122170,122171,122173],{"class":64,"line":65},[62,122172,15824],{"class":122},[62,122174,3709],{"class":72},[62,122176,122177,122180,122183,122186,122189,122192,122195],{"class":64,"line":76},[62,122178,122179],{"class":72}," [",[62,122181,122182],{"class":1675},"'link'",[62,122184,122185],{"class":72},", { rel: ",[62,122187,122188],{"class":1675},"'icon'",[62,122190,122191],{"class":72},", href: ",[62,122193,122194],{"class":1675},"'/favicon.png'",[62,122196,122197],{"class":72}," }],\n",[62,122199,122200,122202,122205,122208,122211],{"class":64,"line":82},[62,122201,122179],{"class":72},[62,122203,122204],{"class":1675},"'script'",[62,122206,122207],{"class":72},", { src: ",[62,122209,122210],{"class":1675},"'/scripts/cookies.js'",[62,122212,122213],{"class":72}," }]\n",[62,122215,122216],{"class":64,"line":89},[62,122217,32833],{"class":72},[22,122219,122220],{},"A VuePress application is a single page application so this was only going to run once. Every subsequent request was loaded through the app so this script would only be called once. This was actually ok for this problem so I continued on and built a small cookie script.",[52,122222,122224],{"className":105863,"code":122223,"language":105865,"meta":57,"style":57},"document.addEventListener(\"DOMContentLoaded\", () => {\n\n // if a cookie has not been defined and they aren't on the language selection page\n if( !cookieExists('language') && window.location.pathname != '/language.html' ) {\n // a cookie doesn't exist yet, we need to create one with a default language.\n document.cookie = `language=javascript;max-age=${60*60*24*120};path=/`;\n // we are setting a default cookie but we still want the visitor to have a chance to change it\n window.location.href=\"/language.html\";\n }\n\n})\n\nfunction cookieExists(name) {\n return document.cookie.split(';').filter((item) => item.trim().startsWith(`${name}=`)).length;\n}\n",[59,122225,122226,122244,122248,122253,122282,122287,122318,122323,122335,122339,122343,122347,122351,122364,122416],{"__ignoreMap":57},[62,122227,122228,122231,122233,122235,122238,122240,122242],{"class":64,"line":65},[62,122229,122230],{"class":72},"document.",[62,122232,21036],{"class":122},[62,122234,2109],{"class":72},[62,122236,122237],{"class":1675},"\"DOMContentLoaded\"",[62,122239,27373],{"class":72},[62,122241,21525],{"class":68},[62,122243,126],{"class":72},[62,122245,122246],{"class":64,"line":76},[62,122247,79],{"emptyLinePlaceholder":13},[62,122249,122250],{"class":64,"line":82},[62,122251,122252],{"class":85}," // if a cookie has not been defined and they aren't on the language selection page\n",[62,122254,122255,122257,122259,122261,122264,122266,122268,122270,122272,122275,122277,122280],{"class":64,"line":89},[62,122256,107558],{"class":68},[62,122258,52630],{"class":72},[62,122260,6277],{"class":68},[62,122262,122263],{"class":122},"cookieExists",[62,122265,2109],{"class":72},[62,122267,121812],{"class":1675},[62,122269,5024],{"class":72},[62,122271,74610],{"class":68},[62,122273,122274],{"class":72}," window.location.pathname ",[62,122276,13321],{"class":68},[62,122278,122279],{"class":1675}," '/language.html'",[62,122281,73082],{"class":72},[62,122283,122284],{"class":64,"line":95},[62,122285,122286],{"class":85}," // a cookie doesn't exist yet, we need to create one with a default language.\n",[62,122288,122289,122292,122294,122297,122300,122302,122304,122306,122308,122310,122313,122316],{"class":64,"line":101},[62,122290,122291],{"class":72}," document.cookie ",[62,122293,146],{"class":68},[62,122295,122296],{"class":1675}," `language=javascript;max-age=${",[62,122298,122299],{"class":149},"60",[62,122301,6973],{"class":68},[62,122303,122299],{"class":149},[62,122305,6973],{"class":68},[62,122307,45730],{"class":149},[62,122309,6973],{"class":68},[62,122311,122312],{"class":149},"120",[62,122314,122315],{"class":1675},"};path=/`",[62,122317,153],{"class":72},[62,122319,122320],{"class":64,"line":107},[62,122321,122322],{"class":85}," // we are setting a default cookie but we still want the visitor to have a chance to change it\n",[62,122324,122325,122328,122330,122333],{"class":64,"line":113},[62,122326,122327],{"class":72}," window.location.href",[62,122329,146],{"class":68},[62,122331,122332],{"class":1675},"\"/language.html\"",[62,122334,153],{"class":72},[62,122336,122337],{"class":64,"line":129},[62,122338,3731],{"class":72},[62,122340,122341],{"class":64,"line":134},[62,122342,79],{"emptyLinePlaceholder":13},[62,122344,122345],{"class":64,"line":156},[62,122346,50607],{"class":72},[62,122348,122349],{"class":64,"line":161},[62,122350,79],{"emptyLinePlaceholder":13},[62,122352,122353,122355,122358,122360,122362],{"class":64,"line":167},[62,122354,21046],{"class":68},[62,122356,122357],{"class":122}," cookieExists",[62,122359,2109],{"class":72},[62,122361,3107],{"class":889},[62,122363,768],{"class":72},[62,122365,122366,122368,122371,122373,122375,122378,122380,122382,122384,122387,122389,122391,122394,122396,122398,122400,122402,122405,122407,122410,122412,122414],{"class":64,"line":173},[62,122367,82091],{"class":68},[62,122369,122370],{"class":72}," document.cookie.",[62,122372,6894],{"class":122},[62,122374,2109],{"class":72},[62,122376,122377],{"class":1675},"';'",[62,122379,15503],{"class":72},[62,122381,3216],{"class":122},[62,122383,85590],{"class":72},[62,122385,122386],{"class":889},"item",[62,122388,5024],{"class":72},[62,122390,21525],{"class":68},[62,122392,122393],{"class":72}," item.",[62,122395,110699],{"class":122},[62,122397,3229],{"class":72},[62,122399,4265],{"class":122},[62,122401,2109],{"class":72},[62,122403,122404],{"class":1675},"`${",[62,122406,3107],{"class":72},[62,122408,122409],{"class":1675},"}=`",[62,122411,81247],{"class":72},[62,122413,14193],{"class":149},[62,122415,153],{"class":72},[62,122417,122418],{"class":64,"line":179},[62,122419,379],{"class":72},[22,122421,122422],{},"The script will check to see if the cookie exists and if it doesn't it will create a default one and forward you on the language selection page. This is nothing more than a simple markdown page with some copy and a custom component I built to change the cookie value.",[52,122424,122426],{"className":15773,"code":122425,"language":15775,"meta":57,"style":57},"\u003Ctemplate>\n \u003Cdiv class=\"language\">\n \u003Cp>Current Language: {{ currentLanguage }}\u003C/p>\n\n \u003Cselect @change=\"updateLanguage($event)\">\n \u003Coption value=\"\">Change Language\u003C/option>\n \u003Coption value=\"javascript\">JavaScript\u003C/option>\n \u003Coption value=\"typescript\">TypeScript\u003C/option>\n \u003C/select>\n\n \u003C/div>\n\u003C/template>\n\n\u003Cscript>\nexport default {\n name: 'language-select',\n data() {\n return {\n languages: [\n { label: 'JavaScript', value: 'javascript' },\n { lagel: 'TypeScript', value: 'typescript' }\n ],\n currentLanguage: ''\n }\n },\n methods: {\n updateLanguage(event) {\n const language = event.target.value;\n this.setCookie(language);\n this.currentLanguage = language;\n },\n setCookie(value) {\n document.cookie = `language=${value};max-age=${60*60*24*120};path=/`;\n },\n getCookie() {\n return document.cookie.replace(/(?:(?:^|.*;\\s*)language\\s*\\=\\s*([^;]*).*$)|^.*$/, \"$1\");\n },\n },\n mounted() {\n this.currentLanguage = this.getCookie();\n }\n}\n\u003C/script>\n",[59,122427,122428,122436,122451,122464,122468,122484,122504,122524,122544,122552,122556,122564,122572,122576,122584,122592,122601,122607,122613,122618,122634,122650,122654,122661,122665,122669,122673,122684,122696,122709,122721,122725,122736,122769,122773,122780,122860,122864,122868,122874,122891,122895,122899],{"__ignoreMap":57},[62,122429,122430,122432,122434],{"class":64,"line":65},[62,122431,760],{"class":72},[62,122433,105991],{"class":1780},[62,122435,1784],{"class":72},[62,122437,122438,122440,122442,122444,122446,122449],{"class":64,"line":76},[62,122439,33056],{"class":72},[62,122441,15944],{"class":1780},[62,122443,119],{"class":122},[62,122445,146],{"class":72},[62,122447,122448],{"class":1675},"\"language\"",[62,122450,1784],{"class":72},[62,122452,122453,122455,122457,122460,122462],{"class":64,"line":82},[62,122454,1789],{"class":72},[62,122456,22],{"class":1780},[62,122458,122459],{"class":72},">Current Language: {{ currentLanguage }}\u003C/",[62,122461,22],{"class":1780},[62,122463,1784],{"class":72},[62,122465,122466],{"class":64,"line":89},[62,122467,79],{"emptyLinePlaceholder":13},[62,122469,122470,122472,122474,122477,122479,122482],{"class":64,"line":95},[62,122471,1789],{"class":72},[62,122473,45898],{"class":1780},[62,122475,122476],{"class":122}," @change",[62,122478,146],{"class":72},[62,122480,122481],{"class":1675},"\"updateLanguage($event)\"",[62,122483,1784],{"class":72},[62,122485,122486,122488,122491,122493,122495,122497,122500,122502],{"class":64,"line":101},[62,122487,92881],{"class":72},[62,122489,122490],{"class":1780},"option",[62,122492,16115],{"class":122},[62,122494,146],{"class":72},[62,122496,25895],{"class":1675},[62,122498,122499],{"class":72},">Change Language\u003C/",[62,122501,122490],{"class":1780},[62,122503,1784],{"class":72},[62,122505,122506,122508,122510,122512,122514,122517,122520,122522],{"class":64,"line":107},[62,122507,92881],{"class":72},[62,122509,122490],{"class":1780},[62,122511,16115],{"class":122},[62,122513,146],{"class":72},[62,122515,122516],{"class":1675},"\"javascript\"",[62,122518,122519],{"class":72},">JavaScript\u003C/",[62,122521,122490],{"class":1780},[62,122523,1784],{"class":72},[62,122525,122526,122528,122530,122532,122534,122537,122540,122542],{"class":64,"line":113},[62,122527,92881],{"class":72},[62,122529,122490],{"class":1780},[62,122531,16115],{"class":122},[62,122533,146],{"class":72},[62,122535,122536],{"class":1675},"\"typescript\"",[62,122538,122539],{"class":72},">TypeScript\u003C/",[62,122541,122490],{"class":1780},[62,122543,1784],{"class":72},[62,122545,122546,122548,122550],{"class":64,"line":129},[62,122547,1982],{"class":72},[62,122549,45898],{"class":1780},[62,122551,1784],{"class":72},[62,122553,122554],{"class":64,"line":134},[62,122555,79],{"emptyLinePlaceholder":13},[62,122557,122558,122560,122562],{"class":64,"line":156},[62,122559,33187],{"class":72},[62,122561,15944],{"class":1780},[62,122563,1784],{"class":72},[62,122565,122566,122568,122570],{"class":64,"line":161},[62,122567,1818],{"class":72},[62,122569,105991],{"class":1780},[62,122571,1784],{"class":72},[62,122573,122574],{"class":64,"line":167},[62,122575,79],{"emptyLinePlaceholder":13},[62,122577,122578,122580,122582],{"class":64,"line":173},[62,122579,760],{"class":72},[62,122581,15846],{"class":1780},[62,122583,1784],{"class":72},[62,122585,122586,122588,122590],{"class":64,"line":179},[62,122587,14767],{"class":68},[62,122589,106045],{"class":68},[62,122591,126],{"class":72},[62,122593,122594,122596,122599],{"class":64,"line":185},[62,122595,106052],{"class":72},[62,122597,122598],{"class":1675},"'language-select'",[62,122600,3338],{"class":72},[62,122602,122603,122605],{"class":64,"line":191},[62,122604,106190],{"class":122},[62,122606,206],{"class":72},[62,122608,122609,122611],{"class":64,"line":209},[62,122610,2599],{"class":68},[62,122612,126],{"class":72},[62,122614,122615],{"class":64,"line":220},[62,122616,122617],{"class":72}," languages: [\n",[62,122619,122620,122623,122625,122628,122631],{"class":64,"line":226},[62,122621,122622],{"class":72}," { label: ",[62,122624,121817],{"class":1675},[62,122626,122627],{"class":72},", value: ",[62,122629,122630],{"class":1675},"'javascript'",[62,122632,122633],{"class":72}," },\n",[62,122635,122636,122639,122642,122644,122647],{"class":64,"line":231},[62,122637,122638],{"class":72}," { lagel: ",[62,122640,122641],{"class":1675},"'TypeScript'",[62,122643,122627],{"class":72},[62,122645,122646],{"class":1675},"'typescript'",[62,122648,122649],{"class":72}," }\n",[62,122651,122652],{"class":64,"line":236},[62,122653,49905],{"class":72},[62,122655,122656,122659],{"class":64,"line":242},[62,122657,122658],{"class":72}," currentLanguage: ",[62,122660,106206],{"class":1675},[62,122662,122663],{"class":64,"line":247},[62,122664,223],{"class":72},[62,122666,122667],{"class":64,"line":252},[62,122668,32848],{"class":72},[62,122670,122671],{"class":64,"line":257},[62,122672,107160],{"class":72},[62,122674,122675,122678,122680,122682],{"class":64,"line":271},[62,122676,122677],{"class":122}," updateLanguage",[62,122679,2109],{"class":72},[62,122681,21051],{"class":889},[62,122683,768],{"class":72},[62,122685,122686,122688,122691,122693],{"class":64,"line":281},[62,122687,115079],{"class":68},[62,122689,122690],{"class":149}," language",[62,122692,2556],{"class":68},[62,122694,122695],{"class":72}," event.target.value;\n",[62,122697,122698,122701,122703,122706],{"class":64,"line":286},[62,122699,122700],{"class":149}," this",[62,122702,2755],{"class":72},[62,122704,122705],{"class":122},"setCookie",[62,122707,122708],{"class":72},"(language);\n",[62,122710,122711,122713,122716,122718],{"class":64,"line":291},[62,122712,122700],{"class":149},[62,122714,122715],{"class":72},".currentLanguage ",[62,122717,146],{"class":68},[62,122719,122720],{"class":72}," language;\n",[62,122722,122723],{"class":64,"line":296},[62,122724,50124],{"class":72},[62,122726,122727,122730,122732,122734],{"class":64,"line":302},[62,122728,122729],{"class":122}," setCookie",[62,122731,2109],{"class":72},[62,122733,2553],{"class":889},[62,122735,768],{"class":72},[62,122737,122738,122741,122743,122746,122748,122751,122753,122755,122757,122759,122761,122763,122765,122767],{"class":64,"line":308},[62,122739,122740],{"class":72}," document.cookie ",[62,122742,146],{"class":68},[62,122744,122745],{"class":1675}," `language=${",[62,122747,2553],{"class":72},[62,122749,122750],{"class":1675},"};max-age=${",[62,122752,122299],{"class":149},[62,122754,6973],{"class":68},[62,122756,122299],{"class":149},[62,122758,6973],{"class":68},[62,122760,45730],{"class":149},[62,122762,6973],{"class":68},[62,122764,122312],{"class":149},[62,122766,122315],{"class":1675},[62,122768,153],{"class":72},[62,122770,122771],{"class":64,"line":314},[62,122772,50124],{"class":72},[62,122774,122775,122778],{"class":64,"line":320},[62,122776,122777],{"class":122}," getCookie",[62,122779,206],{"class":72},[62,122781,122782,122784,122786,122788,122790,122792,122795,122798,122800,122802,122805,122808,122810,122813,122815,122817,122820,122822,122824,122826,122828,122830,122833,122835,122837,122839,122842,122844,122847,122849,122851,122853,122855,122858],{"class":64,"line":326},[62,122783,28884],{"class":68},[62,122785,122370],{"class":72},[62,122787,68699],{"class":122},[62,122789,2109],{"class":72},[62,122791,6936],{"class":1675},[62,122793,122794],{"class":73988},"(?:(?:",[62,122796,122797],{"class":68},"^|",[62,122799,2755],{"class":149},[62,122801,6973],{"class":68},[62,122803,122804],{"class":73988},";",[62,122806,122807],{"class":149},"\\s",[62,122809,6973],{"class":68},[62,122811,122812],{"class":73988},")language",[62,122814,122807],{"class":149},[62,122816,6973],{"class":68},[62,122818,122819],{"class":73985},"\\=",[62,122821,122807],{"class":149},[62,122823,6973],{"class":68},[62,122825,2109],{"class":73988},[62,122827,113672],{"class":149},[62,122829,74041],{"class":68},[62,122831,122832],{"class":149},";]",[62,122834,6973],{"class":68},[62,122836,2712],{"class":73988},[62,122838,2755],{"class":149},[62,122840,122841],{"class":68},"*$",[62,122843,2712],{"class":73988},[62,122845,122846],{"class":68},"|^",[62,122848,2755],{"class":149},[62,122850,122841],{"class":68},[62,122852,6936],{"class":1675},[62,122854,976],{"class":72},[62,122856,122857],{"class":1675},"\"$1\"",[62,122859,1133],{"class":72},[62,122861,122862],{"class":64,"line":338},[62,122863,50124],{"class":72},[62,122865,122866],{"class":64,"line":343},[62,122867,32848],{"class":72},[62,122869,122870,122872],{"class":64,"line":357},[62,122871,106219],{"class":122},[62,122873,206],{"class":72},[62,122875,122876,122878,122880,122882,122884,122886,122889],{"class":64,"line":366},[62,122877,39124],{"class":149},[62,122879,122715],{"class":72},[62,122881,146],{"class":68},[62,122883,9961],{"class":149},[62,122885,2755],{"class":72},[62,122887,122888],{"class":122},"getCookie",[62,122890,822],{"class":72},[62,122892,122893],{"class":64,"line":371},[62,122894,3731],{"class":72},[62,122896,122897],{"class":64,"line":376},[62,122898,379],{"class":72},[62,122900,122901,122903,122905],{"class":64,"line":16333},[62,122902,1818],{"class":72},[62,122904,15846],{"class":1780},[62,122906,1784],{"class":72},[26,122908,122910],{"id":122909},"custom-component-to-read-cookie","Custom Component to read cookie",[22,122912,122913],{},"Now that everything was in place I needed a way to conditionally check in markdown what language the user was set to. It might make sense to start with the component but I like to start with what I want my markup to look like. If I am in markdown and I want to only display the following code if the user's language selection is TypeScript I would envision writing the following markup.",[52,122915,122917],{"className":29690,"code":122916,"language":29692,"meta":57,"style":57},"\u003Ccode-block langugage=\"typescript\">\n```ts\nclass Greeter {\n greeting: string;\n constructor(message: string) {\n this.greeting = message;\n }\n greet() {\n return \"Hello, \" + this.greeting;\n }\n}\n```\n\u003C/code-block>\n",[59,122918,122919,122924,122928,122932,122936,122940,122944,122948,122952,122956,122960,122964,122968],{"__ignoreMap":57},[62,122920,122921],{"class":64,"line":65},[62,122922,122923],{},"\u003Ccode-block langugage=\"typescript\">\n",[62,122925,122926],{"class":64,"line":76},[62,122927,121615],{},[62,122929,122930],{"class":64,"line":82},[62,122931,121560],{},[62,122933,122934],{"class":64,"line":89},[62,122935,121624],{},[62,122937,122938],{"class":64,"line":95},[62,122939,121629],{},[62,122941,122942],{"class":64,"line":101},[62,122943,121634],{},[62,122945,122946],{"class":64,"line":107},[62,122947,223],{},[62,122949,122950],{"class":64,"line":113},[62,122951,121643],{},[62,122953,122954],{"class":64,"line":129},[62,122955,121648],{},[62,122957,122958],{"class":64,"line":134},[62,122959,223],{},[62,122961,122962],{"class":64,"line":156},[62,122963,379],{},[62,122965,122966],{"class":64,"line":161},[62,122967,121597],{},[62,122969,122970],{"class":64,"line":167},[62,122971,122972],{},"\u003C/code-block>\n",[22,122974,122975,122976,122979],{},"This allows me to write code in normal markdown code fences which makes me happy. To pass whatever is between the component tags you can use a slot and I can use a variable to determine if I should display the content or not. The last piece of the puzzle is to read the cookie value and we can do that in the mounted method because we know at that point the DOM is available. If you create ",[59,122977,122978],{},".vuepress/components/CodeBlock.vue"," with the following the code above should work.",[52,122981,122983],{"className":15773,"code":122982,"language":15775,"meta":57,"style":57},"\u003Ctemplate>\n \u003Cdiv class=\"code-block\">\n \u003Cslot v-if=\"display\"/>\n \u003C/div>\n\u003C/template>\n\n\u003Cscript>\nexport default {\n name: 'code-block',\n props: {\n language: String\n },\n data() {\n return {\n display: false\n }\n },\n methods: {\n getCookie() {\n return document.cookie.replace(/(?:(?:^|.*;\\s*)language\\s*\\=\\s*([^;]*).*$)|^.*$/, \"$1\");\n },\n },\n mounted() {\n const cookieValue = this.getCookie();\n this.display = cookieValue === this.language;\n }\n}\n\u003C/script>\n",[59,122984,122985,122993,123008,123026,123034,123042,123046,123054,123062,123071,123076,123081,123085,123091,123097,123104,123108,123112,123116,123122,123192,123196,123200,123206,123223,123242,123246,123250],{"__ignoreMap":57},[62,122986,122987,122989,122991],{"class":64,"line":65},[62,122988,760],{"class":72},[62,122990,105991],{"class":1780},[62,122992,1784],{"class":72},[62,122994,122995,122997,122999,123001,123003,123006],{"class":64,"line":76},[62,122996,33056],{"class":72},[62,122998,15944],{"class":1780},[62,123000,119],{"class":122},[62,123002,146],{"class":72},[62,123004,123005],{"class":1675},"\"code-block\"",[62,123007,1784],{"class":72},[62,123009,123010,123012,123015,123017,123019,123022,123024],{"class":64,"line":82},[62,123011,1789],{"class":72},[62,123013,123014],{"class":1780},"slot",[62,123016,107033],{"class":122},[62,123018,146],{"class":72},[62,123020,123021],{"class":1675},"\"display\"",[62,123023,6936],{"class":23824},[62,123025,1784],{"class":72},[62,123027,123028,123030,123032],{"class":64,"line":89},[62,123029,33187],{"class":72},[62,123031,15944],{"class":1780},[62,123033,1784],{"class":72},[62,123035,123036,123038,123040],{"class":64,"line":95},[62,123037,1818],{"class":72},[62,123039,105991],{"class":1780},[62,123041,1784],{"class":72},[62,123043,123044],{"class":64,"line":101},[62,123045,79],{"emptyLinePlaceholder":13},[62,123047,123048,123050,123052],{"class":64,"line":107},[62,123049,760],{"class":72},[62,123051,15846],{"class":1780},[62,123053,1784],{"class":72},[62,123055,123056,123058,123060],{"class":64,"line":113},[62,123057,14767],{"class":68},[62,123059,106045],{"class":68},[62,123061,126],{"class":72},[62,123063,123064,123066,123069],{"class":64,"line":129},[62,123065,106052],{"class":72},[62,123067,123068],{"class":1675},"'code-block'",[62,123070,3338],{"class":72},[62,123072,123073],{"class":64,"line":134},[62,123074,123075],{"class":72}," props: {\n",[62,123077,123078],{"class":64,"line":156},[62,123079,123080],{"class":72}," language: String\n",[62,123082,123083],{"class":64,"line":161},[62,123084,32848],{"class":72},[62,123086,123087,123089],{"class":64,"line":167},[62,123088,106190],{"class":122},[62,123090,206],{"class":72},[62,123092,123093,123095],{"class":64,"line":173},[62,123094,2599],{"class":68},[62,123096,126],{"class":72},[62,123098,123099,123102],{"class":64,"line":179},[62,123100,123101],{"class":72}," display: ",[62,123103,40782],{"class":149},[62,123105,123106],{"class":64,"line":185},[62,123107,223],{"class":72},[62,123109,123110],{"class":64,"line":191},[62,123111,32848],{"class":72},[62,123113,123114],{"class":64,"line":209},[62,123115,107160],{"class":72},[62,123117,123118,123120],{"class":64,"line":220},[62,123119,122777],{"class":122},[62,123121,206],{"class":72},[62,123123,123124,123126,123128,123130,123132,123134,123136,123138,123140,123142,123144,123146,123148,123150,123152,123154,123156,123158,123160,123162,123164,123166,123168,123170,123172,123174,123176,123178,123180,123182,123184,123186,123188,123190],{"class":64,"line":226},[62,123125,28884],{"class":68},[62,123127,122370],{"class":72},[62,123129,68699],{"class":122},[62,123131,2109],{"class":72},[62,123133,6936],{"class":1675},[62,123135,122794],{"class":73988},[62,123137,122797],{"class":68},[62,123139,2755],{"class":149},[62,123141,6973],{"class":68},[62,123143,122804],{"class":73988},[62,123145,122807],{"class":149},[62,123147,6973],{"class":68},[62,123149,122812],{"class":73988},[62,123151,122807],{"class":149},[62,123153,6973],{"class":68},[62,123155,122819],{"class":73985},[62,123157,122807],{"class":149},[62,123159,6973],{"class":68},[62,123161,2109],{"class":73988},[62,123163,113672],{"class":149},[62,123165,74041],{"class":68},[62,123167,122832],{"class":149},[62,123169,6973],{"class":68},[62,123171,2712],{"class":73988},[62,123173,2755],{"class":149},[62,123175,122841],{"class":68},[62,123177,2712],{"class":73988},[62,123179,122846],{"class":68},[62,123181,2755],{"class":149},[62,123183,122841],{"class":68},[62,123185,6936],{"class":1675},[62,123187,976],{"class":72},[62,123189,122857],{"class":1675},[62,123191,1133],{"class":72},[62,123193,123194],{"class":64,"line":231},[62,123195,50124],{"class":72},[62,123197,123198],{"class":64,"line":236},[62,123199,32848],{"class":72},[62,123201,123202,123204],{"class":64,"line":242},[62,123203,106219],{"class":122},[62,123205,206],{"class":72},[62,123207,123208,123210,123213,123215,123217,123219,123221],{"class":64,"line":247},[62,123209,36857],{"class":68},[62,123211,123212],{"class":149}," cookieValue",[62,123214,2556],{"class":68},[62,123216,9961],{"class":149},[62,123218,2755],{"class":72},[62,123220,122888],{"class":122},[62,123222,822],{"class":72},[62,123224,123225,123227,123230,123232,123235,123237,123239],{"class":64,"line":252},[62,123226,39124],{"class":149},[62,123228,123229],{"class":72},".display ",[62,123231,146],{"class":68},[62,123233,123234],{"class":72}," cookieValue ",[62,123236,21072],{"class":68},[62,123238,9961],{"class":149},[62,123240,123241],{"class":72},".language;\n",[62,123243,123244],{"class":64,"line":257},[62,123245,3731],{"class":72},[62,123247,123248],{"class":64,"line":271},[62,123249,379],{"class":72},[62,123251,123252,123254,123256],{"class":64,"line":281},[62,123253,1818],{"class":72},[62,123255,15846],{"class":1780},[62,123257,1784],{"class":72},[26,123259,1499],{"id":1498},[22,123261,123262],{},"I have been writing code a long time so usually when I come across a problem it's one that I have solved over and over again. The idea of static site generators combining the client and server presents problems that I haven't run into before and that is exciting. If you have a better solution for this I would love to hear about it. I hope someone else got something from this article and as always friends....",[22,123264,36004,123265,82545],{},[36006,123266],{},[1527,123268,123269],{},"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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .s4-Ip, html code.shiki .s4-Ip{--shiki-default:#6A737D;--shiki-dark:#6A737D;--shiki-sepia:#6A737D}html pre.shiki code .spoOn, html code.shiki .spoOn{--shiki-default:#E36209;--shiki-dark:#FFAB70;--shiki-sepia:#E36209}html pre.shiki code .suaqH, html code.shiki .suaqH{--shiki-default:#22863A;--shiki-dark:#85E89D;--shiki-sepia:#22863A}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}html pre.shiki code .sLcMm, html code.shiki .sLcMm{--shiki-default:#032F62;--shiki-dark:#DBEDFF;--shiki-sepia:#032F62}html pre.shiki code .sIS5D, html code.shiki .sIS5D{--shiki-default:#22863A;--shiki-default-font-weight:bold;--shiki-dark:#85E89D;--shiki-dark-font-weight:bold;--shiki-sepia:#22863A;--shiki-sepia-font-weight:bold}html pre.shiki code .shOWo, html code.shiki .shOWo{--shiki-default:#B31D28;--shiki-default-font-style:italic;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic;--shiki-sepia:#B31D28;--shiki-sepia-font-style:italic}",{"title":57,"searchDepth":76,"depth":76,"links":123271},[123272,123273,123279,123280],{"id":121529,"depth":76,"text":121530},{"id":121678,"depth":76,"text":121679,"children":123274},[123275,123276,123277,123278],{"id":121693,"depth":82,"text":121694},{"id":121828,"depth":82,"text":121829},{"id":122051,"depth":82,"text":122052},{"id":122149,"depth":82,"text":122150},{"id":122909,"depth":76,"text":122910},{"id":1498,"depth":76,"text":1499},{"_id":123282,"path":123283,"title":123284,"description":123285,"meta":123286,"body":123291},"content/blog/2019/06/05/triggering-events-router-vue.md","/blog/2019/06/05/triggering-events-router-vue","Triggering events from Vue Router views","In this article I will show you how to trigger events from views using the Router View component.",{"slug":123287,"date":123288,"published":13,"tags":123289,"author":17,"cover":123290,"excerpt":-1},"triggering-events-router-vue","2019-06-05T20:13:17.445Z",[105335],"./triggering-events-router-vue-cover.png",{"type":19,"value":123292,"toc":124465},[123293,123296,123299,123302,123312,123315,123321,123329,123333,123341,123345,123348,123353,123364,123515,123518,123631,123638,123643,123646,123650,123659,123826,123839,123855,124145,124157,124426,124429,124437,124443,124445,124458,124462],[22,123294,123295],{},"Last week I was working on tightening up our documentation on Vue Router and I was presented with an interesting challenge. In a short period of time our students are able to build some really cool applications using Vue. We teach them how components communicate with each other and introduce them to Vue Router.",[22,123297,123298],{},"The problem comes into play when you want to start triggering events from your views or even components within those views. In the real world you would start to see this as an obvious pain and look to see what others are using to solve this problem.",[22,123300,123301],{},"When this problem shows up you would probably reach for something like Vuex. In our situation we just don't have the time to include that in our curriculum so when it comes time for them to build their final capstone projects they need a way to trigger events in their views.",[22,123303,123304,123305,123308,123309,123311],{},"I put together a quick demo and had an a ha moment. When we want to trigger an event in any component we just emit one and listen for it on the parent component. In this case the view is just a component and the parent component is ",[59,123306,123307],{},"\u003Crouter-view>",". It's easy to forget but ",[59,123310,123307],{}," is just a component.",[22,123313,123314],{},"I was pretty excited about this so I tweeted this out:",[22,123316,123317],{},[677,123318,123319],{"href":123319,"rel":123320},"https://twitter.com/therealdanvega/status/1134550365049937920",[681],[22,123322,123323,123328],{},[677,123324,123327],{"href":123325,"rel":123326},"https://twitter.com/DamianDulisz",[681],"Damian Dulisz"," was quick to respond and remind me that because it was a component it can also accept props. That was the exact discussion we had at work and I was happy to hear we weren't alone in thinking that.",[26,123330,123332],{"id":123331},"triggering-events-from-vue-router-demo","Triggering events from Vue Router Demo",[22,123334,123335,123336,2755],{},"I'm not able to use the exact project that triggered this article but I was able to put together a simple example that should help explain the solution. If you want to skip ahead you can jump into the final solution on ",[677,123337,123340],{"href":123338,"rel":123339},"https://codesandbox.io/s/trigger-event-views-p9oyt?fontsize=14",[681],"CodeSandbox",[636,123342,123344],{"id":123343},"demo-introduction","Demo Introduction",[22,123346,123347],{},"In our sample application we have a footer component that is constant throughout our application and it contains a status message.",[22,123349,123350],{},[653,123351],{"alt":57,"src":123352},"/images/blog/2019/06/05/2019-06-05_12-13-15-1bacd64d-69d7-48c7-a7ac-b90cc67baea5.png",[22,123354,88497,123355,123357,123358,123360,123361,2755],{},[59,123356,105973],{}," there is a ",[59,123359,123307],{}," component that will display each of our views and below that you will include a component ",[59,123362,123363],{},"TheFooter.vue",[52,123365,123367],{"className":15773,"code":123366,"language":15775,"meta":57,"style":57},"\u003Ctemplate>\n \u003Cdiv id=\"app\">\n \u003Cul class=\"nav\">\n \u003Crouter-link to=\"/\">Home\u003C/router-link\n >|\n \u003Crouter-link to=\"/admin\">Admin\u003C/router-link>\n \u003C/ul>\n \u003Crouter-view @updateStatus=\"updateStatus\" class=\"content\" />\n \u003Cthe-footer :status=\"status\" />\n \u003C/div>\n\u003C/template>\n",[59,123368,123369,123377,123391,123406,123425,123430,123450,123458,123482,123499,123507],{"__ignoreMap":57},[62,123370,123371,123373,123375],{"class":64,"line":65},[62,123372,760],{"class":72},[62,123374,105991],{"class":1780},[62,123376,1784],{"class":72},[62,123378,123379,123381,123383,123385,123387,123389],{"class":64,"line":76},[62,123380,33056],{"class":72},[62,123382,15944],{"class":1780},[62,123384,20831],{"class":122},[62,123386,146],{"class":72},[62,123388,49987],{"class":1675},[62,123390,1784],{"class":72},[62,123392,123393,123395,123397,123399,123401,123404],{"class":64,"line":82},[62,123394,1789],{"class":72},[62,123396,915],{"class":1780},[62,123398,119],{"class":122},[62,123400,146],{"class":72},[62,123402,123403],{"class":1675},"\"nav\"",[62,123405,1784],{"class":72},[62,123407,123408,123410,123413,123415,123417,123419,123422],{"class":64,"line":89},[62,123409,92881],{"class":72},[62,123411,123412],{"class":1780},"router-link",[62,123414,57317],{"class":122},[62,123416,146],{"class":72},[62,123418,15635],{"class":1675},[62,123420,123421],{"class":72},">Home\u003C/",[62,123423,123424],{"class":1780},"router-link\n",[62,123426,123427],{"class":64,"line":95},[62,123428,123429],{"class":72}," >|\n",[62,123431,123432,123434,123436,123438,123440,123443,123446,123448],{"class":64,"line":101},[62,123433,92881],{"class":72},[62,123435,123412],{"class":1780},[62,123437,57317],{"class":122},[62,123439,146],{"class":72},[62,123441,123442],{"class":1675},"\"/admin\"",[62,123444,123445],{"class":72},">Admin\u003C/",[62,123447,123412],{"class":1780},[62,123449,1784],{"class":72},[62,123451,123452,123454,123456],{"class":64,"line":107},[62,123453,1982],{"class":72},[62,123455,915],{"class":1780},[62,123457,1784],{"class":72},[62,123459,123460,123462,123465,123468,123470,123473,123475,123477,123480],{"class":64,"line":113},[62,123461,1789],{"class":72},[62,123463,123464],{"class":1780},"router-view",[62,123466,123467],{"class":122}," @updateStatus",[62,123469,146],{"class":72},[62,123471,123472],{"class":1675},"\"updateStatus\"",[62,123474,119],{"class":122},[62,123476,146],{"class":72},[62,123478,123479],{"class":1675},"\"content\"",[62,123481,67133],{"class":72},[62,123483,123484,123486,123489,123492,123494,123497],{"class":64,"line":129},[62,123485,1789],{"class":72},[62,123487,123488],{"class":1780},"the-footer",[62,123490,123491],{"class":122}," :status",[62,123493,146],{"class":72},[62,123495,123496],{"class":1675},"\"status\"",[62,123498,67133],{"class":72},[62,123500,123501,123503,123505],{"class":64,"line":134},[62,123502,33187],{"class":72},[62,123504,15944],{"class":1780},[62,123506,1784],{"class":72},[62,123508,123509,123511,123513],{"class":64,"line":156},[62,123510,1818],{"class":72},[62,123512,105991],{"class":1780},[62,123514,1784],{"class":72},[22,123516,123517],{},"The code for the footer component is pretty minimal.",[52,123519,123521],{"className":15773,"code":123520,"language":15775,"meta":57,"style":57},"\u003Ctemplate>\n \u003Cdiv class=\"footer\">\n \u003Cp>{{ status }}\u003C/p>\n \u003C/div>\n\u003C/template>\n\n\u003Cscript>\n export default {\n name: \"the-footer\",\n props: {\n status: String\n }\n };\n\u003C/script>\n",[59,123522,123523,123531,123546,123559,123567,123575,123579,123587,123595,123605,123610,123615,123619,123623],{"__ignoreMap":57},[62,123524,123525,123527,123529],{"class":64,"line":65},[62,123526,760],{"class":72},[62,123528,105991],{"class":1780},[62,123530,1784],{"class":72},[62,123532,123533,123535,123537,123539,123541,123544],{"class":64,"line":76},[62,123534,33056],{"class":72},[62,123536,15944],{"class":1780},[62,123538,119],{"class":122},[62,123540,146],{"class":72},[62,123542,123543],{"class":1675},"\"footer\"",[62,123545,1784],{"class":72},[62,123547,123548,123550,123552,123555,123557],{"class":64,"line":82},[62,123549,1789],{"class":72},[62,123551,22],{"class":1780},[62,123553,123554],{"class":72},">{{ status }}\u003C/",[62,123556,22],{"class":1780},[62,123558,1784],{"class":72},[62,123560,123561,123563,123565],{"class":64,"line":89},[62,123562,33187],{"class":72},[62,123564,15944],{"class":1780},[62,123566,1784],{"class":72},[62,123568,123569,123571,123573],{"class":64,"line":95},[62,123570,1818],{"class":72},[62,123572,105991],{"class":1780},[62,123574,1784],{"class":72},[62,123576,123577],{"class":64,"line":101},[62,123578,79],{"emptyLinePlaceholder":13},[62,123580,123581,123583,123585],{"class":64,"line":107},[62,123582,760],{"class":72},[62,123584,15846],{"class":1780},[62,123586,1784],{"class":72},[62,123588,123589,123591,123593],{"class":64,"line":113},[62,123590,114675],{"class":68},[62,123592,106045],{"class":68},[62,123594,126],{"class":72},[62,123596,123597,123600,123603],{"class":64,"line":129},[62,123598,123599],{"class":72}," name: ",[62,123601,123602],{"class":1675},"\"the-footer\"",[62,123604,3338],{"class":72},[62,123606,123607],{"class":64,"line":134},[62,123608,123609],{"class":72}," props: {\n",[62,123611,123612],{"class":64,"line":156},[62,123613,123614],{"class":72}," status: String\n",[62,123616,123617],{"class":64,"line":161},[62,123618,223],{"class":72},[62,123620,123621],{"class":64,"line":167},[62,123622,82135],{"class":72},[62,123624,123625,123627,123629],{"class":64,"line":173},[62,123626,1818],{"class":72},[62,123628,15846],{"class":1780},[62,123630,1784],{"class":72},[22,123632,123633,123634,123637],{},"What we want is the ability to change that status message from a component. The catch here is that the component (",[59,123635,123636],{},"StatusUpdate.vue",") is being used in another view called admin.",[22,123639,123640],{},[653,123641],{"alt":57,"src":123642},"/images/blog/2019/06/05/2019-06-05_12-15-38-7d19d6b2-654c-4187-8d61-fa1a6b4f88aa.png",[22,123644,123645],{},"So to make this work you will need to trigger the event from the admin page up to the main App component and then pass that into the footer as a prop.",[636,123647,123649],{"id":123648},"trigger-events-in-router-views","Trigger events in router views",[22,123651,123652,123653,123655,123656],{},"If you have worked with events before you know that you can emit an event so that any parent components are able to listen for it. To update the status you will type in the new status in the input field and click the button. When the button is clicked in our ",[59,123654,123636],{}," component it will emit an event called ",[59,123657,123658],{},"updateStatus()",[52,123660,123662],{"className":15773,"code":123661,"language":15775,"meta":57,"style":57},"\u003Ctemplate>\n \u003Cdiv class=\"update-status\">\n \u003Cp>Please enter a message below to update the status message.\u003C/p>\n \u003Cinput type=\"text\" v-model=\"status\" />\n \u003Cbutton @click=\"$emit('updateStatus',status)\">Update Status\u003C/button>\n \u003C/div>\n\u003C/template>\n\n\u003Cscript>\n export default {\n name: \"status-message\",\n data() {\n return {\n status: \"\"\n };\n }\n };\n\u003C/script>\n",[59,123663,123664,123672,123687,123700,123721,123741,123749,123757,123761,123769,123777,123786,123792,123798,123806,123810,123814,123818],{"__ignoreMap":57},[62,123665,123666,123668,123670],{"class":64,"line":65},[62,123667,760],{"class":72},[62,123669,105991],{"class":1780},[62,123671,1784],{"class":72},[62,123673,123674,123676,123678,123680,123682,123685],{"class":64,"line":76},[62,123675,33056],{"class":72},[62,123677,15944],{"class":1780},[62,123679,119],{"class":122},[62,123681,146],{"class":72},[62,123683,123684],{"class":1675},"\"update-status\"",[62,123686,1784],{"class":72},[62,123688,123689,123691,123693,123696,123698],{"class":64,"line":82},[62,123690,1789],{"class":72},[62,123692,22],{"class":1780},[62,123694,123695],{"class":72},">Please enter a message below to update the status message.\u003C/",[62,123697,22],{"class":1780},[62,123699,1784],{"class":72},[62,123701,123702,123704,123706,123708,123710,123712,123715,123717,123719],{"class":64,"line":89},[62,123703,1789],{"class":72},[62,123705,8890],{"class":1780},[62,123707,16099],{"class":122},[62,123709,146],{"class":72},[62,123711,16152],{"class":1675},[62,123713,123714],{"class":122}," v-model",[62,123716,146],{"class":72},[62,123718,123496],{"class":1675},[62,123720,67133],{"class":72},[62,123722,123723,123725,123727,123729,123731,123734,123737,123739],{"class":64,"line":95},[62,123724,1789],{"class":72},[62,123726,16275],{"class":1780},[62,123728,117145],{"class":122},[62,123730,146],{"class":72},[62,123732,123733],{"class":1675},"\"$emit('updateStatus',status)\"",[62,123735,123736],{"class":72},">Update Status\u003C/",[62,123738,16275],{"class":1780},[62,123740,1784],{"class":72},[62,123742,123743,123745,123747],{"class":64,"line":101},[62,123744,33187],{"class":72},[62,123746,15944],{"class":1780},[62,123748,1784],{"class":72},[62,123750,123751,123753,123755],{"class":64,"line":107},[62,123752,1818],{"class":72},[62,123754,105991],{"class":1780},[62,123756,1784],{"class":72},[62,123758,123759],{"class":64,"line":113},[62,123760,79],{"emptyLinePlaceholder":13},[62,123762,123763,123765,123767],{"class":64,"line":129},[62,123764,760],{"class":72},[62,123766,15846],{"class":1780},[62,123768,1784],{"class":72},[62,123770,123771,123773,123775],{"class":64,"line":134},[62,123772,114675],{"class":68},[62,123774,106045],{"class":68},[62,123776,126],{"class":72},[62,123778,123779,123781,123784],{"class":64,"line":156},[62,123780,123599],{"class":72},[62,123782,123783],{"class":1675},"\"status-message\"",[62,123785,3338],{"class":72},[62,123787,123788,123790],{"class":64,"line":161},[62,123789,114684],{"class":122},[62,123791,206],{"class":72},[62,123793,123794,123796],{"class":64,"line":167},[62,123795,28884],{"class":68},[62,123797,126],{"class":72},[62,123799,123800,123803],{"class":64,"line":173},[62,123801,123802],{"class":72}," status: ",[62,123804,123805],{"class":1675},"\"\"\n",[62,123807,123808],{"class":64,"line":179},[62,123809,59009],{"class":72},[62,123811,123812],{"class":64,"line":185},[62,123813,223],{"class":72},[62,123815,123816],{"class":64,"line":191},[62,123817,82135],{"class":72},[62,123819,123820,123822,123824],{"class":64,"line":209},[62,123821,1818],{"class":72},[62,123823,15846],{"class":1780},[62,123825,1784],{"class":72},[22,123827,123828,123829,94571,123832,123835,123836,123838],{},"The parent component is a view in ",[59,123830,123831],{},"src/views/",[59,123833,123834],{},"Admin.vue",". On the admin page you will import the status message component and display it on screen. When you emit an event it's only available in the components parent so to listen for it in ",[59,123837,105973],{}," you will need to bubble it up from Admin to App.",[22,123840,123841,123842,123845,123846,123849,123850,123852,123853,2755],{},"To make this work add an event listener to listen for the ",[59,123843,123844],{},"updateStatus"," event that will be emitted from the ",[59,123847,123848],{},"UpdateStatus.vue"," component. This will turn around and emit the ",[59,123851,123844],{}," event to the parent component of Admin, which is our ",[59,123854,123307],{},[52,123856,123858],{"className":15773,"code":123857,"language":15775,"meta":57,"style":57},"\u003Ctemplate>\n \u003Cdiv class=\"admin\">\n \u003Ch1>Admin Page\u003C/h1>\n \u003Cp>\n Siphon white macchiato arabica frappuccino breve as, affogato that acerbic\n dark sweet. Qui brewed single shot white bar plunger pot single shot\n cinnamon. Lungo brewed turkish white aged skinny french press. Aged french\n press white medium, brewed and ut seasonal single origin. Single origin,\n aroma, robusta trifecta cup frappuccino cup skinny.\n \u003C/p>\n \u003Cp>\n Arabica and percolator blue mountain to go mug frappuccino white medium\n brewed single shot. Skinny redeye aromatic, java flavour mazagran blue\n mountain robusta milk. Trifecta single shot strong single origin caffeine\n cream cinnamon cream extra. Turkish, caramelization so, cultivar brewed,\n cream mocha plunger pot white robusta saucer. Caffeine dark, brewed\n carajillo pumpkin spice mocha caffeine.\n \u003C/p>\n\n \u003Cstatus-message @updateStatus=\"updateStatus\" />\n \u003C/div>\n\u003C/template>\n\n\u003Cscript>\n import StatusMessage from \"@/components/StatusMessage.vue\";\n\n export default {\n name: \"admin\",\n components: {\n StatusMessage\n },\n data() {\n return {};\n },\n methods: {\n updateStatus(status) {\n this.$emit(\"updateStatus\", status);\n }\n }\n };\n\u003C/script>\n",[59,123859,123860,123868,123882,123895,123903,123908,123913,123918,123923,123928,123936,123944,123949,123954,123959,123964,123969,123974,123982,123986,124001,124009,124017,124021,124029,124043,124047,124055,124063,124068,124073,124077,124083,124089,124093,124098,124109,124125,124129,124133,124137],{"__ignoreMap":57},[62,123861,123862,123864,123866],{"class":64,"line":65},[62,123863,760],{"class":72},[62,123865,105991],{"class":1780},[62,123867,1784],{"class":72},[62,123869,123870,123872,123874,123876,123878,123880],{"class":64,"line":76},[62,123871,33056],{"class":72},[62,123873,15944],{"class":1780},[62,123875,119],{"class":122},[62,123877,146],{"class":72},[62,123879,15689],{"class":1675},[62,123881,1784],{"class":72},[62,123883,123884,123886,123888,123891,123893],{"class":64,"line":82},[62,123885,1789],{"class":72},[62,123887,4168],{"class":1780},[62,123889,123890],{"class":72},">Admin Page\u003C/",[62,123892,4168],{"class":1780},[62,123894,1784],{"class":72},[62,123896,123897,123899,123901],{"class":64,"line":89},[62,123898,1789],{"class":72},[62,123900,22],{"class":1780},[62,123902,1784],{"class":72},[62,123904,123905],{"class":64,"line":95},[62,123906,123907],{"class":72}," Siphon white macchiato arabica frappuccino breve as, affogato that acerbic\n",[62,123909,123910],{"class":64,"line":101},[62,123911,123912],{"class":72}," dark sweet. Qui brewed single shot white bar plunger pot single shot\n",[62,123914,123915],{"class":64,"line":107},[62,123916,123917],{"class":72}," cinnamon. Lungo brewed turkish white aged skinny french press. Aged french\n",[62,123919,123920],{"class":64,"line":113},[62,123921,123922],{"class":72}," press white medium, brewed and ut seasonal single origin. Single origin,\n",[62,123924,123925],{"class":64,"line":129},[62,123926,123927],{"class":72}," aroma, robusta trifecta cup frappuccino cup skinny.\n",[62,123929,123930,123932,123934],{"class":64,"line":134},[62,123931,1982],{"class":72},[62,123933,22],{"class":1780},[62,123935,1784],{"class":72},[62,123937,123938,123940,123942],{"class":64,"line":156},[62,123939,1789],{"class":72},[62,123941,22],{"class":1780},[62,123943,1784],{"class":72},[62,123945,123946],{"class":64,"line":161},[62,123947,123948],{"class":72}," Arabica and percolator blue mountain to go mug frappuccino white medium\n",[62,123950,123951],{"class":64,"line":167},[62,123952,123953],{"class":72}," brewed single shot. Skinny redeye aromatic, java flavour mazagran blue\n",[62,123955,123956],{"class":64,"line":173},[62,123957,123958],{"class":72}," mountain robusta milk. Trifecta single shot strong single origin caffeine\n",[62,123960,123961],{"class":64,"line":179},[62,123962,123963],{"class":72}," cream cinnamon cream extra. Turkish, caramelization so, cultivar brewed,\n",[62,123965,123966],{"class":64,"line":185},[62,123967,123968],{"class":72}," cream mocha plunger pot white robusta saucer. Caffeine dark, brewed\n",[62,123970,123971],{"class":64,"line":191},[62,123972,123973],{"class":72}," carajillo pumpkin spice mocha caffeine.\n",[62,123975,123976,123978,123980],{"class":64,"line":209},[62,123977,1982],{"class":72},[62,123979,22],{"class":1780},[62,123981,1784],{"class":72},[62,123983,123984],{"class":64,"line":220},[62,123985,79],{"emptyLinePlaceholder":13},[62,123987,123988,123990,123993,123995,123997,123999],{"class":64,"line":226},[62,123989,1789],{"class":72},[62,123991,123992],{"class":1780},"status-message",[62,123994,123467],{"class":122},[62,123996,146],{"class":72},[62,123998,123472],{"class":1675},[62,124000,67133],{"class":72},[62,124002,124003,124005,124007],{"class":64,"line":231},[62,124004,33187],{"class":72},[62,124006,15944],{"class":1780},[62,124008,1784],{"class":72},[62,124010,124011,124013,124015],{"class":64,"line":236},[62,124012,1818],{"class":72},[62,124014,105991],{"class":1780},[62,124016,1784],{"class":72},[62,124018,124019],{"class":64,"line":242},[62,124020,79],{"emptyLinePlaceholder":13},[62,124022,124023,124025,124027],{"class":64,"line":247},[62,124024,760],{"class":72},[62,124026,15846],{"class":1780},[62,124028,1784],{"class":72},[62,124030,124031,124033,124036,124038,124041],{"class":64,"line":252},[62,124032,115046],{"class":68},[62,124034,124035],{"class":72}," StatusMessage ",[62,124037,3507],{"class":68},[62,124039,124040],{"class":1675}," \"@/components/StatusMessage.vue\"",[62,124042,153],{"class":72},[62,124044,124045],{"class":64,"line":257},[62,124046,79],{"emptyLinePlaceholder":13},[62,124048,124049,124051,124053],{"class":64,"line":271},[62,124050,114675],{"class":68},[62,124052,106045],{"class":68},[62,124054,126],{"class":72},[62,124056,124057,124059,124061],{"class":64,"line":281},[62,124058,123599],{"class":72},[62,124060,15689],{"class":1675},[62,124062,3338],{"class":72},[62,124064,124065],{"class":64,"line":286},[62,124066,124067],{"class":72}," components: {\n",[62,124069,124070],{"class":64,"line":291},[62,124071,124072],{"class":72}," StatusMessage\n",[62,124074,124075],{"class":64,"line":296},[62,124076,50124],{"class":72},[62,124078,124079,124081],{"class":64,"line":302},[62,124080,114684],{"class":122},[62,124082,206],{"class":72},[62,124084,124085,124087],{"class":64,"line":308},[62,124086,28884],{"class":68},[62,124088,59540],{"class":72},[62,124090,124091],{"class":64,"line":314},[62,124092,50124],{"class":72},[62,124094,124095],{"class":64,"line":320},[62,124096,124097],{"class":72}," methods: {\n",[62,124099,124100,124103,124105,124107],{"class":64,"line":326},[62,124101,124102],{"class":122}," updateStatus",[62,124104,2109],{"class":72},[62,124106,55012],{"class":889},[62,124108,768],{"class":72},[62,124110,124111,124113,124115,124118,124120,124122],{"class":64,"line":338},[62,124112,2405],{"class":149},[62,124114,2755],{"class":72},[62,124116,124117],{"class":122},"$emit",[62,124119,2109],{"class":72},[62,124121,123472],{"class":1675},[62,124123,124124],{"class":72},", status);\n",[62,124126,124127],{"class":64,"line":343},[62,124128,29042],{"class":72},[62,124130,124131],{"class":64,"line":357},[62,124132,223],{"class":72},[62,124134,124135],{"class":64,"line":366},[62,124136,82135],{"class":72},[62,124138,124139,124141,124143],{"class":64,"line":371},[62,124140,1818],{"class":72},[62,124142,15846],{"class":1780},[62,124144,1784],{"class":72},[22,124146,124147,124148,124150,124151,124153,124154,124156],{},"Now in ",[59,124149,105973],{}," you can listen for ",[59,124152,123844],{}," event on the ",[59,124155,123307],{}," component. As I mentioned before it's just a component so this will work. You can now update the status and it will get passed into your footer using props.",[52,124158,124160],{"className":15773,"code":124159,"language":15775,"meta":57,"style":57},"\u003Ctemplate>\n \u003Cdiv id=\"app\">\n \u003Cul class=\"nav\">\n \u003Crouter-link to=\"/\">Home\u003C/router-link\n >|\n \u003Crouter-link to=\"/admin\">Admin\u003C/router-link>\n \u003C/ul>\n \u003Crouter-view @updateStatus=\"updateStatus\" class=\"content\" />\n \u003Cthe-footer :status=\"status\" />\n \u003C/div>\n\u003C/template>\n\n\u003Cscript>\n import TheFooter from \"./components/TheFooter\";\n\n export default {\n name: \"App\",\n components: {\n TheFooter\n },\n data() {\n return {\n status: \"This is the default status message\"\n };\n },\n methods: {\n updateStatus(status) {\n this.status = status;\n }\n }\n };\n\u003C/script>\n",[59,124161,124162,124170,124184,124198,124214,124218,124236,124244,124264,124278,124286,124294,124298,124306,124320,124324,124332,124340,124344,124349,124353,124359,124365,124372,124376,124380,124384,124394,124406,124410,124414,124418],{"__ignoreMap":57},[62,124163,124164,124166,124168],{"class":64,"line":65},[62,124165,760],{"class":72},[62,124167,105991],{"class":1780},[62,124169,1784],{"class":72},[62,124171,124172,124174,124176,124178,124180,124182],{"class":64,"line":76},[62,124173,33056],{"class":72},[62,124175,15944],{"class":1780},[62,124177,20831],{"class":122},[62,124179,146],{"class":72},[62,124181,49987],{"class":1675},[62,124183,1784],{"class":72},[62,124185,124186,124188,124190,124192,124194,124196],{"class":64,"line":82},[62,124187,1789],{"class":72},[62,124189,915],{"class":1780},[62,124191,119],{"class":122},[62,124193,146],{"class":72},[62,124195,123403],{"class":1675},[62,124197,1784],{"class":72},[62,124199,124200,124202,124204,124206,124208,124210,124212],{"class":64,"line":89},[62,124201,92881],{"class":72},[62,124203,123412],{"class":1780},[62,124205,57317],{"class":122},[62,124207,146],{"class":72},[62,124209,15635],{"class":1675},[62,124211,123421],{"class":72},[62,124213,123424],{"class":1780},[62,124215,124216],{"class":64,"line":95},[62,124217,123429],{"class":72},[62,124219,124220,124222,124224,124226,124228,124230,124232,124234],{"class":64,"line":101},[62,124221,92881],{"class":72},[62,124223,123412],{"class":1780},[62,124225,57317],{"class":122},[62,124227,146],{"class":72},[62,124229,123442],{"class":1675},[62,124231,123445],{"class":72},[62,124233,123412],{"class":1780},[62,124235,1784],{"class":72},[62,124237,124238,124240,124242],{"class":64,"line":107},[62,124239,1982],{"class":72},[62,124241,915],{"class":1780},[62,124243,1784],{"class":72},[62,124245,124246,124248,124250,124252,124254,124256,124258,124260,124262],{"class":64,"line":113},[62,124247,1789],{"class":72},[62,124249,123464],{"class":1780},[62,124251,123467],{"class":122},[62,124253,146],{"class":72},[62,124255,123472],{"class":1675},[62,124257,119],{"class":122},[62,124259,146],{"class":72},[62,124261,123479],{"class":1675},[62,124263,67133],{"class":72},[62,124265,124266,124268,124270,124272,124274,124276],{"class":64,"line":129},[62,124267,1789],{"class":72},[62,124269,123488],{"class":1780},[62,124271,123491],{"class":122},[62,124273,146],{"class":72},[62,124275,123496],{"class":1675},[62,124277,67133],{"class":72},[62,124279,124280,124282,124284],{"class":64,"line":134},[62,124281,33187],{"class":72},[62,124283,15944],{"class":1780},[62,124285,1784],{"class":72},[62,124287,124288,124290,124292],{"class":64,"line":156},[62,124289,1818],{"class":72},[62,124291,105991],{"class":1780},[62,124293,1784],{"class":72},[62,124295,124296],{"class":64,"line":161},[62,124297,79],{"emptyLinePlaceholder":13},[62,124299,124300,124302,124304],{"class":64,"line":167},[62,124301,760],{"class":72},[62,124303,15846],{"class":1780},[62,124305,1784],{"class":72},[62,124307,124308,124310,124313,124315,124318],{"class":64,"line":173},[62,124309,115046],{"class":68},[62,124311,124312],{"class":72}," TheFooter ",[62,124314,3507],{"class":68},[62,124316,124317],{"class":1675}," \"./components/TheFooter\"",[62,124319,153],{"class":72},[62,124321,124322],{"class":64,"line":179},[62,124323,79],{"emptyLinePlaceholder":13},[62,124325,124326,124328,124330],{"class":64,"line":185},[62,124327,114675],{"class":68},[62,124329,106045],{"class":68},[62,124331,126],{"class":72},[62,124333,124334,124336,124338],{"class":64,"line":191},[62,124335,123599],{"class":72},[62,124337,107115],{"class":1675},[62,124339,3338],{"class":72},[62,124341,124342],{"class":64,"line":209},[62,124343,124067],{"class":72},[62,124345,124346],{"class":64,"line":220},[62,124347,124348],{"class":72}," TheFooter\n",[62,124350,124351],{"class":64,"line":226},[62,124352,50124],{"class":72},[62,124354,124355,124357],{"class":64,"line":231},[62,124356,114684],{"class":122},[62,124358,206],{"class":72},[62,124360,124361,124363],{"class":64,"line":236},[62,124362,28884],{"class":68},[62,124364,126],{"class":72},[62,124366,124367,124369],{"class":64,"line":242},[62,124368,123802],{"class":72},[62,124370,124371],{"class":1675},"\"This is the default status message\"\n",[62,124373,124374],{"class":64,"line":247},[62,124375,59009],{"class":72},[62,124377,124378],{"class":64,"line":252},[62,124379,50124],{"class":72},[62,124381,124382],{"class":64,"line":257},[62,124383,124097],{"class":72},[62,124385,124386,124388,124390,124392],{"class":64,"line":271},[62,124387,124102],{"class":122},[62,124389,2109],{"class":72},[62,124391,55012],{"class":889},[62,124393,768],{"class":72},[62,124395,124396,124398,124401,124403],{"class":64,"line":281},[62,124397,2405],{"class":149},[62,124399,124400],{"class":72},".status ",[62,124402,146],{"class":68},[62,124404,124405],{"class":72}," status;\n",[62,124407,124408],{"class":64,"line":286},[62,124409,29042],{"class":72},[62,124411,124412],{"class":64,"line":291},[62,124413,223],{"class":72},[62,124415,124416],{"class":64,"line":296},[62,124417,82135],{"class":72},[62,124419,124420,124422,124424],{"class":64,"line":302},[62,124421,1818],{"class":72},[62,124423,15846],{"class":1780},[62,124425,1784],{"class":72},[26,124427,123340],{"id":124428},"codesandbox",[22,124430,124431,124432,124436],{},"If you're interested in checking out the final code for this project you can ",[677,124433,124435],{"href":123338,"rel":124434},[681],"click here"," or use the CodeSandbox embed below.",[22,124438,124439],{},[677,124440,124441],{"href":124441,"rel":124442},"https://codesandbox.io/embed/trigger-event-views-p9oyt?fontsize=14&view=editor",[681],[26,124444,1499],{"id":1498},[22,124446,124447,124448,124451,124452,19931,124455,124457],{},"I think the important thing to remember here is that your components inside of the ",[59,124449,124450],{},"/views"," folder along with ",[59,124453,124454],{},"\u003Crouter-link>",[59,124456,123307],{}," are all just Single File Components at the end of the day. It's because of this that they can trigger and listen for events or act like any other component would. I hope this little tip helped make your day an easier one and as always....",[22,124459,36004,124460,82545],{},[36006,124461],{},[1527,124463,124464],{},"html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .suaqH, html code.shiki .suaqH{--shiki-default:#22863A;--shiki-dark:#85E89D;--shiki-sepia:#22863A}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .spoOn, html code.shiki .spoOn{--shiki-default:#E36209;--shiki-dark:#FFAB70;--shiki-sepia:#E36209}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}",{"title":57,"searchDepth":76,"depth":76,"links":124466},[124467,124471,124472],{"id":123331,"depth":76,"text":123332,"children":124468},[124469,124470],{"id":123343,"depth":82,"text":123344},{"id":123648,"depth":82,"text":123649},{"id":124428,"depth":76,"text":123340},{"id":1498,"depth":76,"text":1499},{"_id":124474,"path":124475,"title":124476,"description":124477,"meta":124478,"body":124483},"content/blog/2019/05/31/escape-backtick-markdown.md","/blog/2019/05/31/escape-backtick-markdown","How to escape a backtick within a code block in Markdown","In this article I will show you how to escape the triple backtick in a markdown code block so that you can display it in a post or a Github comment. ",{"slug":124479,"date":124480,"published":13,"tags":124481,"author":17,"cover":124482,"excerpt":-1},"escape-backtick-markdown","2019-05-31T18:32:41.710Z",[29692,119709],"./escape-backtick-markdown-cover.png",{"type":19,"value":124484,"toc":124743},[124485,124488,124492,124495,124498,124564,124567,124572,124575,124579,124582,124585,124613,124616,124633,124636,124682,124685,124732,124734,124737,124741],[22,124486,124487],{},"This is going to be a pretty short tutorial but I was so excited that I had to share it with all of you. It isn't often but there are times where I need to share a markdown code block in a blog post or in a Github comment and I never know how to do it. In this article, I am going to show you how to escape a backtick ` within a code block in Markdown",[26,124489,124491],{"id":124490},"fenced-code-blocks","Fenced Code Blocks",[22,124493,124494],{},"If you have made it this far I am going to assume you're pretty familiar with Markdown. You can create fenced code blocks by placing triple backtick ``` before and after the code block. Whatever Markdown processor you're using will have a plugin that takes that code and the language you defined and add some highlighting to it.",[22,124496,124497],{},"In the following example I am using VuePress and in my markdown template I want to display text and some code to a user.",[52,124499,124501],{"className":29690,"code":124500,"language":29692,"meta":57,"style":57},"## Inside an Existing Project\n\nIf you have an existing project and would like to keep documentation inside the project, you should install VuePress as a local dependency. This setup also allows you to use CI or services like Netlify for automatic deployment on push.\n\n```bash\n# install as a local dependency\nyarn add -D vuepress@next # OR npm install -D vuepress@next\n\n# create a docs directory\nmkdir docs\n# create a markdown file\necho '# Hello VuePress' > docs/README.md\n```\n",[59,124502,124503,124508,124512,124517,124521,124526,124531,124536,124540,124545,124550,124555,124560],{"__ignoreMap":57},[62,124504,124505],{"class":64,"line":65},[62,124506,124507],{},"## Inside an Existing Project\n",[62,124509,124510],{"class":64,"line":76},[62,124511,79],{"emptyLinePlaceholder":13},[62,124513,124514],{"class":64,"line":82},[62,124515,124516],{},"If you have an existing project and would like to keep documentation inside the project, you should install VuePress as a local dependency. This setup also allows you to use CI or services like Netlify for automatic deployment on push.\n",[62,124518,124519],{"class":64,"line":89},[62,124520,79],{"emptyLinePlaceholder":13},[62,124522,124523],{"class":64,"line":95},[62,124524,124525],{},"```bash\n",[62,124527,124528],{"class":64,"line":101},[62,124529,124530],{},"# install as a local dependency\n",[62,124532,124533],{"class":64,"line":107},[62,124534,124535],{},"yarn add -D vuepress@next # OR npm install -D vuepress@next\n",[62,124537,124538],{"class":64,"line":113},[62,124539,79],{"emptyLinePlaceholder":13},[62,124541,124542],{"class":64,"line":129},[62,124543,124544],{},"# create a docs directory\n",[62,124546,124547],{"class":64,"line":134},[62,124548,124549],{},"mkdir docs\n",[62,124551,124552],{"class":64,"line":156},[62,124553,124554],{},"# create a markdown file\n",[62,124556,124557],{"class":64,"line":161},[62,124558,124559],{},"echo '# Hello VuePress' > docs/README.md\n",[62,124561,124562],{"class":64,"line":167},[62,124563,121597],{},[22,124565,124566],{},"When that is rendered to the page it looks like this:",[22,124568,124569],{},[653,124570],{"alt":57,"src":124571},"/images/blog/2019/05/31/output-1a5ea040-ca04-47a1-aff6-a5e4467bfb1a.png",[22,124573,124574],{},"Anything in between the triple backtick ``` is processed by the Markdown processor (in this case Markdown-it) and turned into the nice looking code block you see above complete with syntax highlighting. So knowing that the markdown process will take that code and turn it into a nice looking code block how can I display the actual code to you on this blog post or in a Github comment?",[26,124576,124578],{"id":124577},"escaping-the-backtick","Escaping the backtick",[22,124580,124581],{},"The answer is of course that you need to escape the backtick. Seems like something that would be really easy to do but after a lot of searching I kept coming up empty. I'm happy to tell you that I have found an answer to this but it might differ for you depending on the markdown processor you're using.",[22,124583,124584],{},"The first solution to our problem is to use more backticks. You will need to place 4 of them around your code block that contains 3. Notice that I have also specified the language as markdown which will allow you to get some syntax highlighting for your code.",[52,124586,124588],{"className":29690,"code":124587,"language":29692,"meta":57,"style":57},"````markdown\n```js\nconsole.log('Hello, World!');\n```\n````\n",[59,124589,124590,124595,124599,124604,124608],{"__ignoreMap":57},[62,124591,124592],{"class":64,"line":65},[62,124593,124594],{},"````markdown\n",[62,124596,124597],{"class":64,"line":76},[62,124598,121555],{},[62,124600,124601],{"class":64,"line":82},[62,124602,124603],{},"console.log('Hello, World!');\n",[62,124605,124606],{"class":64,"line":89},[62,124607,121597],{},[62,124609,124610],{"class":64,"line":95},[62,124611,124612],{},"````\n",[22,124614,124615],{},"Will render the following instead of displaying the console.log inside of a rendered code block.",[52,124617,124619],{"className":29690,"code":124618,"language":29692,"meta":57,"style":57},"```js\nconsole.log('Hello, World!');\n```\n",[59,124620,124621,124625,124629],{"__ignoreMap":57},[62,124622,124623],{"class":64,"line":65},[62,124624,121555],{},[62,124626,124627],{"class":64,"line":76},[62,124628,124603],{},[62,124630,124631],{"class":64,"line":82},[62,124632,121597],{},[22,124634,124635],{},"If you're going to have multiple blocks you don't need to surround each of them with 4 backticks. You can just put this around all of the text that you need to be escaped.",[52,124637,124639],{"className":29690,"code":124638,"language":29692,"meta":57,"style":57},"````markdown\nThis is some text\n```js\nconsole.log('Hello, World!');\n```\n\n```js\nconsole.log('Hello, World!');\n```\n````\n",[59,124640,124641,124645,124650,124654,124658,124662,124666,124670,124674,124678],{"__ignoreMap":57},[62,124642,124643],{"class":64,"line":65},[62,124644,124594],{},[62,124646,124647],{"class":64,"line":76},[62,124648,124649],{},"This is some text\n",[62,124651,124652],{"class":64,"line":82},[62,124653,121555],{},[62,124655,124656],{"class":64,"line":89},[62,124657,124603],{},[62,124659,124660],{"class":64,"line":95},[62,124661,121597],{},[62,124663,124664],{"class":64,"line":101},[62,124665,79],{"emptyLinePlaceholder":13},[62,124667,124668],{"class":64,"line":107},[62,124669,121555],{},[62,124671,124672],{"class":64,"line":113},[62,124673,124603],{},[62,124675,124676],{"class":64,"line":129},[62,124677,121597],{},[62,124679,124680],{"class":64,"line":134},[62,124681,124612],{},[22,124683,124684],{},"If you don't want to add more backticks or your markdown processor doesn't support it you can also solve this problem by using the tilde character. Another really good use case for 2 different options is this blog post. If I didn't have 2 options I wouldn't be able to escape the escape. This is getting very Inception like so I am going to stop here 🤯",[52,124686,124688],{"className":29690,"code":124687,"language":29692,"meta":57,"style":57},"~~~markdown\nThis is some text\n```js\nconsole.log('Hello, World!');\n```\n\n```js\nconsole.log('Hello, World!');\n```\n~~~\n",[59,124689,124690,124695,124699,124703,124707,124711,124715,124719,124723,124727],{"__ignoreMap":57},[62,124691,124692],{"class":64,"line":65},[62,124693,124694],{},"~~~markdown\n",[62,124696,124697],{"class":64,"line":76},[62,124698,124649],{},[62,124700,124701],{"class":64,"line":82},[62,124702,121555],{},[62,124704,124705],{"class":64,"line":89},[62,124706,124603],{},[62,124708,124709],{"class":64,"line":95},[62,124710,121597],{},[62,124712,124713],{"class":64,"line":101},[62,124714,79],{"emptyLinePlaceholder":13},[62,124716,124717],{"class":64,"line":107},[62,124718,121555],{},[62,124720,124721],{"class":64,"line":113},[62,124722,124603],{},[62,124724,124725],{"class":64,"line":129},[62,124726,121597],{},[62,124728,124729],{"class":64,"line":134},[62,124730,124731],{},"~~~\n",[26,124733,1499],{"id":1498},[22,124735,124736],{},"I hope this short tutorial saved your desk from your head banging on it 😉and as always friends...",[22,124738,36004,124739,82545],{},[36006,124740],{},[1527,124742,55375],{},{"title":57,"searchDepth":76,"depth":76,"links":124744},[124745,124746,124747],{"id":124490,"depth":76,"text":124491},{"id":124577,"depth":76,"text":124578},{"id":1498,"depth":76,"text":1499},{"_id":124749,"path":124750,"title":124751,"description":124752,"meta":124753,"body":124759},"content/blog/2019/05/17/vue-cli-error.md","/blog/2019/05/17/vue-cli-error","Vue CLI ValidationError: webpack Dev Server Invalid Options","In this article, I will tell you about an error I received while trying to run and new VueJS project this morning and how I was able to fix it.",{"slug":124754,"date":124755,"published":13,"tags":124756,"author":17,"cover":124758,"excerpt":-1},"vue-cli-error","2019-05-17T15:25:36.871Z",[105335,32645,124757,32793],"nodejs","vue-cli-validation-error.png",{"type":19,"value":124760,"toc":125489},[124761,124764,124768,124782,125255,125272,125278,125282,125290,125303,125340,125343,125424,125432,125469,125477,125479,125482,125486],[22,124762,124763],{},"This morning I ran into an issue creating a new Vue project using the Vue CLI. The strange thing about this is that I just created a new project yesterday and it ran fine. I did some digging around and this seems to be affecting a lot of users so I figured I would throw together a quick post about.",[26,124765,124767],{"id":124766},"creating-running-a-new-vuejs-project","Creating & Running a new VueJS Project",[22,124769,124770,124771,124774,124775,124777,124778,124781],{},"I started out my morning by creating a new project using the ",[677,124772,105805],{"href":105803,"rel":124773},[681],". After I created the project I changed into that directory and typed the command ",[59,124776,106310],{}," which calls ",[59,124779,124780],{},"vue-cli-service serve",". A few seconds after trying to startup I received the following error in the terminal.",[52,124783,124785],{"className":1663,"code":124784,"language":1665,"meta":57,"style":57}," INFO Starting development server...\n ERROR ValidationError: webpack Dev Server Invalid Options\n\noptions.clientLogLevel should be {String} and equal to one of the allowed values\n\n [ 'info', 'warn', 'error', 'debug', 'trace', 'silent' ]\n\n (https://webpack.js.org/configuration/dev-server/#devserverclientloglevel)\n\nValidationError: webpack Dev Server Invalid Options\n\noptions.clientLogLevel should be {String} and equal to one of the allowed values\n\n [ 'info', 'warn', 'error', 'debug', 'trace', 'silent' ]\n\n (https://webpack.js.org/configuration/dev-server/#devserverclientloglevel)\n\n at validateOptions (/Users/vega/dev/vue/hello-vue-cli/node_modules/schema-utils/src/validateOptions.js:32:11)\n at new Server (/Users/vega/dev/vue/hello-vue-cli/node_modules/webpack-dev-server/lib/Server.js:71:5)\n at serve (/Users/vega/dev/vue/hello-vue-cli/node_modules/@vue/cli-service/lib/commands/serve.js:138:20)\n at process._tickCallback (internal/process/next_tick.js:68:7)\n at Function.Module.runMain (internal/modules/cjs/loader.js:745:11)\n at startup (internal/bootstrap/node.js:283:19)\n at bootstrapNodeJSCore (internal/bootstrap/node.js:743:3)\nnpm ERR! code ELIFECYCLE\nnpm ERR! errno 1\nnpm ERR! hello-vue-cli@0.1.0 serve: `vue-cli-service serve`\nnpm ERR! Exit status 1\nnpm ERR!\nnpm ERR! Failed at the hello-vue-cli@0.1.0 serve script.\nnpm ERR! This is probably not a problem with npm. There is likely additional logging output above.\n\nnpm ERR! A complete log of this run can be found in:\nnpm ERR! /Users/vega/.npm/_logs/2019-05-17T15_05_54_552Z-debug.log\n",[59,124786,124787,124801,124824,124828,124860,124864,124900,124904,124913,124917,124932,124936,124962,124966,124994,124998,125006,125010,125020,125031,125040,125050,125060,125070,125080,125092,125104,125125,125139,125146,125167,125212,125216,125246],{"__ignoreMap":57},[62,124788,124789,124792,124795,124798],{"class":64,"line":65},[62,124790,124791],{"class":122}," INFO",[62,124793,124794],{"class":1675}," Starting",[62,124796,124797],{"class":1675}," development",[62,124799,124800],{"class":1675}," server...\n",[62,124802,124803,124806,124809,124812,124815,124818,124821],{"class":64,"line":76},[62,124804,124805],{"class":122}," ERROR",[62,124807,124808],{"class":1675}," ValidationError:",[62,124810,124811],{"class":1675}," webpack",[62,124813,124814],{"class":1675}," Dev",[62,124816,124817],{"class":1675}," Server",[62,124819,124820],{"class":1675}," Invalid",[62,124822,124823],{"class":1675}," Options\n",[62,124825,124826],{"class":64,"line":82},[62,124827,79],{"emptyLinePlaceholder":13},[62,124829,124830,124833,124836,124838,124841,124843,124846,124848,124850,124852,124854,124857],{"class":64,"line":89},[62,124831,124832],{"class":122},"options.clientLogLevel",[62,124834,124835],{"class":1675}," should",[62,124837,96544],{"class":1675},[62,124839,124840],{"class":1675}," {String}",[62,124842,50404],{"class":1675},[62,124844,124845],{"class":1675}," equal",[62,124847,57317],{"class":1675},[62,124849,115260],{"class":1675},[62,124851,59066],{"class":1675},[62,124853,53632],{"class":1675},[62,124855,124856],{"class":1675}," allowed",[62,124858,124859],{"class":1675}," values\n",[62,124861,124862],{"class":64,"line":95},[62,124863,79],{"emptyLinePlaceholder":13},[62,124865,124866,124869,124872,124874,124877,124879,124882,124884,124887,124889,124892,124894,124897],{"class":64,"line":101},[62,124867,124868],{"class":72}," [ ",[62,124870,124871],{"class":1675},"'info'",[62,124873,976],{"class":72},[62,124875,124876],{"class":1675},"'warn'",[62,124878,976],{"class":72},[62,124880,124881],{"class":1675},"'error'",[62,124883,976],{"class":72},[62,124885,124886],{"class":1675},"'debug'",[62,124888,976],{"class":72},[62,124890,124891],{"class":1675},"'trace'",[62,124893,976],{"class":72},[62,124895,124896],{"class":1675},"'silent'",[62,124898,124899],{"class":72}," ]\n",[62,124901,124902],{"class":64,"line":107},[62,124903,79],{"emptyLinePlaceholder":13},[62,124905,124906,124908,124911],{"class":64,"line":113},[62,124907,744],{"class":72},[62,124909,124910],{"class":122},"https://webpack.js.org/configuration/dev-server/#devserverclientloglevel",[62,124912,2212],{"class":72},[62,124914,124915],{"class":64,"line":129},[62,124916,79],{"emptyLinePlaceholder":13},[62,124918,124919,124922,124924,124926,124928,124930],{"class":64,"line":134},[62,124920,124921],{"class":122},"ValidationError:",[62,124923,124811],{"class":1675},[62,124925,124814],{"class":1675},[62,124927,124817],{"class":1675},[62,124929,124820],{"class":1675},[62,124931,124823],{"class":1675},[62,124933,124934],{"class":64,"line":156},[62,124935,79],{"emptyLinePlaceholder":13},[62,124937,124938,124940,124942,124944,124946,124948,124950,124952,124954,124956,124958,124960],{"class":64,"line":161},[62,124939,124832],{"class":122},[62,124941,124835],{"class":1675},[62,124943,96544],{"class":1675},[62,124945,124840],{"class":1675},[62,124947,50404],{"class":1675},[62,124949,124845],{"class":1675},[62,124951,57317],{"class":1675},[62,124953,115260],{"class":1675},[62,124955,59066],{"class":1675},[62,124957,53632],{"class":1675},[62,124959,124856],{"class":1675},[62,124961,124859],{"class":1675},[62,124963,124964],{"class":64,"line":167},[62,124965,79],{"emptyLinePlaceholder":13},[62,124967,124968,124970,124972,124974,124976,124978,124980,124982,124984,124986,124988,124990,124992],{"class":64,"line":173},[62,124969,124868],{"class":72},[62,124971,124871],{"class":1675},[62,124973,976],{"class":72},[62,124975,124876],{"class":1675},[62,124977,976],{"class":72},[62,124979,124881],{"class":1675},[62,124981,976],{"class":72},[62,124983,124886],{"class":1675},[62,124985,976],{"class":72},[62,124987,124891],{"class":1675},[62,124989,976],{"class":72},[62,124991,124896],{"class":1675},[62,124993,124899],{"class":72},[62,124995,124996],{"class":64,"line":179},[62,124997,79],{"emptyLinePlaceholder":13},[62,124999,125000,125002,125004],{"class":64,"line":185},[62,125001,744],{"class":72},[62,125003,124910],{"class":122},[62,125005,2212],{"class":72},[62,125007,125008],{"class":64,"line":191},[62,125009,79],{"emptyLinePlaceholder":13},[62,125011,125012,125014,125017],{"class":64,"line":209},[62,125013,70808],{"class":122},[62,125015,125016],{"class":1675}," validateOptions",[62,125018,125019],{"class":72}," (/Users/vega/dev/vue/hello-vue-cli/node_modules/schema-utils/src/validateOptions.js:32:11)\n",[62,125021,125022,125024,125026,125028],{"class":64,"line":220},[62,125023,70808],{"class":122},[62,125025,466],{"class":1675},[62,125027,124817],{"class":1675},[62,125029,125030],{"class":72}," (/Users/vega/dev/vue/hello-vue-cli/node_modules/webpack-dev-server/lib/Server.js:71:5)\n",[62,125032,125033,125035,125037],{"class":64,"line":226},[62,125034,70808],{"class":122},[62,125036,112281],{"class":1675},[62,125038,125039],{"class":72}," (/Users/vega/dev/vue/hello-vue-cli/node_modules/@vue/cli-service/lib/commands/serve.js:138:20)\n",[62,125041,125042,125044,125047],{"class":64,"line":231},[62,125043,70808],{"class":122},[62,125045,125046],{"class":1675}," process._tickCallback",[62,125048,125049],{"class":72}," (internal/process/next_tick.js:68:7)\n",[62,125051,125052,125054,125057],{"class":64,"line":236},[62,125053,70808],{"class":122},[62,125055,125056],{"class":1675}," Function.Module.runMain",[62,125058,125059],{"class":72}," (internal/modules/cjs/loader.js:745:11)\n",[62,125061,125062,125064,125067],{"class":64,"line":242},[62,125063,70808],{"class":122},[62,125065,125066],{"class":1675}," startup",[62,125068,125069],{"class":72}," (internal/bootstrap/node.js:283:19)\n",[62,125071,125072,125074,125077],{"class":64,"line":247},[62,125073,70808],{"class":122},[62,125075,125076],{"class":1675}," bootstrapNodeJSCore",[62,125078,125079],{"class":72}," (internal/bootstrap/node.js:743:3)\n",[62,125081,125082,125084,125087,125089],{"class":64,"line":252},[62,125083,32645],{"class":122},[62,125085,125086],{"class":1675}," ERR!",[62,125088,111495],{"class":1675},[62,125090,125091],{"class":1675}," ELIFECYCLE\n",[62,125093,125094,125096,125098,125101],{"class":64,"line":257},[62,125095,32645],{"class":122},[62,125097,125086],{"class":1675},[62,125099,125100],{"class":1675}," errno",[62,125102,125103],{"class":149}," 1\n",[62,125105,125106,125108,125110,125113,125116,125119,125122],{"class":64,"line":271},[62,125107,32645],{"class":122},[62,125109,125086],{"class":1675},[62,125111,125112],{"class":1675}," hello-vue-cli@0.1.0",[62,125114,125115],{"class":1675}," serve:",[62,125117,125118],{"class":1675}," `",[62,125120,125121],{"class":122},"vue-cli-service",[62,125123,125124],{"class":1675}," serve`\n",[62,125126,125127,125129,125131,125134,125137],{"class":64,"line":281},[62,125128,32645],{"class":122},[62,125130,125086],{"class":1675},[62,125132,125133],{"class":1675}," Exit",[62,125135,125136],{"class":1675}," status",[62,125138,125103],{"class":149},[62,125140,125141,125143],{"class":64,"line":286},[62,125142,32645],{"class":122},[62,125144,125145],{"class":1675}," ERR!\n",[62,125147,125148,125150,125152,125155,125158,125160,125162,125164],{"class":64,"line":291},[62,125149,32645],{"class":122},[62,125151,125086],{"class":1675},[62,125153,125154],{"class":1675}," Failed",[62,125156,125157],{"class":1675}," at",[62,125159,53632],{"class":1675},[62,125161,125112],{"class":1675},[62,125163,112281],{"class":1675},[62,125165,125166],{"class":1675}," script.\n",[62,125168,125169,125171,125173,125176,125178,125181,125183,125185,125188,125190,125193,125196,125198,125201,125204,125207,125209],{"class":64,"line":296},[62,125170,32645],{"class":122},[62,125172,125086],{"class":1675},[62,125174,125175],{"class":1675}," This",[62,125177,50409],{"class":1675},[62,125179,125180],{"class":1675}," probably",[62,125182,96541],{"class":1675},[62,125184,28870],{"class":1675},[62,125186,125187],{"class":1675}," problem",[62,125189,112416],{"class":1675},[62,125191,125192],{"class":1675}," npm.",[62,125194,125195],{"class":1675}," There",[62,125197,50409],{"class":1675},[62,125199,125200],{"class":1675}," likely",[62,125202,125203],{"class":1675}," additional",[62,125205,125206],{"class":1675}," logging",[62,125208,111940],{"class":1675},[62,125210,125211],{"class":1675}," above.\n",[62,125213,125214],{"class":64,"line":302},[62,125215,79],{"emptyLinePlaceholder":13},[62,125217,125218,125220,125222,125225,125228,125230,125232,125234,125236,125239,125241,125243],{"class":64,"line":308},[62,125219,32645],{"class":122},[62,125221,125086],{"class":1675},[62,125223,125224],{"class":1675}," A",[62,125226,125227],{"class":1675}," complete",[62,125229,111579],{"class":1675},[62,125231,59066],{"class":1675},[62,125233,9961],{"class":1675},[62,125235,1716],{"class":1675},[62,125237,125238],{"class":1675}," can",[62,125240,96544],{"class":1675},[62,125242,70795],{"class":1675},[62,125244,125245],{"class":1675}," in:\n",[62,125247,125248,125250,125252],{"class":64,"line":314},[62,125249,32645],{"class":122},[62,125251,125086],{"class":1675},[62,125253,125254],{"class":1675}," /Users/vega/.npm/_logs/2019-05-17T15_05_54_552Z-debug.log\n",[22,125256,125257,125258,125261,125262,125267,125268,125271],{},"I checked the version of the Vue CLI by running the command ",[59,125259,125260],{},"vue -V"," and I was ",[677,125263,125266],{"href":125264,"rel":125265},"https://github.com/vuejs/vue-cli/releases",[681],"running the latest 3.7.0",". I thought maybe I did something wrong so removed the project by running ",[59,125269,125270],{},"rm -Rf hello-vue-cli"," and tried again only to run into the same error.",[22,125273,125274,125275,125277],{},"I next wondered if this was affecting all of my projects or just new ones. I went into an existing project I had and ran ",[59,125276,106310],{}," and it started up just fine. This was adding to my head scratching because I didn't update NodeJS, NPM or Vue this morning so what the heck was going on.",[26,125279,125281],{"id":125280},"vue-cli-issues-on-github","Vue CLI Issues on Github",[22,125283,125284,125285,125289],{},"At this point, I was pretty confused so my next step was to head over to ",[677,125286,50830],{"href":125287,"rel":125288},"https://github.com/vuejs/vue-cli/issues",[681]," and see if anyone else was seeing similar issues. Sure enough, the first 2 posts looked very similar to what I was experiencing.",[22,125291,125292,125293,125298,125299,125302],{},"Shout out to ",[677,125294,125297],{"href":125295,"rel":125296},"https://github.com/vuejs/vue-cli/issues/4017#issuecomment-493481614",[681],"dland512"," for providing some clarity as to what was happening. It appears that the problem is with the generated webpack configuration ",[59,125300,125301],{},"node_modules/@vue/cli-service/lib/commands/serve.js"," which has the following:",[52,125304,125306],{"className":32791,"code":125305,"language":32793,"meta":57,"style":57},"const server = new WebpackDevServer(compiler, Object.assign({\n clientLogLevel: 'none',\n",[59,125307,125308,125330],{"__ignoreMap":57},[62,125309,125310,125312,125315,125317,125319,125322,125325,125328],{"class":64,"line":65},[62,125311,110541],{"class":68},[62,125313,125314],{"class":149}," server",[62,125316,2556],{"class":68},[62,125318,466],{"class":68},[62,125320,125321],{"class":122}," WebpackDevServer",[62,125323,125324],{"class":72},"(compiler, Object.",[62,125326,125327],{"class":122},"assign",[62,125329,50544],{"class":72},[62,125331,125332,125335,125338],{"class":64,"line":76},[62,125333,125334],{"class":72}," clientLogLevel: ",[62,125336,125337],{"class":1675},"'none'",[62,125339,3338],{"class":72},[22,125341,125342],{},"If you look back at the original error it said:",[52,125344,125346],{"className":1663,"code":125345,"language":1665,"meta":57,"style":57},"ValidationError: webpack Dev Server Invalid Options\n\noptions.clientLogLevel should be {String} and equal to one of the allowed values\n\n [ 'info', 'warn', 'error', 'debug', 'trace', 'silent' ]\n",[59,125347,125348,125362,125366,125392,125396],{"__ignoreMap":57},[62,125349,125350,125352,125354,125356,125358,125360],{"class":64,"line":65},[62,125351,124921],{"class":122},[62,125353,124811],{"class":1675},[62,125355,124814],{"class":1675},[62,125357,124817],{"class":1675},[62,125359,124820],{"class":1675},[62,125361,124823],{"class":1675},[62,125363,125364],{"class":64,"line":76},[62,125365,79],{"emptyLinePlaceholder":13},[62,125367,125368,125370,125372,125374,125376,125378,125380,125382,125384,125386,125388,125390],{"class":64,"line":82},[62,125369,124832],{"class":122},[62,125371,124835],{"class":1675},[62,125373,96544],{"class":1675},[62,125375,124840],{"class":1675},[62,125377,50404],{"class":1675},[62,125379,124845],{"class":1675},[62,125381,57317],{"class":1675},[62,125383,115260],{"class":1675},[62,125385,59066],{"class":1675},[62,125387,53632],{"class":1675},[62,125389,124856],{"class":1675},[62,125391,124859],{"class":1675},[62,125393,125394],{"class":64,"line":89},[62,125395,79],{"emptyLinePlaceholder":13},[62,125397,125398,125400,125402,125404,125406,125408,125410,125412,125414,125416,125418,125420,125422],{"class":64,"line":95},[62,125399,124868],{"class":72},[62,125401,124871],{"class":1675},[62,125403,976],{"class":72},[62,125405,124876],{"class":1675},[62,125407,976],{"class":72},[62,125409,124881],{"class":1675},[62,125411,976],{"class":72},[62,125413,124886],{"class":1675},[62,125415,976],{"class":72},[62,125417,124891],{"class":1675},[62,125419,976],{"class":72},[62,125421,124896],{"class":1675},[62,125423,124899],{"class":72},[22,125425,125426,125427,125429,125430,105860],{},"Still not sure what has caused this but it could be the ",[59,125428,125121],{}," itself. The workaround is to create a new file in the root of your project ",[59,125431,105859],{},[52,125433,125435],{"className":32791,"code":125434,"language":32793,"meta":57,"style":57},"module.exports = {\n devServer: {\n clientLogLevel: 'info'\n }\n};\n",[59,125436,125437,125449,125453,125461,125465],{"__ignoreMap":57},[62,125438,125439,125441,125443,125445,125447],{"class":64,"line":65},[62,125440,32813],{"class":149},[62,125442,2755],{"class":72},[62,125444,32818],{"class":149},[62,125446,2556],{"class":68},[62,125448,126],{"class":72},[62,125450,125451],{"class":64,"line":76},[62,125452,105894],{"class":72},[62,125454,125455,125458],{"class":64,"line":82},[62,125456,125457],{"class":72}," clientLogLevel: ",[62,125459,125460],{"class":1675},"'info'\n",[62,125462,125463],{"class":64,"line":89},[62,125464,223],{"class":72},[62,125466,125467],{"class":64,"line":95},[62,125468,107354],{"class":72},[22,125470,125471,125472,2755],{},"After adding this configuration I was able to run the project with no errors. If you want to read more about the Vue CLI global configuration you can check out the ",[677,125473,125476],{"href":125474,"rel":125475},"https://cli.vuejs.org/config/#global-cli-config",[681],"documentation here",[26,125478,1499],{"id":1498},[22,125480,125481],{},"I am guessing by the time this article gets indexed it will no longer be an issue but I thought it was important to write up. This is one of those errors that would have completely derailed a day or even a week for me 15 years ago. With experience and a lot of patience, I have learned how to deal with issues like this that will ultimately come up. I hope this article either fixed this issue for you or just gave you some insight into my thought process of working through a frustrating issue. As always ...",[22,125483,36004,125484,82545],{},[36006,125485],{},[1527,125487,125488],{},"html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}",{"title":57,"searchDepth":76,"depth":76,"links":125490},[125491,125492,125493],{"id":124766,"depth":76,"text":124767},{"id":125280,"depth":76,"text":125281},{"id":1498,"depth":76,"text":1499},{"_id":125495,"path":125496,"title":125497,"description":125498,"meta":125499,"body":125505},"content/blog/2019/05/15/run-vue-visual-studio-code.md","/blog/2019/05/15/run-vue-visual-studio-code","3 ways to run VueJS applications from Visual Studio Code","In this tutorial, I am going to show you 3 ways to run your VueJS applications from Visual Studio Code. ",{"slug":125500,"date":125501,"published":13,"tags":125502,"author":17,"cover":125504,"excerpt":-1},"run-vue-visual-studio-code","2019-05-15T14:19:36.459Z",[105335,32645,125503,32793],"vscode","./run-vue-visual-studio-code-cover.png",{"type":19,"value":125506,"toc":126201},[125507,125513,125518,125522,125534,125819,125823,125829,125833,125836,125839,125843,125846,125852,125958,125961,125977,125983,125999,126084,126088,126106,126111,126114,126119,126131,126136,126139,126144,126149,126153,126156,126162,126165,126181,126184,126189,126191,126194,126198],[22,125508,125509,125510,125512],{},"In this tutorial, I am going to show you 3 ways to run your VueJS applications from Visual Studio Code. This will also work for any NPM project that has a ",[59,125511,32733],{}," but I am going to focus on Vue for this article. I have found that developers who are new to both NPM & Vue have some difficulty getting started so hopefully this will help.",[22,125514,125515],{},[653,125516],{"alt":57,"src":125517},"/images/blog/2019/05/15/2019-05-15_07-33-46-8ad84b96-bc16-4d6e-902b-aa83d9fac58e.png",[26,125519,125521],{"id":125520},"node-npm-packagejson","Node, NPM & package.json",[22,125523,125524,125525,125527,125528,125530,125531,2755],{},"If you're new to Node & NPM the good news is you don't need to know everything to get up and running. Whenever you see a ",[59,125526,32733],{}," just know that this file is there to help you manage your project. In this file, you will find metadata about the project, packages that this project depends on and scripts that are available to run. Here is a simple ",[59,125529,32733],{}," of a project created using the ",[677,125532,105805],{"href":105803,"rel":125533},[681],[52,125535,125537],{"className":32791,"code":125536,"language":32793,"meta":57,"style":57},"{\n \"name\": \"hello-world\",\n \"version\": \"0.1.0\",\n \"private\": true,\n \"scripts\": {\n \"serve\": \"vue-cli-service serve\",\n \"build\": \"vue-cli-service build\",\n \"lint\": \"vue-cli-service lint\",\n \"test:e2e\": \"vue-cli-service test:e2e\",\n \"test:unit\": \"vue-cli-service test:unit\"\n },\n \"dependencies\": {\n \"core-js\": \"^2.6.5\",\n \"vue\": \"^2.6.10\"\n },\n \"devDependencies\": {\n \"@vue/cli-plugin-babel\": \"^3.7.0\",\n \"@vue/cli-plugin-e2e-cypress\": \"^3.7.0\",\n \"@vue/cli-plugin-eslint\": \"^3.7.0\",\n \"@vue/cli-plugin-unit-mocha\": \"^3.7.0\",\n \"@vue/cli-service\": \"^3.7.0\",\n \"@vue/test-utils\": \"1.0.0-beta.29\",\n \"babel-eslint\": \"^10.0.1\",\n \"chai\": \"^4.1.2\",\n \"eslint\": \"^5.16.0\",\n \"eslint-plugin-vue\": \"^5.0.0\",\n \"vue-template-compiler\": \"^2.5.21\"\n }\n}\n",[59,125538,125539,125543,125553,125565,125576,125583,125595,125607,125619,125631,125641,125645,125652,125664,125674,125678,125685,125697,125708,125719,125730,125741,125753,125765,125777,125789,125801,125811,125815],{"__ignoreMap":57},[62,125540,125541],{"class":64,"line":65},[62,125542,3680],{"class":72},[62,125544,125545,125547,125549,125551],{"class":64,"line":76},[62,125546,87967],{"class":1675},[62,125548,3696],{"class":72},[62,125550,43555],{"class":1675},[62,125552,3338],{"class":72},[62,125554,125555,125558,125560,125563],{"class":64,"line":82},[62,125556,125557],{"class":1675}," \"version\"",[62,125559,3696],{"class":72},[62,125561,125562],{"class":1675},"\"0.1.0\"",[62,125564,3338],{"class":72},[62,125566,125567,125570,125572,125574],{"class":64,"line":89},[62,125568,125569],{"class":1675}," \"private\"",[62,125571,3696],{"class":72},[62,125573,21775],{"class":149},[62,125575,3338],{"class":72},[62,125577,125578,125581],{"class":64,"line":95},[62,125579,125580],{"class":1675}," \"scripts\"",[62,125582,3688],{"class":72},[62,125584,125585,125588,125590,125593],{"class":64,"line":101},[62,125586,125587],{"class":1675}," \"serve\"",[62,125589,3696],{"class":72},[62,125591,125592],{"class":1675},"\"vue-cli-service serve\"",[62,125594,3338],{"class":72},[62,125596,125597,125600,125602,125605],{"class":64,"line":107},[62,125598,125599],{"class":1675}," \"build\"",[62,125601,3696],{"class":72},[62,125603,125604],{"class":1675},"\"vue-cli-service build\"",[62,125606,3338],{"class":72},[62,125608,125609,125612,125614,125617],{"class":64,"line":113},[62,125610,125611],{"class":1675}," \"lint\"",[62,125613,3696],{"class":72},[62,125615,125616],{"class":1675},"\"vue-cli-service lint\"",[62,125618,3338],{"class":72},[62,125620,125621,125624,125626,125629],{"class":64,"line":129},[62,125622,125623],{"class":1675}," \"test:e2e\"",[62,125625,3696],{"class":72},[62,125627,125628],{"class":1675},"\"vue-cli-service test:e2e\"",[62,125630,3338],{"class":72},[62,125632,125633,125636,125638],{"class":64,"line":134},[62,125634,125635],{"class":1675}," \"test:unit\"",[62,125637,3696],{"class":72},[62,125639,125640],{"class":1675},"\"vue-cli-service test:unit\"\n",[62,125642,125643],{"class":64,"line":156},[62,125644,32848],{"class":72},[62,125646,125647,125650],{"class":64,"line":161},[62,125648,125649],{"class":1675}," \"dependencies\"",[62,125651,3688],{"class":72},[62,125653,125654,125657,125659,125662],{"class":64,"line":167},[62,125655,125656],{"class":1675}," \"core-js\"",[62,125658,3696],{"class":72},[62,125660,125661],{"class":1675},"\"^2.6.5\"",[62,125663,3338],{"class":72},[62,125665,125666,125669,125671],{"class":64,"line":173},[62,125667,125668],{"class":1675}," \"vue\"",[62,125670,3696],{"class":72},[62,125672,125673],{"class":1675},"\"^2.6.10\"\n",[62,125675,125676],{"class":64,"line":179},[62,125677,32848],{"class":72},[62,125679,125680,125683],{"class":64,"line":185},[62,125681,125682],{"class":1675}," \"devDependencies\"",[62,125684,3688],{"class":72},[62,125686,125687,125690,125692,125695],{"class":64,"line":191},[62,125688,125689],{"class":1675}," \"@vue/cli-plugin-babel\"",[62,125691,3696],{"class":72},[62,125693,125694],{"class":1675},"\"^3.7.0\"",[62,125696,3338],{"class":72},[62,125698,125699,125702,125704,125706],{"class":64,"line":209},[62,125700,125701],{"class":1675}," \"@vue/cli-plugin-e2e-cypress\"",[62,125703,3696],{"class":72},[62,125705,125694],{"class":1675},[62,125707,3338],{"class":72},[62,125709,125710,125713,125715,125717],{"class":64,"line":220},[62,125711,125712],{"class":1675}," \"@vue/cli-plugin-eslint\"",[62,125714,3696],{"class":72},[62,125716,125694],{"class":1675},[62,125718,3338],{"class":72},[62,125720,125721,125724,125726,125728],{"class":64,"line":226},[62,125722,125723],{"class":1675}," \"@vue/cli-plugin-unit-mocha\"",[62,125725,3696],{"class":72},[62,125727,125694],{"class":1675},[62,125729,3338],{"class":72},[62,125731,125732,125735,125737,125739],{"class":64,"line":231},[62,125733,125734],{"class":1675}," \"@vue/cli-service\"",[62,125736,3696],{"class":72},[62,125738,125694],{"class":1675},[62,125740,3338],{"class":72},[62,125742,125743,125746,125748,125751],{"class":64,"line":236},[62,125744,125745],{"class":1675}," \"@vue/test-utils\"",[62,125747,3696],{"class":72},[62,125749,125750],{"class":1675},"\"1.0.0-beta.29\"",[62,125752,3338],{"class":72},[62,125754,125755,125758,125760,125763],{"class":64,"line":242},[62,125756,125757],{"class":1675}," \"babel-eslint\"",[62,125759,3696],{"class":72},[62,125761,125762],{"class":1675},"\"^10.0.1\"",[62,125764,3338],{"class":72},[62,125766,125767,125770,125772,125775],{"class":64,"line":247},[62,125768,125769],{"class":1675}," \"chai\"",[62,125771,3696],{"class":72},[62,125773,125774],{"class":1675},"\"^4.1.2\"",[62,125776,3338],{"class":72},[62,125778,125779,125782,125784,125787],{"class":64,"line":252},[62,125780,125781],{"class":1675}," \"eslint\"",[62,125783,3696],{"class":72},[62,125785,125786],{"class":1675},"\"^5.16.0\"",[62,125788,3338],{"class":72},[62,125790,125791,125794,125796,125799],{"class":64,"line":257},[62,125792,125793],{"class":1675}," \"eslint-plugin-vue\"",[62,125795,3696],{"class":72},[62,125797,125798],{"class":1675},"\"^5.0.0\"",[62,125800,3338],{"class":72},[62,125802,125803,125806,125808],{"class":64,"line":271},[62,125804,125805],{"class":1675}," \"vue-template-compiler\"",[62,125807,3696],{"class":72},[62,125809,125810],{"class":1675},"\"^2.5.21\"\n",[62,125812,125813],{"class":64,"line":281},[62,125814,3731],{"class":72},[62,125816,125817],{"class":64,"line":286},[62,125818,379],{"class":72},[26,125820,125822],{"id":125821},"visual-studio-code","Visual Studio Code",[22,125824,125825,125826,125828],{},"Now that you have a brief introduction to what information the ",[59,125827,32733],{}," contains it's time to learn how to run our VueJS applications from Visual Studio Code.",[636,125830,125832],{"id":125831},"using-the-integrated-terminal","Using the integrated terminal",[22,125834,125835],{},"The first way to run your VueJS applications from Visual Studio Code is the one you might have already learned about and that is by using the integrated terminal. If you're opening up a new terminal/command prompt to run your projects this will speed things up and bring everything back to Visual Studio Code.",[22,125837,125838],{},"With your project open in Visual Studio Code hit the keyboard shortcut (cmd/ctrl) + `. The backtick is located right above the tab key on your keyboard. This will open the integrated terminal and from there you can run any script for your project.",[636,125840,125842],{"id":125841},"what-commands-can-i-run","What commands can I run?",[22,125844,125845],{},"Now this is often a point of confusion for those new to VueJS and NPM in general. What is the command that I type to start my application? What is the command that I type to run my tests? After you have been working in Vue for a while these will become second nature but there are cases where you might inherit a project that has custom scripts.",[22,125847,125848,125849,125851],{},"The easiest way to find out what scripts are available is to open the ",[59,125850,32733],{}," and look in the scripts block.",[52,125853,125855],{"className":32791,"code":125854,"language":32793,"meta":57,"style":57},"{\n \"name\": \"hello-world\",\n \"version\": \"0.1.0\",\n \"private\": true,\n \"scripts\": {\n \"serve\": \"vue-cli-service serve\",\n \"build\": \"vue-cli-service build\",\n \"lint\": \"vue-cli-service lint\",\n \"test:e2e\": \"vue-cli-service test:e2e\",\n \"test:unit\": \"vue-cli-service test:unit\"\n },\n ...\n}\n",[59,125856,125857,125861,125871,125881,125891,125897,125907,125917,125927,125937,125945,125949,125954],{"__ignoreMap":57},[62,125858,125859],{"class":64,"line":65},[62,125860,3680],{"class":72},[62,125862,125863,125865,125867,125869],{"class":64,"line":76},[62,125864,87967],{"class":1675},[62,125866,3696],{"class":72},[62,125868,43555],{"class":1675},[62,125870,3338],{"class":72},[62,125872,125873,125875,125877,125879],{"class":64,"line":82},[62,125874,125557],{"class":1675},[62,125876,3696],{"class":72},[62,125878,125562],{"class":1675},[62,125880,3338],{"class":72},[62,125882,125883,125885,125887,125889],{"class":64,"line":89},[62,125884,125569],{"class":1675},[62,125886,3696],{"class":72},[62,125888,21775],{"class":149},[62,125890,3338],{"class":72},[62,125892,125893,125895],{"class":64,"line":95},[62,125894,125580],{"class":1675},[62,125896,3688],{"class":72},[62,125898,125899,125901,125903,125905],{"class":64,"line":101},[62,125900,125587],{"class":1675},[62,125902,3696],{"class":72},[62,125904,125592],{"class":1675},[62,125906,3338],{"class":72},[62,125908,125909,125911,125913,125915],{"class":64,"line":107},[62,125910,125599],{"class":1675},[62,125912,3696],{"class":72},[62,125914,125604],{"class":1675},[62,125916,3338],{"class":72},[62,125918,125919,125921,125923,125925],{"class":64,"line":113},[62,125920,125611],{"class":1675},[62,125922,3696],{"class":72},[62,125924,125616],{"class":1675},[62,125926,3338],{"class":72},[62,125928,125929,125931,125933,125935],{"class":64,"line":129},[62,125930,125623],{"class":1675},[62,125932,3696],{"class":72},[62,125934,125628],{"class":1675},[62,125936,3338],{"class":72},[62,125938,125939,125941,125943],{"class":64,"line":134},[62,125940,125635],{"class":1675},[62,125942,3696],{"class":72},[62,125944,125640],{"class":1675},[62,125946,125947],{"class":64,"line":156},[62,125948,32848],{"class":72},[62,125950,125951],{"class":64,"line":161},[62,125952,125953],{"class":68}," ...\n",[62,125955,125956],{"class":64,"line":167},[62,125957,379],{"class":72},[22,125959,125960],{},"Looking at this I can tell right away that the 5 scripts I have available to me are:",[915,125962,125963,125966,125968,125971,125974],{},[37,125964,125965],{},"serve",[37,125967,2189],{},[37,125969,125970],{},"lint",[37,125972,125973],{},"test:e2e",[37,125975,125976],{},"test:unit",[22,125978,125979,125980,125982],{},"So if I want to run any of these I simply type ",[59,125981,106310],{}," or the name of the script you want to run. The serve script is the one that will start your application up in development mode. The scripts block above is what a typical VueJS project will look when you create it using the Vue CLI and selecting both Unit & End to End testing.",[22,125984,125985,125986,125991,125992,125995,125996,2755],{},"There is a chance that if you're working on an existing project there could be a bunch of custom scripts. ",[677,125987,125990],{"href":125988,"rel":125989},"https://www.danvega.dev/blog/2019/04/23/gridsome-blog-post-generator",[681],"In a recent article",", I documented the process that I used to create a new post generator that I can run to add a new blog post. In that case, I have a ",[59,125993,125994],{},"newpost"," script so to run that I just run the command ",[59,125997,125998],{},"npm run newpost",[52,126000,126002],{"className":32791,"code":126001,"language":32793,"meta":57,"style":57},"{\n \"name\": \"danvega-dev\",\n \"private\": true,\n \"scripts\": {\n \"build\": \"gridsome build\",\n \"develop\": \"gridsome develop\",\n \"explore\": \"gridsome explore\",\n \"newpost\": \"node ./scripts/newpost.js\"\n },\n",[59,126003,126004,126008,126019,126029,126035,126046,126058,126070,126080],{"__ignoreMap":57},[62,126005,126006],{"class":64,"line":65},[62,126007,3680],{"class":72},[62,126009,126010,126012,126014,126017],{"class":64,"line":76},[62,126011,87967],{"class":1675},[62,126013,3696],{"class":72},[62,126015,126016],{"class":1675},"\"danvega-dev\"",[62,126018,3338],{"class":72},[62,126020,126021,126023,126025,126027],{"class":64,"line":82},[62,126022,125569],{"class":1675},[62,126024,3696],{"class":72},[62,126026,21775],{"class":149},[62,126028,3338],{"class":72},[62,126030,126031,126033],{"class":64,"line":89},[62,126032,125580],{"class":1675},[62,126034,3688],{"class":72},[62,126036,126037,126039,126041,126044],{"class":64,"line":95},[62,126038,125599],{"class":1675},[62,126040,3696],{"class":72},[62,126042,126043],{"class":1675},"\"gridsome build\"",[62,126045,3338],{"class":72},[62,126047,126048,126051,126053,126056],{"class":64,"line":101},[62,126049,126050],{"class":1675}," \"develop\"",[62,126052,3696],{"class":72},[62,126054,126055],{"class":1675},"\"gridsome develop\"",[62,126057,3338],{"class":72},[62,126059,126060,126063,126065,126068],{"class":64,"line":107},[62,126061,126062],{"class":1675}," \"explore\"",[62,126064,3696],{"class":72},[62,126066,126067],{"class":1675},"\"gridsome explore\"",[62,126069,3338],{"class":72},[62,126071,126072,126075,126077],{"class":64,"line":113},[62,126073,126074],{"class":1675}," \"newpost\"",[62,126076,3696],{"class":72},[62,126078,126079],{"class":1675},"\"node ./scripts/newpost.js\"\n",[62,126081,126082],{"class":64,"line":129},[62,126083,32848],{"class":72},[636,126085,126087],{"id":126086},"tasks-run-task","Tasks: Run Task",[22,126089,126090,126091,126094,126095,34867,126098,126101,126102,126105],{},"So that's how we run scripts from the command line but not everyone loves typing out commands every time they want to run a project. With your project open the command palette by using the menu item ",[59,126092,126093],{},"View > Command Palette"," or by using the keyboard shortcut ",[59,126096,126097],{},"Shift + CMD + P",[59,126099,126100],{},"Shift + CTRL + P"," on Windows. From there type ",[59,126103,126104],{},"Tasks"," and click on the Run Task command.",[22,126107,126108],{},[653,126109],{"alt":57,"src":126110},"/images/blog/2019/05/15/2019-05-15_09-41-08-caa0581d-abfc-4bf2-9693-907857c6bb79.png",[22,126112,126113],{},"This will examine your project and give you a list of the available scripts to run.",[22,126115,126116],{},[653,126117],{"alt":57,"src":126118},"/images/blog/2019/05/15/2019-05-15_09-44-31-d411391e-3095-46a9-bdbc-4842f3950d12.png",[22,126120,126121,126122,126125,126126,2755],{},"You can click on ",[59,126123,126124],{},"npm:serve"," or you can start typing the word serve and hit enter when it's selected to run it without using your mouse. If you see the following options you can go ",[677,126127,126130],{"href":126128,"rel":126129},"https://code.visualstudio.com/docs/editor/tasks#vscode",[681],"here to learn more about scanning the task output",[22,126132,126133],{},[653,126134],{"alt":57,"src":126135},"/images/blog/2019/05/15/2019-05-15_09-46-03-ff4e4351-a423-4916-80e6-b2d561204651.png",[22,126137,126138],{},"If you hit continue without scanning the task output Visual Studio Code will run your task. This opens up a terminal for you and runs the script.",[22,126140,126141],{},[653,126142],{"alt":57,"src":126143},"/images/blog/2019/05/15/2019-05-15_09-49-10-c43e1838-3da5-4b05-9dc8-0b11cf078a4f.png",[22,126145,126146,2755],{},[4534,126147,126148],{},"The next time you run the task you won't be asked about scanning the output and this process becomes very quick",[636,126150,126152],{"id":126151},"npm-scripts-explorer","NPM Scripts Explorer",[22,126154,126155],{},"If you follow me on Twitter I put this tweet out promising a tip that you might not know existed.",[22,126157,126158],{},[677,126159,126160],{"href":126160,"rel":126161},"https://twitter.com/therealdanvega/status/1128628100898619392",[681],[22,126163,126164],{},"To enable this go into your Visual Studio Code settings and add the following setting",[52,126166,126168],{"className":32791,"code":126167,"language":32793,"meta":57,"style":57},"\"npm.enableScriptExplorer\": true,\n",[59,126169,126170],{"__ignoreMap":57},[62,126171,126172,126175,126177,126179],{"class":64,"line":65},[62,126173,126174],{"class":1675},"\"npm.enableScriptExplorer\"",[62,126176,3696],{"class":72},[62,126178,21775],{"class":149},[62,126180,3338],{"class":72},[22,126182,126183],{},"With your project open you will now have a NPM Scripts Explorer in the sidebar. If you click on the play icon next to the script name it will run the script for you, how awesome is that!",[22,126185,126186],{},[653,126187],{"alt":57,"src":126188},"/images/blog/2019/05/15/2019-05-15_09-57-14-84d9686f-8bf7-4263-b31a-ed5d7af74d62.png",[26,126190,1499],{"id":1498},[22,126192,126193],{},"I hope this article was helpful in identifying and running your VueJS applications from Visual Studio Code. There is usually more than one way to accomplish a task and not all of us have the same preferences. As always friends...",[22,126195,36004,126196,82545],{},[36006,126197],{},[1527,126199,126200],{},"html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}",{"title":57,"searchDepth":76,"depth":76,"links":126202},[126203,126204,126210],{"id":125520,"depth":76,"text":125521},{"id":125821,"depth":76,"text":125822,"children":126205},[126206,126207,126208,126209],{"id":125831,"depth":82,"text":125832},{"id":125841,"depth":82,"text":125842},{"id":126086,"depth":82,"text":126087},{"id":126151,"depth":82,"text":126152},{"id":1498,"depth":76,"text":1499},{"_id":126212,"path":126213,"title":126214,"description":126215,"meta":126216,"body":126222},"content/blog/2019/05/10/what-you-can-learn-from-live-coders.md","/blog/2019/05/10/what-you-can-learn-from-live-coders","What you can learn from live coders","2019 seems to be the year of the live coder. In this article I am going to tell you what you can learn from live coders. ",{"slug":126217,"date":126218,"published":13,"tags":126219,"author":17,"cover":126221,"excerpt":-1},"what-you-can-learn-from-live-coders","2019-05-10T18:42:50.066Z",[126220,37948],"twitch","./live-coders-cover.png",{"type":19,"value":126223,"toc":126733},[126224,126227,126230,126254,126258,126261,126264,126267,126270,126273,126276,126280,126289,126292,126295,126298,126302,126305,126308,126311,126315,126318,126321,126324,126326,126329,126332,126336,126339,126342,126346,126349,126353,126356,126681,126693,126697,126700,126703,126712,126716,126724,126726,126729],[22,126225,126226],{},"2019 seems to be the year of the live coder. In this article I am going to tell you what you can learn from live coders. When I was younger I used to love playing video games. I actually do still enjoy them now but if I have free time these days I would much rather spend it working on code. 🤓",[22,126228,126229],{},"I remember the first Nintendo I got for Christmas and I thought it was the coolest thing ever. Every day after school my friend Mike and I use to play whatever game we were trying to conquer at the time. When we got stuck we couldn't turn to Google for the answers (Yes, I am that old). We had to wait for Nintendo Power, or a guide dedicated to that game or as a last result we would just ask our friends the next day at school.",[11922,126231,126232,126240],{},[11925,126233,126234],{},[11928,126235,126236,126238],{},[11931,126237],{},[11931,126239],{},[11941,126241,126242],{},[11928,126243,126244,126249],{},[11946,126245,126246],{},[653,126247],{"alt":57,"src":126248},"/images/blog/2019/05/10/ea16e9bb12986a0b89f8bd5eefeb2d0f-14c63fb2-1532-44e2-a377-551c0ddc5222.jpg",[11946,126250,126251],{},[653,126252],{"alt":57,"src":126253},"/images/blog/2019/05/10/SMguide-ae13d10d-69b3-412a-951c-b6487d447a59.jpg",[26,126255,126257],{"id":126256},"live-streaming-gamers","Live Streaming Gamers",[22,126259,126260],{},"I bring this up because we are in the middle of an era of gamers who are live streaming. Twitch is the most popular platform for gamers and they saw 2.2 million broadcasters and 15 million daily views in 2018. Some of the top streamers on the platform earn upwards of $300,000 a year while the top earner pulled in over $3.5 million for 2018.",[22,126262,126263],{},"These numbers are just mind blowing to me and shows just how little faith I had in the idea when I first heard about it. My initial reaction was \"Why would anyone want to watch someone else play a video game\"? It's funny to think about my reaction because if I start to think about it I did the same thing when I was younger but on a much smaller scale.",[22,126265,126266],{},"When I first got into video games we didn't have the ability to play with others online. If we wanted to test our skills against the best we had to go to the arcade at the mall. I was really into Street Fighter and Mortal Kombat and used to think I was so good until I got to the mall and found out there were some much better players out there.",[22,126268,126269],{},"After I got beat though I used to stand around the big crowds and watch the really good players play. Not because I didn't have anything better to do but because I wanted to learn why they were so good. What strategies were they using to dominate anyone who played against them.",[22,126271,126272],{},"This was the big picture point I was missing when I first dismissed the idea of live streaming. I am sure there are other reasons to watch these days as a lot of the big earners are just fun to listen to and there is a lot of entertainment value there.",[22,126274,126275],{},"You can probably see where I am going with this but I thought it would be good to relate it to this experience.",[26,126277,126279],{"id":126278},"live-streaming-coders","Live Streaming Coders",[22,126281,126282,126283,126288],{},"While 99% (not a real % just feels that way) of Twitch is using the platform to live stream gaming there is a small percentage of coders that have been using it to write code. YouTube & Facebook also give us the ability to go live in front of audience with the push of a button. There are also services like ",[677,126284,126287],{"href":126285,"rel":126286},"http://restream.io",[681],"Restream.io"," that allow you to stream to multiple platforms at once.",[22,126290,126291],{},"This is not anything new as there have been developers who have done this before but it just feels like 2019 is where this has really started to take off. There are a handful of developers that I have been watching this year and I have really enjoyed it.",[22,126293,126294],{},"I started telling friends and people at work about this and the immediate reaction I got was \"Why would you want to watch someone else live code?\". Does that sound familiar? Most of the time it's not as dismissive as my initial gaming comments but there is usually an eye roll involved.",[22,126296,126297],{},"The biggest argument I get is why wouldn't these developers just create a video and edit out their mistakes and upload this to YouTube like we have been doing for years now.",[636,126299,126301],{"id":126300},"problem-solvers","Problem Solvers",[22,126303,126304],{},"Problem solving is a key skill that all developers will need as they get started and it's something they will improve on throughout their career. The ability to break down a problem into smaller problems to solve the overall problem is something I do on a daily basis.",[22,126306,126307],{},"So when I hear comments like \"I would rather just see the finished product\" I have to disagree. There is so much that goes into watching another developer talk through his or her thought process when they are trying to solve a problem.",[22,126309,126310],{},"There is a reason that this is a part of the interview process. You might not always have all the answers but what we are looking for there is your ability to break down a problem into smaller problems and solve those one at a time.",[636,126312,126314],{"id":126313},"rock-star-developers-they-are-like-us","Rock Star Developers (They are like us)",[22,126316,126317],{},"The other part of watching someone live code is understanding that we all have the same struggles. This isn't always the case but usually the developers who are live coding have a lot of experience under their belt.",[22,126319,126320],{},"I think we hold some developers to a higher standard and think that they don't have the same struggles as we do. So when we see some of these rockstar developers that we all look up to doing the same things that we do, it really helps give us some confidence in our own abilities.",[22,126322,126323],{},"When I see someone unsure of a particular API or not sure how to do something and watch them Google for the answer I think it makes all of us realize that we are all in the same boat. Sure there are probably some really brilliant engineers out there who can code for hours without looking anything up but the rest of us just don't work like that.",[636,126325,37696],{"id":37695},[22,126327,126328],{},"I think another underrated feature of watching live coders is picking up little tips and tricks that developers take for granted. You might start watching because of the headlines but you will stay for the tips and tricks. While a developer might have a live session to build a new app from start to finish I always find the small things really helpful.",[22,126330,126331],{},"I like learning about the different tools in a developers toolbox. Even if its a tool I have heard of or even used before seeing how someone else uses it might inspire me to use it in a way I wouldn't of thought of. I also love seeing how developers setup their IDEs or what themes and extensions they are using.",[636,126333,126335],{"id":126334},"live-or-recorded","Live or Recorded",[22,126337,126338],{},"All of these benefits can also be gained through watching a recording. I need to make sure that I am very clear about this. I don't mean that you need to watch them as they are live but there is a big advantage to watching them live and that is the interaction. A lot of live coders are very interactive with the audience and use them to drive tutorials and discussions so don't be afraid to speak of.",[22,126340,126341],{},"When it comes to live coding you can certainly watch a recording but all I am referring to is that the developer didn't edit down the video to be perfect. When I am talking about live coding I want to see developers talk through problems and stumble just like we all do.",[26,126343,126345],{"id":126344},"live-coding","Live Coding",[22,126347,126348],{},"Now that you know why I love watching developers live code I want to leave you with some next steps. First we are going to start with some developers who are live coding regularly and then I will leave you with some resources if you're interested in live streaming yourself.",[636,126350,126352],{"id":126351},"developers-to-follow","Developers to follow",[22,126354,126355],{},"This is a list of developers that I have either watched at one point or watch on a regular basis. This list is in no particular order and I think everyone below is really amazing and I appreciate all of their efforts. These developers take time out of their day to help others so if you have a chance reach out to them and thank them for everything that they do.",[11922,126357,126358,126371],{},[11925,126359,126360],{},[11928,126361,126362,126364,126366,126369],{},[11931,126363,34914],{},[11931,126365,86336],{},[11931,126367,126368],{},"Twitch",[11931,126370,37949],{},[11941,126372,126373,126392,126411,126434,126453,126472,126491,126510,126529,126548,126567,126586,126605,126624,126643,126662],{},[11928,126374,126375,126378,126384,126390],{},[11946,126376,126377],{},"Chris Sevilleja",[11946,126379,126380],{},[677,126381,126382],{"href":126382,"rel":126383},"https://twitter.com/chrisoncode",[681],[11946,126385,126386],{},[677,126387,126388],{"href":126388,"rel":126389},"https://www.twitch.tv/chrisoncode",[681],[11946,126391],{},[11928,126393,126394,126397,126403,126409],{},[11946,126395,126396],{},"Brian Clark",[11946,126398,126399],{},[677,126400,126401],{"href":126401,"rel":126402},"https://twitter.com/_clarkio",[681],[11946,126404,126405],{},[677,126406,126407],{"href":126407,"rel":126408},"https://www.twitch.tv/clarkio",[681],[11946,126410],{},[11928,126412,126413,126416,126422,126428],{},[11946,126414,126415],{},"Fun Fun Function",[11946,126417,126418],{},[677,126419,126420],{"href":126420,"rel":126421},"https://twitter.com/mpjme",[681],[11946,126423,126424],{},[677,126425,126426],{"href":126426,"rel":126427},"https://www.twitch.tv/funfunfunction",[681],[11946,126429,126430],{},[677,126431,126432],{"href":126432,"rel":126433},"https://www.youtube.com/funfunfunction",[681],[11928,126435,126436,126439,126445,126451],{},[11946,126437,126438],{},"Suz Hinton",[11946,126440,126441],{},[677,126442,126443],{"href":126443,"rel":126444},"https://twitter.com/noopkat",[681],[11946,126446,126447],{},[677,126448,126449],{"href":126449,"rel":126450},"https://www.twitch.tv/noopkat",[681],[11946,126452],{},[11928,126454,126455,126458,126464,126470],{},[11946,126456,126457],{},"Aaron Frost",[11946,126459,126460],{},[677,126461,126462],{"href":126462,"rel":126463},"https://twitter.com/aaronfrost",[681],[11946,126465,126466],{},[677,126467,126468],{"href":126468,"rel":126469},"https://www.twitch.tv/frostydev",[681],[11946,126471],{},[11928,126473,126474,126477,126483,126485],{},[11946,126475,126476],{},"Daniel Shiffman",[11946,126478,126479],{},[677,126480,126481],{"href":126481,"rel":126482},"https://twitter.com/shiffman",[681],[11946,126484],{},[11946,126486,126487],{},[677,126488,126489],{"href":126489,"rel":126490},"https://www.youtube.com/user/shiffman",[681],[11928,126492,126493,126496,126502,126508],{},[11946,126494,126495],{},"Jeff Fritz",[11946,126497,126498],{},[677,126499,126500],{"href":126500,"rel":126501},"https://twitter.com/csharpfritz",[681],[11946,126503,126504],{},[677,126505,126506],{"href":126506,"rel":126507},"https://www.twitch.tv/csharpfritz",[681],[11946,126509],{},[11928,126511,126512,126515,126521,126527],{},[11946,126513,126514],{},"Steve Smith",[11946,126516,126517],{},[677,126518,126519],{"href":126519,"rel":126520},"https://twitter.com/ardalis",[681],[11946,126522,126523],{},[677,126524,126525],{"href":126525,"rel":126526},"https://www.twitch.tv/ardalis",[681],[11946,126528],{},[11928,126530,126531,126534,126540,126546],{},[11946,126532,126533],{},"Brendan Enrick",[11946,126535,126536],{},[677,126537,126538],{"href":126538,"rel":126539},"https://twitter.com/brendoneus",[681],[11946,126541,126542],{},[677,126543,126544],{"href":126544,"rel":126545},"https://www.twitch.tv/DevChatter",[681],[11946,126547],{},[11928,126549,126550,126553,126559,126565],{},[11946,126551,126552],{},"Ted Young",[11946,126554,126555],{},[677,126556,126557],{"href":126557,"rel":126558},"https://twitter.com/jitterted",[681],[11946,126560,126561],{},[677,126562,126563],{"href":126563,"rel":126564},"https://www.twitch.tv/jitterted",[681],[11946,126566],{},[11928,126568,126569,126572,126578,126580],{},[11946,126570,126571],{},"Kent C Dodds",[11946,126573,126574],{},[677,126575,126576],{"href":126576,"rel":126577},"https://twitter.com/kentcdodds",[681],[11946,126579],{},[11946,126581,126582],{},[677,126583,126584],{"href":126584,"rel":126585},"https://www.youtube.com/channel/UCz-BYvuntVRt_VpfR6FKXJw",[681],[11928,126587,126588,126591,126597,126599],{},[11946,126589,126590],{},"Scott Tolinkski",[11946,126592,126593],{},[677,126594,126595],{"href":126595,"rel":126596},"https://twitter.com/stolinski",[681],[11946,126598],{},[11946,126600,126601],{},[677,126602,126603],{"href":126603,"rel":126604},"https://www.youtube.com/c/leveluptuts",[681],[11928,126606,126607,126610,126616,126622],{},[11946,126608,126609],{},"Ed Charbeneau",[11946,126611,126612],{},[677,126613,126614],{"href":126614,"rel":126615},"https://twitter.com/EdCharbeneau",[681],[11946,126617,126618],{},[677,126619,126620],{"href":126620,"rel":126621},"https://www.twitch.tv/edcharbeneau",[681],[11946,126623],{},[11928,126625,126626,126629,126635,126641],{},[11946,126627,126628],{},"Alec Dilanchian",[11946,126630,126631],{},[677,126632,126633],{"href":126633,"rel":126634},"https://twitter.com/alec_dilanchian",[681],[11946,126636,126637],{},[677,126638,126639],{"href":126639,"rel":126640},"https://www.twitch.tv/pixelogicdev",[681],[11946,126642],{},[11928,126644,126645,126648,126654,126660],{},[11946,126646,126647],{},"Nick Larsen",[11946,126649,126650],{},[677,126651,126652],{"href":126652,"rel":126653},"https://twitter.com/fody",[681],[11946,126655,126656],{},[677,126657,126658],{"href":126658,"rel":126659},"https://www.twitch.tv/nick_larsen",[681],[11946,126661],{},[11928,126663,126664,126667,126673,126675],{},[11946,126665,126666],{},"Coding Garden (CJ)",[11946,126668,126669],{},[677,126670,126671],{"href":126671,"rel":126672},"https://twitter.com/coding_garden",[681],[11946,126674],{},[11946,126676,126677],{},[677,126678,126679],{"href":126679,"rel":126680},"https://www.youtube.com/c/CodingGardenWithCJ",[681],[22,126682,126683,126684,126686,126687,126692],{},"This is ",[646,126685,69066],{}," a full list so if you think I have left someone off who should be recognized please reach out and I will get this updated. I would also like to mention the ",[677,126688,126691],{"href":126689,"rel":126690},"https://www.twitch.tv/team/livecoders",[681],"Live Coders Twitch Group"," which has a really nice collection of developers who are live streaming so check them out.",[26,126694,126696],{"id":126695},"live-streaming-setup","Live Streaming Setup",[22,126698,126699],{},"Live streaming is one of those things that I wish I had more time to do. I have a list of about 10 things that I would love to walk through not to mention I have a ton of blog posts that could transfer to video.",[22,126701,126702],{},"I have done a total of 1 live stream so I am not the best person to ask for live streaming setup advice but I do have a couple resources for you.",[22,126704,126705,126706,126711],{},"Suz Hinton (who is on the list above) is an amazing developer who does live streaming on Twitch. She put together an amazing article on her ",[677,126707,126710],{"href":126708,"rel":126709},"https://medium.com/@suzhinton/my-twitch-live-coding-setup-b2516672fb21",[681],"Live Coding Setup"," that is a must read if you're looking to get into this space.",[636,126713,126715],{"id":126714},"ecamm-live","Ecamm Live",[22,126717,126718,126719,126723],{},"I also want to mention ",[677,126720,126715],{"href":126721,"rel":126722},"https://www.ecamm.com/mac/ecammlive/",[681]," which is the all in one live streaming production platform for the mac. If you're on a mac this an amazing piece of software with some really great features. I used it for the 1 broadcast I did but you can also use it for recording videos. If you're interested in hearing more about this reach out to me and let me know. I would love to do a detailed write up on Ecamm Live and the awesome team behind it.",[26,126725,1499],{"id":1498},[22,126727,126728],{},"If you can't tell from this article I really love everything you can learn from live coders. I am curious about your thoughts on developers live streaming. What do you get out of it? Who are some of your favorites? If I were to start live streaming what would you like to see from me? Until next time friends...",[22,126730,36004,126731,82545],{},[36006,126732],{},{"title":57,"searchDepth":76,"depth":76,"links":126734},[126735,126736,126742,126745,126748],{"id":126256,"depth":76,"text":126257},{"id":126278,"depth":76,"text":126279,"children":126737},[126738,126739,126740,126741],{"id":126300,"depth":82,"text":126301},{"id":126313,"depth":82,"text":126314},{"id":37695,"depth":82,"text":37696},{"id":126334,"depth":82,"text":126335},{"id":126344,"depth":76,"text":126345,"children":126743},[126744],{"id":126351,"depth":82,"text":126352},{"id":126695,"depth":76,"text":126696,"children":126746},[126747],{"id":126714,"depth":82,"text":126715},{"id":1498,"depth":76,"text":1499},{"_id":126750,"path":126751,"title":126752,"description":126753,"meta":126754,"body":126759},"content/blog/2019/05/08/codesandbox-custom-theme.md","/blog/2019/05/08/codesandbox-custom-theme","How to customize the VSCode Theme in CodeSandbox","In this article, I am going to give you a quick tip on how to customize the VSCode Theme in CodeSandbox.",{"slug":126755,"date":126756,"published":13,"tags":126757,"author":17,"cover":126758,"excerpt":-1},"codesandbox-custom-theme","2019-05-08T01:56:43.680Z",[124428],"./codesandbox-custom-theme-cover.png",{"type":19,"value":126760,"toc":126910},[126761,126775,126779,126782,126795,126800,126809,126820,126823,126828,126831,126835,126842,126847,126850,126857,126862,126865,126882,126893,126898,126901,126903,126906],[22,126762,126763,126764,126769,126770,126774],{},"This is going to be a short tutorial today but it's a goodie. In my ",[677,126765,126768],{"href":126766,"rel":126767},"https://www.danvega.dev/blog/2019/05/02/gridsome-codesandbox-plugin",[681],"last article",", I talked about what ",[677,126771,123340],{"href":126772,"rel":126773},"https://codesandbox.io/",[681]," is and why I love it so much. In this article, I am going to give you a quick tip on how to customize the VSCode Theme in CodeSandbox.",[26,126776,126778],{"id":126777},"visual-studio-code-themes","Visual Studio Code Themes",[22,126780,126781],{},"I think one of the first things developers like to do when they start using VSCode is to customize the theme. VSCode comes with a lot of great built-in themes and my more in the extension marketplace.",[22,126783,126784,126785,126789,126790,2755],{},"If you ",[677,126786,126788],{"href":86334,"rel":126787},[681],"follow me on Twitter"," you might already know this but for those of you who don't 🤔 I have a new favorite VSCode Theme and it's called ",[677,126791,126794],{"href":126792,"rel":126793},"https://github.com/robb0wen/synthwave-vscode",[681],"Synthwave 84",[22,126796,126797],{},[653,126798],{"alt":57,"src":126799},"/images/blog/2019/05/08/banner-2dbb41d1-cf07-49a7-8400-fd7c36a1c903.png",[22,126801,126802,126803,126808],{},"This a quick description of where the author (",[677,126804,126807],{"href":126805,"rel":126806},"https://github.com/robb0wen",[681],"Robb Owen",") got his inspiration from",[29685,126810,126811],{},[22,126812,126813,126814,126819],{},"This colour scheme is influenced by the music and the cover artwork of modern Synthwave bands like FM-84, Timecop 1983 and The Midnight. By association, that means I've also taken heavy influence from the excellent retro-tinged artwork of ",[677,126815,126818],{"href":126816,"rel":126817},"https://signalnoise.com/",[681],"James White"," (check out his work, it's awesome).",[22,126821,126822],{},"I have been using it now for almost 2 weeks and I have really fallen in love with it. For some reason it just puts me in a calm/good mood. Here is a quick screenshot of what editing my blog in this theme looks like.",[22,126824,126825],{},[653,126826],{"alt":57,"src":126827},"/images/blog/2019/05/08/2019-05-07_21-32-19-dac6471f-8b43-44e3-9d48-850033cf3769.png",[22,126829,126830],{},"Now that I have this theme on all of my machines it's time to move this over to CodeSandbox. It uses VSCode so this should just work right?",[26,126832,126834],{"id":126833},"custom-vscode-theme-in-codesandbox","Custom VSCode Theme in CodeSandbox",[22,126836,126837,126838,126841],{},"When I first started using CodeSandbox I thought I was limited to whatever themes were preloaded in the editor. If you open a project in CodeSandbox and go to ",[59,126839,126840],{},"File > Preferences > Color Theme"," you will be presented with a list of Themes.",[22,126843,126844],{},[653,126845],{"alt":57,"src":126846},"/images/blog/2019/05/08/2019-05-07_21-10-20-2c7ccf1f-d37d-4c3f-8d3b-a18a3629e842.png",[22,126848,126849],{},"After scrolling through the list I quickly found out that my new favorite theme was not available. What was I going to do? How would I ever be productive without it? I am joking of course but us developers love to have consistency across our development environments.",[22,126851,126852,126853,126856],{},"After doing some digging I found out that it was possible to bring Synthwave 84 over to CodeSandbox and here is how you do it. From the editor go to ",[59,126854,126855],{},"File > Preferences > CodeSandbox Settings",". You will notice under Editor Theme there are instructions to use a Custom VSCode Theme.",[22,126858,126859],{},[653,126860],{"alt":57,"src":126861},"/images/blog/2019/05/08/2019-05-07_21-18-45-cf7286cc-ca3e-4619-adac-f41efc3b63b8.png",[22,126863,126864],{},"If that is a little hard to read from the screenshot here are the instructions. You can use your own theme from VSCode directly:",[34,126866,126867,126870,126873,126879],{},[37,126868,126869],{},"Open VSCode",[37,126871,126872],{},"Press (CMD or CTRL) + SHIFT + P",[37,126874,126875,126876],{},"Enter: ",[59,126877,126878],{},"> Developer: Generate Color Scheme From Current Settings",[37,126880,126881],{},"Copy the contents and paste them here.",[22,126883,126884,126885,126888,126889,126892],{},"After that is done you can close that dialog by just clicking outside of it. To activate the theme go back to your list of Color Themes ",[59,126886,126887],{},"File > Preferences > Color Themes"," and select Custom Theme. If you don't see ",[59,126890,126891],{},"Custom Theme"," as an option you might need to refresh CodeSandbox.",[22,126894,126895],{},[653,126896],{"alt":57,"src":126897},"/images/blog/2019/05/08/2019-05-07_21-27-34-ea6a37f1-d158-4440-aa51-a82e8fdad59d.png",[22,126899,126900],{},"With that my new favorite theme is in CodeSandbox and I can get back to being a semi-productive developer!",[26,126902,1499],{"id":1498},[22,126904,126905],{},"If you can't tell already I am really loving everything about CodeSandbox right now. I am using it more and more personally and I have introduced it as a tool that we can take advantage of at work and the whole team is on board. Are you using CodeSandbox and if so for what? Also please let me know what some of your favorite themes are. As always...",[22,126907,36004,126908,82545],{},[36006,126909],{},{"title":57,"searchDepth":76,"depth":76,"links":126911},[126912,126913,126914],{"id":126777,"depth":76,"text":126778},{"id":126833,"depth":76,"text":126834},{"id":1498,"depth":76,"text":1499},{"_id":126916,"path":126917,"title":126918,"description":126919,"meta":126920,"body":126925},"content/blog/2019/05/02/gridsome-codesandbox-plugin.md","/blog/2019/05/02/gridsome-codesandbox-plugin","How I created a CodeSandbox plugin for Gridsome","In this article, I am going to introduce you to CodeSandbox and tell you a little bit about why I love it so much",{"slug":126921,"date":126922,"published":13,"tags":126923,"author":17,"cover":126924,"excerpt":-1},"gridsome-codesandbox-plugin","2019-05-02T15:30:43.839Z",[105335,113419,124428],"./gridsome-codesandbox-plugin-cover.png",{"type":19,"value":126926,"toc":128819},[126927,126935,126939,126954,126957,126961,126964,126967,126972,126977,126985,126992,126995,126998,127003,127006,127012,127016,127019,127022,127025,127028,127031,127035,127042,127045,127060,127064,127083,127254,127265,127270,127273,127337,127341,127344,127372,127385,127408,127420,127610,127615,127626,127753,127756,127939,127944,127982,127985,128014,128023,128501,128505,128514,128521,128541,128544,128550,128566,128580,128584,128587,128807,128809,128812,128816],[22,126928,126929,126930,126934],{},"I have been a big fan of what ",[677,126931,123340],{"href":126932,"rel":126933},"https://codesandbox.io",[681]," has been doing for a while now. Lately, I have been using it a lot more and I am finding it to be one of the tools that I can't live without. In this article, I am going to introduce you to CodeSandbox and tell you a little bit about why I love it. Then I will walk you through the plugin I created so that I can embed a CodeSandbox in Markdown so that I could display them on my Gridsome powered blog.",[26,126936,126938],{"id":126937},"what-is-codesandbox","What is CodeSandbox",[22,126940,126941,126944,126945,60103,126950,126953],{},[677,126942,123340],{"href":126932,"rel":126943},[681]," is an online editor that helps you create web applications, from prototype to deployment. Just as an example let's say that you wanted to ",[677,126946,126949],{"href":126947,"rel":126948},"https://www.danvega.dev/blog/2019/04/30/up-and-running-with-vue",[681],"create your first",[677,126951,105537],{"href":117748,"rel":126952},[681]," application. You could make sure you had Visual Studio Code installed locally with the right extensions, node & npm, the Vue CLI and then create your first application from the command line. If you understood all of that and you wanted to take the time to get your development environment setup that's great, but what if you didn't?",[22,126955,126956],{},"What if you just wanted to check it out and create a new project hassle free? What if you wanted to do this for a number of web projects like React, Vue, Gatsby, Gridsome, VuePress, Next, Nuxt and so on. What if you wanted to use it as a teaching tool where a single concept was more important than the whole story. This is where CodeSandbox comes in and really shines bright.",[636,126958,126960],{"id":126959},"getting-started-with-codesandbox","Getting Started with CodeSandbox",[22,126962,126963],{},"To understand what it is and why you would want to use it I think it's one of those tools that you need to just go in and try for yourself. To get started you will need to signup using your Github account. From there you will be taken to your dashboard where you can create and organize new CodeSandboxes.",[22,126965,126966],{},"If you click on create sandbox you will be given a dialog that looks something like this. From here you can choose from a variety of templates. If you select Vue as your template it will spin up a new application using the Vue CLI.",[22,126968,126969],{},[653,126970],{"alt":57,"src":126971},"/images/blog/2019/05/02/2019-05-02_06-28-26-61e4a736-935d-42c1-973a-b0622a99a834.png",[22,126973,126974],{},[653,126975],{"alt":57,"src":126976},"/images/blog/2019/05/02/2019-05-02_06-32-24-f590aff3-0d4d-4c51-b494-5541e6f20e45.png",[22,126978,126979,126980,126984],{},"If you were to just exit out of here and go back to your dashboard nothing would have been saved for you. You can tell from the browser window preview URL that it's currently ",[677,126981,126982],{"href":126982,"rel":126983},"https://vue.codesandbox.io/",[681]," and not a unique address.",[22,126986,126987,126988,126991],{},"Once you click File > Save you should see that URL change to a unique one and now this is your CodeSandbox ready for you to play with. I also like to give this CodeSandbox a name so in the top header where it currently says ",[59,126989,126990],{},"My Sandbox / Vue Template",", click on Vue Template and change the name to whatever you're working on. In my case, I will change it to Hello VueSandbox.",[22,126993,126994],{},"If you're used to using Visual Studio Code than this should look pretty familiar. Yes, that is correct, this is Visual Studio Code (extensions and all) running in the browser. Take some time to open and edit some files and watch your changes trigger a reload of the preview. Have you noticed how smooth this online editing experience is?",[22,126996,126997],{},"If you need to install a dependency for your project simply click on the Add Dependency button in your project files and search for the npm package you want to install.",[22,126999,127000],{},[653,127001],{"alt":57,"src":127002},"/images/blog/2019/05/02/2019-05-02_06-39-54-3e52b93e-a9be-4593-9ece-d2039affd22d.png",[22,127004,127005],{},"It's just that simple to create a new project and now you have something that you can share with anyone. If you look in the upper right hand corner you will see a share button with a variety of ways to share your CodeSandbox. Here is a tutorial I followed along with on YouTube to learn about a using a draggable component.",[22,127007,127008],{},[677,127009,127010],{"href":127010,"rel":127011},"https://codesandbox.io/embed/wq3o75v4qk?fontsize=12",[681],[636,127013,127015],{"id":127014},"why-codesandbox","Why CodeSandbox",[22,127017,127018],{},"When I first heard about CodeSandbox I was pretty skeptical because I just couldn't for the life of me understand why anyone would want to code online. I have this great development environment on my local machine with Node + NPM, Visual Studio Code and everything set up to my preference.",[22,127020,127021],{},"Not to mention that this has been tried before and performance has never been anything but undesirable. Well, I am here to tell you that all my doubts and concerns about such a product have been removed thanks to CodeSandbox.",[22,127023,127024],{},"If you spend anytime in the editor you will see that the performance is great. I don't even want to think about the engineering feats that went into making this possible but I sure am grateful. So now that it works what are some reasons that you might want to use it?",[22,127026,127027],{},"Do you remember the days of zipping up your code and uploading to your FTP site and sharing it with your friends? Well I'm old so I do but you live in a great time where Github allows you to share your code with anyone right? Sure, but they still need to clone your repo and have all of the tools setup locally to run your code. Not to mention that your code probably isn't tried and tested at this point and while it runs fine on your machine it might not work on mine.",[22,127029,127030],{},"This can be extremely frustrating when you just want to view a cool demo or try out a framework/library for the first time. These are great use cases to share your CodeSandbox with friends, readers, followers or even students.",[26,127032,127034],{"id":127033},"creating-the-gridsome-plugin","Creating the Gridsome Plugin",[22,127036,127037,127038,127041],{},"Now that you know a little bit more about CodeSandbox I want to tell you how and why I created a plugin for Gridsome. If you have been following me you know that I moved my blog over to ",[677,127039,104836],{"href":104834,"rel":127040},[681]," this year. This allows me to write all of my blog posts in Markdown which is my preferred way of writing content.",[22,127043,127044],{},"This does comes with a challenge though when you need to add some sort of custom HTML, like in this case. When you are on a CodeSandbox you can hit the share button and you will be given an embed link or some HTML using an iframe that you can use to embed the CodeSandbox in your application.",[22,127046,127047,127048,127053,127054,127059],{},"Remark is the ",[677,127049,127052],{"href":127050,"rel":127051},"https://www.npmjs.com/package/@gridsome/transformer-remark",[681],"Markdown processor used by Gridsome"," and that is the first thing you need to understand if you're going to create a Markdown plugin. I have written a ",[677,127055,127058],{"href":127056,"rel":127057},"https://github.com/danvega/gridsome-plugin-remark-twitter",[681],"Gridsome Remark plugin"," before that allows you to insert Twitter Cards so I have a little bit of an idea what I need to do here.",[26,127061,127063],{"id":127062},"gridsome-plugin-first-steps","Gridsome plugin first steps",[22,127065,127066,127067,127072,127073,127076,127077,127079,127080,2755],{},"If you have never written a Gridsome Plugin I would check out ",[677,127068,127071],{"href":127069,"rel":127070},"https://gridsome.org/docs/how-to-create-a-plugin",[681],"their documentation"," before moving on. The first step you need to do is to create a folder called ",[59,127074,127075],{},"gridsome-plugin-remark-codesandbox"," which follows the convention of other Gridsome Remark plugins. From there you will create a new ",[59,127078,32733],{}," by running the command ",[59,127081,127082],{},"npm init",[52,127084,127086],{"className":32791,"code":127085,"language":32793,"meta":57,"style":57},"{\n \"name\": \"gridsome-plugin-remark-codesandbox\",\n \"version\": \"0.1.0\",\n \"description\": \"This plugin will allow you to add a codesandbox embed link to your markdown files\",\n \"main\": \"index.js\",\n \"scripts\": {\n \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n },\n \"keywords\": [\n \"gridsome\",\n \"gridsome-plugin\",\n \"markdown\",\n \"vuejs\",\n \"codesandbox\"\n ],\n \"author\": \"Dan Vega \u003Cdanvega@gmail.com>\",\n \"license\": \"MIT\",\n \"dependencies\": {\n\n }\n}\n",[59,127087,127088,127092,127103,127113,127125,127137,127143,127164,127168,127175,127182,127189,127196,127203,127208,127212,127224,127236,127242,127246,127250],{"__ignoreMap":57},[62,127089,127090],{"class":64,"line":65},[62,127091,3680],{"class":72},[62,127093,127094,127096,127098,127101],{"class":64,"line":76},[62,127095,87967],{"class":1675},[62,127097,3696],{"class":72},[62,127099,127100],{"class":1675},"\"gridsome-plugin-remark-codesandbox\"",[62,127102,3338],{"class":72},[62,127104,127105,127107,127109,127111],{"class":64,"line":82},[62,127106,125557],{"class":1675},[62,127108,3696],{"class":72},[62,127110,125562],{"class":1675},[62,127112,3338],{"class":72},[62,127114,127115,127118,127120,127123],{"class":64,"line":89},[62,127116,127117],{"class":1675}," \"description\"",[62,127119,3696],{"class":72},[62,127121,127122],{"class":1675},"\"This plugin will allow you to add a codesandbox embed link to your markdown files\"",[62,127124,3338],{"class":72},[62,127126,127127,127130,127132,127135],{"class":64,"line":95},[62,127128,127129],{"class":1675}," \"main\"",[62,127131,3696],{"class":72},[62,127133,127134],{"class":1675},"\"index.js\"",[62,127136,3338],{"class":72},[62,127138,127139,127141],{"class":64,"line":101},[62,127140,125580],{"class":1675},[62,127142,3688],{"class":72},[62,127144,127145,127148,127150,127153,127156,127159,127161],{"class":64,"line":107},[62,127146,127147],{"class":1675}," \"test\"",[62,127149,3696],{"class":72},[62,127151,127152],{"class":1675},"\"echo ",[62,127154,127155],{"class":149},"\\\"",[62,127157,127158],{"class":1675},"Error: no test specified",[62,127160,127155],{"class":149},[62,127162,127163],{"class":1675}," && exit 1\"\n",[62,127165,127166],{"class":64,"line":113},[62,127167,32848],{"class":72},[62,127169,127170,127173],{"class":64,"line":129},[62,127171,127172],{"class":1675}," \"keywords\"",[62,127174,3709],{"class":72},[62,127176,127177,127180],{"class":64,"line":134},[62,127178,127179],{"class":1675}," \"gridsome\"",[62,127181,3338],{"class":72},[62,127183,127184,127187],{"class":64,"line":156},[62,127185,127186],{"class":1675}," \"gridsome-plugin\"",[62,127188,3338],{"class":72},[62,127190,127191,127194],{"class":64,"line":161},[62,127192,127193],{"class":1675}," \"markdown\"",[62,127195,3338],{"class":72},[62,127197,127198,127201],{"class":64,"line":167},[62,127199,127200],{"class":1675}," \"vuejs\"",[62,127202,3338],{"class":72},[62,127204,127205],{"class":64,"line":173},[62,127206,127207],{"class":1675}," \"codesandbox\"\n",[62,127209,127210],{"class":64,"line":179},[62,127211,50066],{"class":72},[62,127213,127214,127217,127219,127222],{"class":64,"line":185},[62,127215,127216],{"class":1675}," \"author\"",[62,127218,3696],{"class":72},[62,127220,127221],{"class":1675},"\"Dan Vega \u003Cdanvega@gmail.com>\"",[62,127223,3338],{"class":72},[62,127225,127226,127229,127231,127234],{"class":64,"line":191},[62,127227,127228],{"class":1675}," \"license\"",[62,127230,3696],{"class":72},[62,127232,127233],{"class":1675},"\"MIT\"",[62,127235,3338],{"class":72},[62,127237,127238,127240],{"class":64,"line":209},[62,127239,125649],{"class":1675},[62,127241,3688],{"class":72},[62,127243,127244],{"class":64,"line":220},[62,127245,79],{"emptyLinePlaceholder":13},[62,127247,127248],{"class":64,"line":226},[62,127249,3731],{"class":72},[62,127251,127252],{"class":64,"line":231},[62,127253,379],{"class":72},[22,127255,127256,127257,127260,127261,2755],{},"One important note here is that you need to add the keyword ",[59,127258,127259],{},"gridsome-plugin"," if you want it to get picked up on the Gridsome Plugins Search at ",[677,127262,127263],{"href":127263,"rel":127264},"https://gridsome.org/plugins",[681],[22,127266,127267],{},[653,127268],{"alt":57,"src":127269},"/images/blog/2019/05/02/2019-05-02_09-58-34-6c0295ad-4e1e-46d3-9e9d-368d24373694.png",[22,127271,127272],{},"At this point I would create a new Github (or your favorite Git Host) Repository to store your plugin code in. After you create the Github Repository you can follow the instructions to make the initial commit and add your remote origin.",[52,127274,127276],{"className":1663,"code":127275,"language":1665,"meta":57,"style":57},"git init\ngit add .\ngit commit -m \"initial commit\"\ngit remote add origin https://github.com/danvega/gridsome-plugin-remark-codesandbox.git\ngit push -u origin master\n",[59,127277,127278,127285,127294,127307,127322],{"__ignoreMap":57},[62,127279,127280,127283],{"class":64,"line":65},[62,127281,127282],{"class":122},"git",[62,127284,32775],{"class":1675},[62,127286,127287,127289,127291],{"class":64,"line":76},[62,127288,127282],{"class":122},[62,127290,28863],{"class":1675},[62,127292,127293],{"class":1675}," .\n",[62,127295,127296,127298,127301,127304],{"class":64,"line":82},[62,127297,127282],{"class":122},[62,127299,127300],{"class":1675}," commit",[62,127302,127303],{"class":149}," -m",[62,127305,127306],{"class":1675}," \"initial commit\"\n",[62,127308,127309,127311,127314,127316,127319],{"class":64,"line":89},[62,127310,127282],{"class":122},[62,127312,127313],{"class":1675}," remote",[62,127315,28863],{"class":1675},[62,127317,127318],{"class":1675}," origin",[62,127320,127321],{"class":1675}," https://github.com/danvega/gridsome-plugin-remark-codesandbox.git\n",[62,127323,127324,127326,127329,127332,127334],{"class":64,"line":95},[62,127325,127282],{"class":122},[62,127327,127328],{"class":1675}," push",[62,127330,127331],{"class":149}," -u",[62,127333,127318],{"class":1675},[62,127335,127336],{"class":1675}," master\n",[636,127338,127340],{"id":127339},"gridsome-markdown-remark-processing","Gridsome Markdown Remark Processing",[22,127342,127343],{},"Before you dive into the code it's good to have a plan of how you want your Markdown structured. When I want to embed a CodeSandbox I am just going to add the embed link on it's own line (paragraph). This is probably the easiest way to handle it because the parser can make its way through paragraphs and find links.",[52,127345,127347],{"className":29690,"code":127346,"language":29692,"meta":57,"style":57},"# CodeSandbox Embed Demo\n\nThis is a really cool sortable demo\n\nhttps://codesandbox.io/embed/wq3o75v4qk?fontsize=12\n",[59,127348,127349,127354,127358,127363,127367],{"__ignoreMap":57},[62,127350,127351],{"class":64,"line":65},[62,127352,127353],{},"# CodeSandbox Embed Demo\n",[62,127355,127356],{"class":64,"line":76},[62,127357,79],{"emptyLinePlaceholder":13},[62,127359,127360],{"class":64,"line":82},[62,127361,127362],{},"This is a really cool sortable demo\n",[62,127364,127365],{"class":64,"line":89},[62,127366,79],{"emptyLinePlaceholder":13},[62,127368,127369],{"class":64,"line":95},[62,127370,127371],{},"https://codesandbox.io/embed/wq3o75v4qk?fontsize=12\n",[22,127373,127374,127375,127378,127379,127382,127383,2755],{},"With all the infrastructure setup It's time to write some code. You can start by creating ",[59,127376,127377],{},"index.js"," in the root of your project. This project will only have a single dependency and you need to install it now by running the command ",[59,127380,127381],{},"npm install unist-util-visit"," and then requiring it at the top of ",[59,127384,127377],{},[52,127386,127388],{"className":32791,"code":127387,"language":32793,"meta":57,"style":57},"const visit = require(\"unist-util-visit\");\n",[59,127389,127390],{"__ignoreMap":57},[62,127391,127392,127394,127397,127399,127401,127403,127406],{"class":64,"line":65},[62,127393,110541],{"class":68},[62,127395,127396],{"class":149}," visit",[62,127398,2556],{"class":68},[62,127400,120735],{"class":122},[62,127402,2109],{"class":72},[62,127404,127405],{"class":1675},"\"unist-util-visit\"",[62,127407,1133],{"class":72},[22,127409,3521,127410,127415,127416,127419],{},[677,127411,127414],{"href":127412,"rel":127413},"https://www.npmjs.com/package/unist-util-visit",[681],"unist-util-visit"," package does all the heavy lifting for us and is really helpful for working with remark. Within the visit function we are moving down the tree looking for paragraphs. This means that the embed code needs to be on it's own line in it's own paragraph which is exactly how I want it. Next you will call a method called ",[59,127417,127418],{},"isCodeSandboxLink"," and pass it the current node. Each time you find one you will add it to the nodes array so that you can process them later.",[52,127421,127423],{"className":32791,"code":127422,"language":32793,"meta":57,"style":57},"const visit = require(\"unist-util-visit\");\n\nmodule.exports = options => {\n const debug = options.debug ? console.log : () => {};\n\n return tree => {\n const nodes = [];\n\n visit(tree, \"paragraph\", node => {\n debug(node);\n if (isCodeSandboxLink(node)) {\n debug(`\\nfound codesandbox link`, node.children[0].url);\n nodes.push([node, node.children[0].url]);\n }\n });\n };\n};\n",[59,127424,127425,127441,127445,127462,127488,127492,127503,127515,127519,127538,127546,127557,127579,127594,127598,127602,127606],{"__ignoreMap":57},[62,127426,127427,127429,127431,127433,127435,127437,127439],{"class":64,"line":65},[62,127428,110541],{"class":68},[62,127430,127396],{"class":149},[62,127432,2556],{"class":68},[62,127434,120735],{"class":122},[62,127436,2109],{"class":72},[62,127438,127405],{"class":1675},[62,127440,1133],{"class":72},[62,127442,127443],{"class":64,"line":76},[62,127444,79],{"emptyLinePlaceholder":13},[62,127446,127447,127449,127451,127453,127455,127458,127460],{"class":64,"line":82},[62,127448,32813],{"class":149},[62,127450,2755],{"class":72},[62,127452,32818],{"class":149},[62,127454,2556],{"class":68},[62,127456,127457],{"class":889}," options",[62,127459,85402],{"class":68},[62,127461,126],{"class":72},[62,127463,127464,127466,127469,127471,127474,127476,127479,127481,127484,127486],{"class":64,"line":89},[62,127465,85414],{"class":68},[62,127467,127468],{"class":149}," debug",[62,127470,2556],{"class":68},[62,127472,127473],{"class":72}," options.debug ",[62,127475,5668],{"class":68},[62,127477,127478],{"class":72}," console.log ",[62,127480,1266],{"class":68},[62,127482,127483],{"class":72}," () ",[62,127485,21525],{"class":68},[62,127487,59540],{"class":72},[62,127489,127490],{"class":64,"line":95},[62,127491,79],{"emptyLinePlaceholder":13},[62,127493,127494,127496,127499,127501],{"class":64,"line":101},[62,127495,82091],{"class":68},[62,127497,127498],{"class":889}," tree",[62,127500,85402],{"class":68},[62,127502,126],{"class":72},[62,127504,127505,127507,127510,127512],{"class":64,"line":107},[62,127506,36857],{"class":68},[62,127508,127509],{"class":149}," nodes",[62,127511,2556],{"class":68},[62,127513,127514],{"class":72}," [];\n",[62,127516,127517],{"class":64,"line":113},[62,127518,79],{"emptyLinePlaceholder":13},[62,127520,127521,127524,127527,127530,127532,127534,127536],{"class":64,"line":129},[62,127522,127523],{"class":122}," visit",[62,127525,127526],{"class":72},"(tree, ",[62,127528,127529],{"class":1675},"\"paragraph\"",[62,127531,976],{"class":72},[62,127533,32634],{"class":889},[62,127535,85402],{"class":68},[62,127537,126],{"class":72},[62,127539,127540,127543],{"class":64,"line":134},[62,127541,127542],{"class":122}," debug",[62,127544,127545],{"class":72},"(node);\n",[62,127547,127548,127550,127552,127554],{"class":64,"line":156},[62,127549,29011],{"class":68},[62,127551,744],{"class":72},[62,127553,127418],{"class":122},[62,127555,127556],{"class":72},"(node)) {\n",[62,127558,127559,127562,127564,127566,127568,127571,127574,127576],{"class":64,"line":161},[62,127560,127561],{"class":122}," debug",[62,127563,2109],{"class":72},[62,127565,31872],{"class":1675},[62,127567,4501],{"class":149},[62,127569,127570],{"class":1675},"found codesandbox link`",[62,127572,127573],{"class":72},", node.children[",[62,127575,1130],{"class":149},[62,127577,127578],{"class":72},"].url);\n",[62,127580,127581,127584,127586,127589,127591],{"class":64,"line":167},[62,127582,127583],{"class":72}," nodes.",[62,127585,5946],{"class":122},[62,127587,127588],{"class":72},"([node, node.children[",[62,127590,1130],{"class":149},[62,127592,127593],{"class":72},"].url]);\n",[62,127595,127596],{"class":64,"line":173},[62,127597,29042],{"class":72},[62,127599,127600],{"class":64,"line":179},[62,127601,906],{"class":72},[62,127603,127604],{"class":64,"line":185},[62,127605,82135],{"class":72},[62,127607,127608],{"class":64,"line":191},[62,127609,107354],{"class":72},[22,127611,3521,127612,127614],{},[59,127613,127418],{}," function checks a couple of things",[915,127616,127617,127620,127623],{},[37,127618,127619],{},"The embed link should be on its own line by itself.",[37,127621,127622],{},"It should be a link so just putting an id there won't work.",[37,127624,127625],{},"It matches the Regular Expression defined to match an embed link.",[52,127627,127629],{"className":32791,"code":127628,"language":32793,"meta":57,"style":57},"const codeSandboxRegexp = /https:\\/\\/codesandbox\\.io\\/embed\\/.\\*/;\n\nconst isCodeSandboxLink = node => {\n return (\n node.children.length === 1 &&\n node.children[0].type === \"link\" &&\n codeSandboxRegexp.test(node.children[0].url)\n );\n};\n",[59,127630,127631,127672,127676,127692,127698,127713,127730,127745,127749],{"__ignoreMap":57},[62,127632,127633,127635,127638,127640,127642,127645,127648,127650,127653,127656,127659,127662,127664,127666,127668,127670],{"class":64,"line":65},[62,127634,110541],{"class":68},[62,127636,127637],{"class":149}," codeSandboxRegexp",[62,127639,2556],{"class":68},[62,127641,118452],{"class":1675},[62,127643,127644],{"class":73988},"https:",[62,127646,127647],{"class":73985},"\\/\\/",[62,127649,124428],{"class":73988},[62,127651,127652],{"class":73985},"\\.",[62,127654,127655],{"class":73988},"io",[62,127657,127658],{"class":73985},"\\/",[62,127660,127661],{"class":73988},"embed",[62,127663,127658],{"class":73985},[62,127665,2755],{"class":149},[62,127667,66539],{"class":73985},[62,127669,6936],{"class":1675},[62,127671,153],{"class":72},[62,127673,127674],{"class":64,"line":76},[62,127675,79],{"emptyLinePlaceholder":13},[62,127677,127678,127680,127683,127685,127688,127690],{"class":64,"line":82},[62,127679,110541],{"class":68},[62,127681,127682],{"class":122}," isCodeSandboxLink",[62,127684,2556],{"class":68},[62,127686,127687],{"class":889}," node",[62,127689,85402],{"class":68},[62,127691,126],{"class":72},[62,127693,127694,127696],{"class":64,"line":89},[62,127695,82091],{"class":68},[62,127697,36931],{"class":72},[62,127699,127700,127703,127705,127708,127710],{"class":64,"line":95},[62,127701,127702],{"class":72}," node.children.",[62,127704,14193],{"class":149},[62,127706,127707],{"class":68}," ===",[62,127709,22038],{"class":149},[62,127711,127712],{"class":68}," &&\n",[62,127714,127715,127718,127720,127723,127725,127728],{"class":64,"line":101},[62,127716,127717],{"class":72}," node.children[",[62,127719,1130],{"class":149},[62,127721,127722],{"class":72},"].type ",[62,127724,21072],{"class":68},[62,127726,127727],{"class":1675}," \"link\"",[62,127729,127712],{"class":68},[62,127731,127732,127735,127737,127740,127742],{"class":64,"line":107},[62,127733,127734],{"class":72}," codeSandboxRegexp.",[62,127736,28137],{"class":122},[62,127738,127739],{"class":72},"(node.children[",[62,127741,1130],{"class":149},[62,127743,127744],{"class":72},"].url)\n",[62,127746,127747],{"class":64,"line":113},[62,127748,85526],{"class":72},[62,127750,127751],{"class":64,"line":129},[62,127752,107354],{"class":72},[22,127754,127755],{},"Now that you have an array of all CodeSandbox links in your Markdown file you need to process them. You will do this right after your visit function call and it looks something like this:",[52,127757,127759],{"className":32791,"code":127758,"language":32793,"meta":57,"style":57},"for (let i = 0; i \u003C nodes.length; i++) {\n const nt = nodes[i];\n const node = nt[0];\n const csLink = nt[1];\n debug(`\\nembeding codesandbox: ${csLink}`);\n try {\n const csEmbed = getEmbeddedCodeSandbox(csLink);\n node.type = \"html\";\n node.value = csEmbed;\n } catch (err) {\n debug(`\\nfailed to get iframe for ${csLink}\\n`, er);\n }\n}\n",[59,127760,127761,127790,127802,127817,127832,127853,127860,127875,127887,127897,127906,127931,127935],{"__ignoreMap":57},[62,127762,127763,127765,127767,127769,127771,127773,127775,127777,127779,127782,127784,127786,127788],{"class":64,"line":65},[62,127764,741],{"class":68},[62,127766,744],{"class":72},[62,127768,21958],{"class":68},[62,127770,750],{"class":72},[62,127772,146],{"class":68},[62,127774,150],{"class":149},[62,127776,757],{"class":72},[62,127778,760],{"class":68},[62,127780,127781],{"class":72}," nodes.",[62,127783,14193],{"class":149},[62,127785,75623],{"class":72},[62,127787,215],{"class":68},[62,127789,768],{"class":72},[62,127791,127792,127794,127797,127799],{"class":64,"line":76},[62,127793,85414],{"class":68},[62,127795,127796],{"class":149}," nt",[62,127798,2556],{"class":68},[62,127800,127801],{"class":72}," nodes[i];\n",[62,127803,127804,127806,127808,127810,127813,127815],{"class":64,"line":82},[62,127805,85414],{"class":68},[62,127807,127687],{"class":149},[62,127809,2556],{"class":68},[62,127811,127812],{"class":72}," nt[",[62,127814,1130],{"class":149},[62,127816,109215],{"class":72},[62,127818,127819,127821,127824,127826,127828,127830],{"class":64,"line":89},[62,127820,85414],{"class":68},[62,127822,127823],{"class":149}," csLink",[62,127825,2556],{"class":68},[62,127827,127812],{"class":72},[62,127829,6689],{"class":149},[62,127831,109215],{"class":72},[62,127833,127834,127837,127839,127841,127843,127846,127849,127851],{"class":64,"line":95},[62,127835,127836],{"class":122}," debug",[62,127838,2109],{"class":72},[62,127840,31872],{"class":1675},[62,127842,4501],{"class":149},[62,127844,127845],{"class":1675},"embeding codesandbox: ${",[62,127847,127848],{"class":72},"csLink",[62,127850,21727],{"class":1675},[62,127852,1133],{"class":72},[62,127854,127855,127858],{"class":64,"line":101},[62,127856,127857],{"class":68}," try",[62,127859,126],{"class":72},[62,127861,127862,127864,127867,127869,127872],{"class":64,"line":107},[62,127863,36857],{"class":68},[62,127865,127866],{"class":149}," csEmbed",[62,127868,2556],{"class":68},[62,127870,127871],{"class":122}," getEmbeddedCodeSandbox",[62,127873,127874],{"class":72},"(csLink);\n",[62,127876,127877,127880,127882,127885],{"class":64,"line":113},[62,127878,127879],{"class":72}," node.type ",[62,127881,146],{"class":68},[62,127883,127884],{"class":1675}," \"html\"",[62,127886,153],{"class":72},[62,127888,127889,127892,127894],{"class":64,"line":129},[62,127890,127891],{"class":72}," node.value ",[62,127893,146],{"class":68},[62,127895,127896],{"class":72}," csEmbed;\n",[62,127898,127899,127901,127903],{"class":64,"line":134},[62,127900,107602],{"class":72},[62,127902,883],{"class":68},[62,127904,127905],{"class":72}," (err) {\n",[62,127907,127908,127911,127913,127915,127917,127920,127922,127924,127926,127928],{"class":64,"line":156},[62,127909,127910],{"class":122}," debug",[62,127912,2109],{"class":72},[62,127914,31872],{"class":1675},[62,127916,4501],{"class":149},[62,127918,127919],{"class":1675},"failed to get iframe for ${",[62,127921,127848],{"class":72},[62,127923,90795],{"class":1675},[62,127925,4501],{"class":149},[62,127927,31872],{"class":1675},[62,127929,127930],{"class":72},", er);\n",[62,127932,127933],{"class":64,"line":161},[62,127934,3731],{"class":72},[62,127936,127937],{"class":64,"line":167},[62,127938,379],{"class":72},[22,127940,127941,127942,2755],{},"Finally you will need to return the HTML needed to embed it using an ",[59,127943,104354],{},[52,127945,127947],{"className":32791,"code":127946,"language":32793,"meta":57,"style":57},"const getEmbeddedCodeSandbox = link => {\n return `\u003Ciframe src=\"${link}\" style=\"width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;\" sandbox=\"allow-modals allow-forms allow-popups allow-scripts allow-same-origin\">\u003C/iframe>`;\n};\n",[59,127948,127949,127964,127978],{"__ignoreMap":57},[62,127950,127951,127953,127955,127957,127960,127962],{"class":64,"line":65},[62,127952,110541],{"class":68},[62,127954,127871],{"class":122},[62,127956,2556],{"class":68},[62,127958,127959],{"class":889}," link",[62,127961,85402],{"class":68},[62,127963,126],{"class":72},[62,127965,127966,127968,127971,127973,127976],{"class":64,"line":76},[62,127967,82091],{"class":68},[62,127969,127970],{"class":1675}," `\u003Ciframe src=\"${",[62,127972,33106],{"class":72},[62,127974,127975],{"class":1675},"}\" style=\"width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;\" sandbox=\"allow-modals allow-forms allow-popups allow-scripts allow-same-origin\">\u003C/iframe>`",[62,127977,153],{"class":72},[62,127979,127980],{"class":64,"line":82},[62,127981,107354],{"class":72},[22,127983,127984],{},"What I really like about this approach is you can customize each embed with it's own options.",[52,127986,127988],{"className":29690,"code":127987,"language":29692,"meta":57,"style":57},"// smaller font\nhttps://codesandbox.io/embed/wq3o75v4qk?fontsize=11\n\n// different view\nhttps://codesandbox.io/embed/wq3o75v4qk?fontsize=14&view=editor\n",[59,127989,127990,127995,128000,128004,128009],{"__ignoreMap":57},[62,127991,127992],{"class":64,"line":65},[62,127993,127994],{},"// smaller font\n",[62,127996,127997],{"class":64,"line":76},[62,127998,127999],{},"https://codesandbox.io/embed/wq3o75v4qk?fontsize=11\n",[62,128001,128002],{"class":64,"line":82},[62,128003,79],{"emptyLinePlaceholder":13},[62,128005,128006],{"class":64,"line":89},[62,128007,128008],{},"// different view\n",[62,128010,128011],{"class":64,"line":95},[62,128012,128013],{},"https://codesandbox.io/embed/wq3o75v4qk?fontsize=14&view=editor\n",[22,128015,128016,128017,128022],{},"If you're curious what options are available you can check out the ",[677,128018,128021],{"href":128019,"rel":128020},"https://codesandbox.io/docs/embedding#embed-options",[681],"CodeSandbox documentation",". If you're following along you should end up with a solution that looks something like this.",[52,128024,128026],{"className":32791,"code":128025,"language":32793,"meta":57,"style":57},"const visit = require(\"unist-util-visit\");\n\nconst codeSandboxRegexp = /https:\\/\\/codesandbox\\.io\\/embed\\/.*/;\n\nconst isCodeSandboxLink = node => {\n return (\n node.children.length === 1 &&\n node.children[0].type === \"link\" &&\n codeSandboxRegexp.test(node.children[0].url)\n );\n};\n\nconst getEmbeddedCodeSandbox = link => {\n return `\u003Ciframe src=\"${link}\" style=\"width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;\" sandbox=\"allow-modals allow-forms allow-popups allow-scripts allow-same-origin\">\u003C/iframe>`;\n};\n\nmodule.exports = options => {\n const debug = options.debug ? console.log : () => {};\n\n return tree => {\n const nodes = [];\n\n visit(tree, \"paragraph\", node => {\n debug(node);\n if (isCodeSandboxLink(node)) {\n debug(`\\nfound codesandbox link`, node.children[0].url);\n nodes.push([node, node.children[0].url]);\n }\n });\n\n for (let i = 0; i \u003C nodes.length; i++) {\n const nt = nodes[i];\n const node = nt[0];\n const csLink = nt[1];\n debug(`\\nembeding codesandbox: ${csLink}`);\n try {\n const csEmbed = getEmbeddedCodeSandbox(csLink);\n node.type = \"html\";\n node.value = csEmbed;\n } catch (err) {\n debug(`\\nfailed to get iframe for ${csLink}\\n`, er);\n }\n }\n };\n};\n",[59,128027,128028,128044,128048,128082,128086,128100,128106,128118,128132,128144,128148,128152,128156,128170,128182,128186,128190,128206,128228,128232,128242,128252,128256,128272,128278,128288,128306,128318,128322,128326,128330,128359,128369,128383,128397,128415,128422,128434,128445,128454,128463,128485,128489,128493,128497],{"__ignoreMap":57},[62,128029,128030,128032,128034,128036,128038,128040,128042],{"class":64,"line":65},[62,128031,110541],{"class":68},[62,128033,127396],{"class":149},[62,128035,2556],{"class":68},[62,128037,120735],{"class":122},[62,128039,2109],{"class":72},[62,128041,127405],{"class":1675},[62,128043,1133],{"class":72},[62,128045,128046],{"class":64,"line":76},[62,128047,79],{"emptyLinePlaceholder":13},[62,128049,128050,128052,128054,128056,128058,128060,128062,128064,128066,128068,128070,128072,128074,128076,128078,128080],{"class":64,"line":82},[62,128051,110541],{"class":68},[62,128053,127637],{"class":149},[62,128055,2556],{"class":68},[62,128057,118452],{"class":1675},[62,128059,127644],{"class":73988},[62,128061,127647],{"class":73985},[62,128063,124428],{"class":73988},[62,128065,127652],{"class":73985},[62,128067,127655],{"class":73988},[62,128069,127658],{"class":73985},[62,128071,127661],{"class":73988},[62,128073,127658],{"class":73985},[62,128075,2755],{"class":149},[62,128077,6973],{"class":68},[62,128079,6936],{"class":1675},[62,128081,153],{"class":72},[62,128083,128084],{"class":64,"line":89},[62,128085,79],{"emptyLinePlaceholder":13},[62,128087,128088,128090,128092,128094,128096,128098],{"class":64,"line":95},[62,128089,110541],{"class":68},[62,128091,127682],{"class":122},[62,128093,2556],{"class":68},[62,128095,127687],{"class":889},[62,128097,85402],{"class":68},[62,128099,126],{"class":72},[62,128101,128102,128104],{"class":64,"line":101},[62,128103,82091],{"class":68},[62,128105,36931],{"class":72},[62,128107,128108,128110,128112,128114,128116],{"class":64,"line":107},[62,128109,127702],{"class":72},[62,128111,14193],{"class":149},[62,128113,127707],{"class":68},[62,128115,22038],{"class":149},[62,128117,127712],{"class":68},[62,128119,128120,128122,128124,128126,128128,128130],{"class":64,"line":113},[62,128121,127717],{"class":72},[62,128123,1130],{"class":149},[62,128125,127722],{"class":72},[62,128127,21072],{"class":68},[62,128129,127727],{"class":1675},[62,128131,127712],{"class":68},[62,128133,128134,128136,128138,128140,128142],{"class":64,"line":129},[62,128135,127734],{"class":72},[62,128137,28137],{"class":122},[62,128139,127739],{"class":72},[62,128141,1130],{"class":149},[62,128143,127744],{"class":72},[62,128145,128146],{"class":64,"line":134},[62,128147,85526],{"class":72},[62,128149,128150],{"class":64,"line":156},[62,128151,107354],{"class":72},[62,128153,128154],{"class":64,"line":161},[62,128155,79],{"emptyLinePlaceholder":13},[62,128157,128158,128160,128162,128164,128166,128168],{"class":64,"line":167},[62,128159,110541],{"class":68},[62,128161,127871],{"class":122},[62,128163,2556],{"class":68},[62,128165,127959],{"class":889},[62,128167,85402],{"class":68},[62,128169,126],{"class":72},[62,128171,128172,128174,128176,128178,128180],{"class":64,"line":173},[62,128173,82091],{"class":68},[62,128175,127970],{"class":1675},[62,128177,33106],{"class":72},[62,128179,127975],{"class":1675},[62,128181,153],{"class":72},[62,128183,128184],{"class":64,"line":179},[62,128185,107354],{"class":72},[62,128187,128188],{"class":64,"line":185},[62,128189,79],{"emptyLinePlaceholder":13},[62,128191,128192,128194,128196,128198,128200,128202,128204],{"class":64,"line":191},[62,128193,32813],{"class":149},[62,128195,2755],{"class":72},[62,128197,32818],{"class":149},[62,128199,2556],{"class":68},[62,128201,127457],{"class":889},[62,128203,85402],{"class":68},[62,128205,126],{"class":72},[62,128207,128208,128210,128212,128214,128216,128218,128220,128222,128224,128226],{"class":64,"line":209},[62,128209,85414],{"class":68},[62,128211,127468],{"class":149},[62,128213,2556],{"class":68},[62,128215,127473],{"class":72},[62,128217,5668],{"class":68},[62,128219,127478],{"class":72},[62,128221,1266],{"class":68},[62,128223,127483],{"class":72},[62,128225,21525],{"class":68},[62,128227,59540],{"class":72},[62,128229,128230],{"class":64,"line":220},[62,128231,79],{"emptyLinePlaceholder":13},[62,128233,128234,128236,128238,128240],{"class":64,"line":226},[62,128235,82091],{"class":68},[62,128237,127498],{"class":889},[62,128239,85402],{"class":68},[62,128241,126],{"class":72},[62,128243,128244,128246,128248,128250],{"class":64,"line":231},[62,128245,36857],{"class":68},[62,128247,127509],{"class":149},[62,128249,2556],{"class":68},[62,128251,127514],{"class":72},[62,128253,128254],{"class":64,"line":236},[62,128255,79],{"emptyLinePlaceholder":13},[62,128257,128258,128260,128262,128264,128266,128268,128270],{"class":64,"line":242},[62,128259,127523],{"class":122},[62,128261,127526],{"class":72},[62,128263,127529],{"class":1675},[62,128265,976],{"class":72},[62,128267,32634],{"class":889},[62,128269,85402],{"class":68},[62,128271,126],{"class":72},[62,128273,128274,128276],{"class":64,"line":247},[62,128275,127542],{"class":122},[62,128277,127545],{"class":72},[62,128279,128280,128282,128284,128286],{"class":64,"line":252},[62,128281,29011],{"class":68},[62,128283,744],{"class":72},[62,128285,127418],{"class":122},[62,128287,127556],{"class":72},[62,128289,128290,128292,128294,128296,128298,128300,128302,128304],{"class":64,"line":257},[62,128291,127561],{"class":122},[62,128293,2109],{"class":72},[62,128295,31872],{"class":1675},[62,128297,4501],{"class":149},[62,128299,127570],{"class":1675},[62,128301,127573],{"class":72},[62,128303,1130],{"class":149},[62,128305,127578],{"class":72},[62,128307,128308,128310,128312,128314,128316],{"class":64,"line":271},[62,128309,127583],{"class":72},[62,128311,5946],{"class":122},[62,128313,127588],{"class":72},[62,128315,1130],{"class":149},[62,128317,127593],{"class":72},[62,128319,128320],{"class":64,"line":281},[62,128321,29042],{"class":72},[62,128323,128324],{"class":64,"line":286},[62,128325,906],{"class":72},[62,128327,128328],{"class":64,"line":291},[62,128329,79],{"emptyLinePlaceholder":13},[62,128331,128332,128335,128337,128339,128341,128343,128345,128347,128349,128351,128353,128355,128357],{"class":64,"line":296},[62,128333,128334],{"class":68}," for",[62,128336,744],{"class":72},[62,128338,21958],{"class":68},[62,128340,750],{"class":72},[62,128342,146],{"class":68},[62,128344,150],{"class":149},[62,128346,757],{"class":72},[62,128348,760],{"class":68},[62,128350,127781],{"class":72},[62,128352,14193],{"class":149},[62,128354,75623],{"class":72},[62,128356,215],{"class":68},[62,128358,768],{"class":72},[62,128360,128361,128363,128365,128367],{"class":64,"line":302},[62,128362,115079],{"class":68},[62,128364,127796],{"class":149},[62,128366,2556],{"class":68},[62,128368,127801],{"class":72},[62,128370,128371,128373,128375,128377,128379,128381],{"class":64,"line":308},[62,128372,115079],{"class":68},[62,128374,127687],{"class":149},[62,128376,2556],{"class":68},[62,128378,127812],{"class":72},[62,128380,1130],{"class":149},[62,128382,109215],{"class":72},[62,128384,128385,128387,128389,128391,128393,128395],{"class":64,"line":314},[62,128386,115079],{"class":68},[62,128388,127823],{"class":149},[62,128390,2556],{"class":68},[62,128392,127812],{"class":72},[62,128394,6689],{"class":149},[62,128396,109215],{"class":72},[62,128398,128399,128401,128403,128405,128407,128409,128411,128413],{"class":64,"line":320},[62,128400,127542],{"class":122},[62,128402,2109],{"class":72},[62,128404,31872],{"class":1675},[62,128406,4501],{"class":149},[62,128408,127845],{"class":1675},[62,128410,127848],{"class":72},[62,128412,21727],{"class":1675},[62,128414,1133],{"class":72},[62,128416,128417,128420],{"class":64,"line":326},[62,128418,128419],{"class":68}," try",[62,128421,126],{"class":72},[62,128423,128424,128426,128428,128430,128432],{"class":64,"line":338},[62,128425,21437],{"class":68},[62,128427,127866],{"class":149},[62,128429,2556],{"class":68},[62,128431,127871],{"class":122},[62,128433,127874],{"class":72},[62,128435,128436,128439,128441,128443],{"class":64,"line":343},[62,128437,128438],{"class":72}," node.type ",[62,128440,146],{"class":68},[62,128442,127884],{"class":1675},[62,128444,153],{"class":72},[62,128446,128447,128450,128452],{"class":64,"line":357},[62,128448,128449],{"class":72}," node.value ",[62,128451,146],{"class":68},[62,128453,127896],{"class":72},[62,128455,128456,128459,128461],{"class":64,"line":366},[62,128457,128458],{"class":72}," } ",[62,128460,883],{"class":68},[62,128462,127905],{"class":72},[62,128464,128465,128467,128469,128471,128473,128475,128477,128479,128481,128483],{"class":64,"line":371},[62,128466,127561],{"class":122},[62,128468,2109],{"class":72},[62,128470,31872],{"class":1675},[62,128472,4501],{"class":149},[62,128474,127919],{"class":1675},[62,128476,127848],{"class":72},[62,128478,90795],{"class":1675},[62,128480,4501],{"class":149},[62,128482,31872],{"class":1675},[62,128484,127930],{"class":72},[62,128486,128487],{"class":64,"line":376},[62,128488,29042],{"class":72},[62,128490,128491],{"class":64,"line":16333},[62,128492,223],{"class":72},[62,128494,128495],{"class":64,"line":16349},[62,128496,82135],{"class":72},[62,128498,128499],{"class":64,"line":16365},[62,128500,107354],{"class":72},[636,128502,128504],{"id":128503},"npm-package-testing-and-publishing","NPM Package Testing and Publishing",[22,128506,128507,128508,128513],{},"I don't want too much time on this because I actually wrote a whole article title \"",[677,128509,128512],{"href":128510,"rel":128511},"https://www.danvega.dev/blog/2019/02/10/creating-your-first-npm-package",[681],"Creating your first npm package","\" that goes through all of this but I do want to mention it.",[22,128515,128516,128517,128520],{},"When you're developing the plugin you need a way to test it without installing it from NPM because it isn't there yet. In the plugin project you can run the command ",[59,128518,128519],{},"npm link"," which will take your package and create a symbolic link in the npm global folder to it.",[52,128522,128524],{"className":1663,"code":128523,"language":1665,"meta":57,"style":57},"/Users/vega/.nvm/versions/node/v10.12.0/lib/node_modules/gridsome-plugin-remark-codesandbox ->\n/Users/vega/dev/npm/gridsome-plugin-remark-codesandbox\n",[59,128525,128526,128536],{"__ignoreMap":57},[62,128527,128528,128531,128534],{"class":64,"line":65},[62,128529,128530],{"class":122},"/Users/vega/.nvm/versions/node/v10.12.0/lib/node_modules/gridsome-plugin-remark-codesandbox",[62,128532,128533],{"class":72}," -",[62,128535,1784],{"class":68},[62,128537,128538],{"class":64,"line":76},[62,128539,128540],{"class":122},"/Users/vega/dev/npm/gridsome-plugin-remark-codesandbox\n",[22,128542,128543],{},"Then from the project that you wish to use it in (for me it was my Gridsome blog) run the following command:",[52,128545,128548],{"className":128546,"code":128547,"language":1727},[1725],"npm link gridsome-plugin-remark-codesandbox\n",[59,128549,128547],{"__ignoreMap":57},[22,128551,128552,128553,128555,128556,128559,128560,128565],{},"Which will add it to your ",[59,128554,112220],{}," folder and you are ready to go. When the plugin is ready to go you can publish it using ",[59,128557,128558],{},"npm publish",". Once the ",[677,128561,128564],{"href":128562,"rel":128563},"https://www.npmjs.com/package/gridsome-plugin-remark-codesandbox",[681],"package is on NPM"," you can install it just like any other package:",[52,128567,128569],{"className":1663,"code":128568,"language":1665,"meta":57,"style":57},"npm install gridsome-plugin-remark-codesandbox\n",[59,128570,128571],{"__ignoreMap":57},[62,128572,128573,128575,128577],{"class":64,"line":65},[62,128574,32645],{"class":122},[62,128576,32750],{"class":1675},[62,128578,128579],{"class":1675}," gridsome-plugin-remark-codesandbox\n",[636,128581,128583],{"id":128582},"using-the-plugin-in-gridsome","Using the plugin in Gridsome",[22,128585,128586],{},"However you have the plugin installed in your project there is one more step to making it work. Where you are defining your remark plugins you need to add this one to the plugins array:",[52,128588,128590],{"className":32791,"code":128589,"language":32793,"meta":57,"style":57},"plugins: [{\n use: '@gridsome/source-filesystem',\n options: {\n path: 'blog/**/*.md',\n typeName: 'Post',\n route: '/blog/:year/:month/:day/:slug',\n refs: {\n tags: {\n typeName: 'Tag',\n route: '/tag/:slug',\n create: true\n }\n },\n resolveAbsolutePaths: true,\n remark: {\n autolinkClassName: 'fas fa-hashtag',\n externalLinksTarget: '_blank',\n externalLinksRel: ['nofollow', 'noopener', 'noreferrer'],\n plugins: [\n ['gridsome-plugin-remark-shiki', {\n theme: 'nord'\n }],\n ['gridsome-plugin-remark-twitter', {}],\n ['gridsome-plugin-remark-codesandbox', {}]\n ]\n }\n }\n},\n",[59,128591,128592,128599,128609,128614,128624,128633,128643,128648,128653,128663,128673,128680,128684,128688,128697,128702,128712,128722,128742,128747,128757,128765,128770,128780,128790,128795,128799,128803],{"__ignoreMap":57},[62,128593,128594,128596],{"class":64,"line":65},[62,128595,92866],{"class":122},[62,128597,128598],{"class":72},": [{\n",[62,128600,128601,128604,128607],{"class":64,"line":76},[62,128602,128603],{"class":72}," use: ",[62,128605,128606],{"class":1675},"'@gridsome/source-filesystem'",[62,128608,3338],{"class":72},[62,128610,128611],{"class":64,"line":82},[62,128612,128613],{"class":72}," options: {\n",[62,128615,128616,128619,128622],{"class":64,"line":89},[62,128617,128618],{"class":72}," path: ",[62,128620,128621],{"class":1675},"'blog/**/*.md'",[62,128623,3338],{"class":72},[62,128625,128626,128629,128631],{"class":64,"line":95},[62,128627,128628],{"class":72}," typeName: ",[62,128630,120891],{"class":1675},[62,128632,3338],{"class":72},[62,128634,128635,128638,128641],{"class":64,"line":101},[62,128636,128637],{"class":72}," route: ",[62,128639,128640],{"class":1675},"'/blog/:year/:month/:day/:slug'",[62,128642,3338],{"class":72},[62,128644,128645],{"class":64,"line":107},[62,128646,128647],{"class":72}," refs: {\n",[62,128649,128650],{"class":64,"line":113},[62,128651,128652],{"class":72}," tags: {\n",[62,128654,128655,128658,128661],{"class":64,"line":129},[62,128656,128657],{"class":72}," typeName: ",[62,128659,128660],{"class":1675},"'Tag'",[62,128662,3338],{"class":72},[62,128664,128665,128668,128671],{"class":64,"line":134},[62,128666,128667],{"class":72}," route: ",[62,128669,128670],{"class":1675},"'/tag/:slug'",[62,128672,3338],{"class":72},[62,128674,128675,128678],{"class":64,"line":156},[62,128676,128677],{"class":72}," create: ",[62,128679,51914],{"class":149},[62,128681,128682],{"class":64,"line":161},[62,128683,29042],{"class":72},[62,128685,128686],{"class":64,"line":167},[62,128687,50124],{"class":72},[62,128689,128690,128693,128695],{"class":64,"line":173},[62,128691,128692],{"class":72}," resolveAbsolutePaths: ",[62,128694,21775],{"class":149},[62,128696,3338],{"class":72},[62,128698,128699],{"class":64,"line":179},[62,128700,128701],{"class":72}," remark: {\n",[62,128703,128704,128707,128710],{"class":64,"line":185},[62,128705,128706],{"class":72}," autolinkClassName: ",[62,128708,128709],{"class":1675},"'fas fa-hashtag'",[62,128711,3338],{"class":72},[62,128713,128714,128717,128720],{"class":64,"line":191},[62,128715,128716],{"class":72}," externalLinksTarget: ",[62,128718,128719],{"class":1675},"'_blank'",[62,128721,3338],{"class":72},[62,128723,128724,128727,128730,128732,128735,128737,128740],{"class":64,"line":209},[62,128725,128726],{"class":72}," externalLinksRel: [",[62,128728,128729],{"class":1675},"'nofollow'",[62,128731,976],{"class":72},[62,128733,128734],{"class":1675},"'noopener'",[62,128736,976],{"class":72},[62,128738,128739],{"class":1675},"'noreferrer'",[62,128741,32833],{"class":72},[62,128743,128744],{"class":64,"line":220},[62,128745,128746],{"class":72}," plugins: [\n",[62,128748,128749,128752,128755],{"class":64,"line":226},[62,128750,128751],{"class":72}," [",[62,128753,128754],{"class":1675},"'gridsome-plugin-remark-shiki'",[62,128756,107180],{"class":72},[62,128758,128759,128762],{"class":64,"line":231},[62,128760,128761],{"class":72}," theme: ",[62,128763,128764],{"class":1675},"'nord'\n",[62,128766,128767],{"class":64,"line":236},[62,128768,128769],{"class":72}," }],\n",[62,128771,128772,128774,128777],{"class":64,"line":242},[62,128773,128751],{"class":72},[62,128775,128776],{"class":1675},"'gridsome-plugin-remark-twitter'",[62,128778,128779],{"class":72},", {}],\n",[62,128781,128782,128784,128787],{"class":64,"line":247},[62,128783,128751],{"class":72},[62,128785,128786],{"class":1675},"'gridsome-plugin-remark-codesandbox'",[62,128788,128789],{"class":72},", {}]\n",[62,128791,128792],{"class":64,"line":252},[62,128793,128794],{"class":72}," ]\n",[62,128796,128797],{"class":64,"line":257},[62,128798,223],{"class":72},[62,128800,128801],{"class":64,"line":271},[62,128802,3731],{"class":72},[62,128804,128805],{"class":64,"line":281},[62,128806,72286],{"class":72},[26,128808,1499],{"id":1498},[22,128810,128811],{},"If you follow this blog at all you probably already know this but I love Gridsome and adding CodeSandbox shares to my Markdown really makes me happy. Are there any plugins that you would love to see added to Gridsome? What are you using CodeSandbox for? As always friends...",[22,128813,36004,128814,82545],{},[36006,128815],{},[1527,128817,128818],{},"html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .spoOn, html code.shiki .spoOn{--shiki-default:#E36209;--shiki-dark:#FFAB70;--shiki-sepia:#E36209}html pre.shiki code .sLcMm, html code.shiki .sLcMm{--shiki-default:#032F62;--shiki-dark:#DBEDFF;--shiki-sepia:#032F62}html pre.shiki code .sIS5D, html code.shiki .sIS5D{--shiki-default:#22863A;--shiki-default-font-weight:bold;--shiki-dark:#85E89D;--shiki-dark-font-weight:bold;--shiki-sepia:#22863A;--shiki-sepia-font-weight:bold}",{"title":57,"searchDepth":76,"depth":76,"links":128820},[128821,128825,128826,128831],{"id":126937,"depth":76,"text":126938,"children":128822},[128823,128824],{"id":126959,"depth":82,"text":126960},{"id":127014,"depth":82,"text":127015},{"id":127033,"depth":76,"text":127034},{"id":127062,"depth":76,"text":127063,"children":128827},[128828,128829,128830],{"id":127339,"depth":82,"text":127340},{"id":128503,"depth":82,"text":128504},{"id":128582,"depth":82,"text":128583},{"id":1498,"depth":76,"text":1499},{"_id":128833,"path":128834,"title":128835,"description":128836,"meta":128837,"body":128842},"content/blog/2019/04/30/up-and-running-with-vue.md","/blog/2019/04/30/up-and-running-with-vue","Everything you need to get up and running with VueJS","In this article, I am going to tell you why I love Vue and give you 4 different ways you can write your first application.",{"slug":128838,"date":128839,"published":13,"tags":128840,"author":17,"cover":128841,"excerpt":-1},"up-and-running-with-vue","2019-04-30T19:38:42.927Z",[105335,32793],"./up-and-running-with-vue-cover.png",{"type":19,"value":128843,"toc":130488},[128844,128851,128854,128857,128861,128864,128867,128878,128881,128884,128889,128896,128901,128908,128913,128920,128925,128932,128937,128944,128949,128952,128957,128978,128983,129006,129009,129012,129081,129084,129087,129092,129095,129098,129101,129105,129108,129111,129114,129118,129123,129255,129258,129299,129306,129309,129332,129335,129384,129387,129465,129468,129521,129530,129533,129538,129550,129556,129704,129707,129710,129713,129716,129720,129723,129740,129750,129755,129758,129772,129778,129783,129790,130014,130017,130029,130035,130040,130043,130047,130050,130062,130070,130075,130078,130083,130086,130091,130093,130099,130102,130107,130110,130115,130119,130337,130340,130346,130351,130353,130361,130384,130386,130402,130406,130427,130431,130434,130442,130468,130471,130473,130481,130485],[22,128845,128846,128847,128850],{},"At this point, you have most likely heard about the JavaScript framework called ",[677,128848,105462],{"href":117748,"rel":128849},[681],". If it's been on your list as something to try out but you weren't quite sure where to start I am here to help. In this article, I am going to tell you why I love Vue and give you 4 different ways you can write your first application.",[22,128852,128853],{},"Normally I shy away from giving someone new to a framework too many options for doing the same thing but in this case, I think it's important. You might be someone who enjoys working with the command line or you might be someone who prefers working within a GUI. There are also ways to start writing Vue applications without a CLI by just adding a script tag to an HTML page. Finally, you might not want to bother setting up a development environment and instead dive right in and try it now. No matter what option suits you best I got you covered.",[22,128855,128856],{},"As you move through this article I will leave you with some resources and when you reach the end I will leave you with what I think you should focus on next. This is not a deep dive into what Vue is or how to use the each of the options I give you so please keep that in mind.",[26,128858,128860],{"id":128859},"low-barrier-of-entry","Low Barrier of entry",[22,128862,128863],{},"For me, the real power of Vue is just how welcoming of a framework it is. The core team has done an amazing job of making sure that developers of all skill levels can jump on in and be productive. As long as you are comfortable with HTML, CSS & JavaScript you can get started building some pretty powerful applications right now.",[22,128865,128866],{},"I also believe that of the 3 top frameworks (Angular, React & Vue) Vue has the lowest barrier to entry. There are a few reasons for this but for me, it comes down to 3 things",[915,128868,128869,128872,128875],{},[37,128870,128871],{},"Great Documentation",[37,128873,128874],{},"Awesome Community",[37,128876,128877],{},"Progressive Framework",[636,128879,128871],{"id":128880},"great-documentation",[22,128882,128883],{},"When you ask most developers who enjoy using Vue I think one of the first things they will tell you is just how great the documentation is. If you head over to the Vue.js website you will find the docs broken down into a few sections.",[22,128885,128886],{},[646,128887,128888],{},"Guide",[22,128890,47116,128891,128895],{},[677,128892,128888],{"href":128893,"rel":128894},"https://vuejs.org/v2/guide/",[681],", you will find everything you need to get started with Vue.js. It's broken down into a nice logical flow of concepts you will need to learn to start building component-based applications.",[22,128897,128898],{},[646,128899,128900],{},"API",[22,128902,47116,128903,128907],{},[677,128904,128900],{"href":128905,"rel":128906},"https://vuejs.org/v2/api/",[681],", you will find everything you need to begin working with the Vue API. If you ever see a method in Vue and you're not quite sure what it does, this should give you the answers.",[22,128909,128910],{},[646,128911,128912],{},"Style Guide",[22,128914,47116,128915,128919],{},[677,128916,128912],{"href":128917,"rel":128918},"https://vuejs.org/v2/style-guide/",[681],", you will find some awesome recommendations for avoided errors and anti-patterns. It's important that these aren't rules but more of a guide. After you get comfortable writing some applications I would start to walk through as I have found it to be a very valuable resource.",[22,128921,128922],{},[646,128923,128924],{},"Examples",[22,128926,47116,128927,128931],{},[677,128928,128924],{"href":128929,"rel":128930},"https://vuejs.org/v2/examples/",[681],", you will find sample applications built with Vue. I would save this resource for later and focus on building simple components but when you're ready to see how full applications are composed check it out.",[22,128933,128934],{},[646,128935,128936],{},"Cookbook",[22,128938,47116,128939,128943],{},[677,128940,128936],{"href":128941,"rel":128942},"https://vuejs.org/v2/cookbook/",[681],", you will find answers to common questions you might have. This could be things like \"How do I do form validation\" or \"How can I unit test a component\". This again is a valuable resource but I would save it for when you start asking these types of questions.",[22,128945,128946],{},[646,128947,128948],{},"Tooling & Core Libraries",[22,128950,128951],{},"There are also great resources and documentation around tooling and core libraries. I am not going to far into these because I want to dive in and start writing some code. I do want to leave these as resources and I suggest giving them a look over so you know what is available to you.",[22,128953,128954],{},[646,128955,128956],{},"Tooling",[915,128958,128959,128966,128971],{},[37,128960,128961],{},[677,128962,128965],{"href":128963,"rel":128964},"https://github.com/vuejs/vue-devtools",[681],"DevTools",[37,128967,128968],{},[677,128969,105805],{"href":105803,"rel":128970},[681],[37,128972,128973],{},[677,128974,128977],{"href":128975,"rel":128976},"https://vue-loader.vuejs.org/",[681],"Vue Loader",[22,128979,128980],{},[646,128981,128982],{},"Core Libraries",[915,128984,128985,128992,128999],{},[37,128986,128987],{},[677,128988,128991],{"href":128989,"rel":128990},"https://router.vuejs.org/",[681],"Vue Router",[37,128993,128994],{},[677,128995,128998],{"href":128996,"rel":128997},"https://vuex.vuejs.org/",[681],"Vuex",[37,129000,129001],{},[677,129002,129005],{"href":129003,"rel":129004},"https://ssr.vuejs.org/",[681],"Vue Server Renderer",[636,129007,128874],{"id":129008},"awesome-community",[22,129010,129011],{},"I feel like I have been a part of some amazing communities in my development career and the Vue community is no different. Everyone is very welcoming, friendly and beyond helpful. An easy way to see what others in the community are building or excited about is to follow some of them on Twitter. Here are 10 people I think you should start following today.",[915,129013,129014,129021,129028,129033,129039,129046,129053,129060,129067,129074],{},[37,129015,129016],{},[677,129017,129020],{"href":129018,"rel":129019},"https://twitter.com/youyuxi",[681],"Evan You",[37,129022,129023],{},[677,129024,129027],{"href":129025,"rel":129026},"https://twitter.com/chrisvfritz",[681],"Chris Fritz",[37,129029,129030],{},[677,129031,118235],{"href":118233,"rel":129032},[681],[37,129034,129035],{},[677,129036,129038],{"href":123325,"rel":129037},[681],"Damian Sulisz",[37,129040,129041],{},[677,129042,129045],{"href":129043,"rel":129044},"https://twitter.com/shortdiv",[681],"Divya Sasidharan",[37,129047,129048],{},[677,129049,129052],{"href":129050,"rel":129051},"https://twitter.com/Akryum",[681],"Guillaume Chau",[37,129054,129055],{},[677,129056,129059],{"href":129057,"rel":129058},"https://twitter.com/bencodezen",[681],"Ben Hong",[37,129061,129062],{},[677,129063,129066],{"href":129064,"rel":129065},"https://twitter.com/Atinux",[681],"Sebastien Chopin",[37,129068,129069],{},[677,129070,129073],{"href":129071,"rel":129072},"https://twitter.com/N_Tepluhina",[681],"Natalia Tepluhina",[37,129075,129076],{},[677,129077,129080],{"href":129078,"rel":129079},"https://twitter.com/EddYerburgh",[681],"Edd Yerburgh",[636,129082,128877],{"id":129083},"progressive-framework",[22,129085,129086],{},"If you visit the home page for Vue.js you will see it is known as \"The Progressive Framework\" and while it might seem like marketing mumbo jumbo at first, you quickly realize that it isn't. Let's start with the definition of progressive",[29685,129088,129089],{},[22,129090,129091],{},"happening or developing gradually or in stages; proceeding step by step.",[22,129093,129094],{},"As you will see in just a minute you can take a very gradual approach to building Vue applications. If you have an existing project that you would like to add Vue too, no problem. You can start by dropping in a script tag and writing a few lines of code.",[22,129096,129097],{},"If you want to use a CLI to scaffold a new project out based on features you might need, also not a problem. As your application starts to scale and you need to add things like routing or state management it's really easy to do.",[22,129099,129100],{},"We talked about it earlier but Vue is an approachable framework. If you already know HTML, CSS & JavaScript you can get started today. If this is you let's jump on in and start writing some",[26,129102,129104],{"id":129103},"vue-js-script","Vue JS Script",[22,129106,129107],{},"The first option we are going to look at is using the script tag. If you have worked with JavaScript in the past this is nothing new to you. You have an HTML page and you need to add some functionality to the page so you drop in a script tag. This isn't just for learning Vue because it has some real practical uses.",[22,129109,129110],{},"It's usually really hard to introduce new tools, libraries and frameworks at work. If you really like working with Vue and went to your boss and asked if you could rewrite the whole system in Vue he or she would almost immediately shoot your idea down.",[22,129112,129113],{},"If instead you went to them and asked to introduce Vue on this new page you were building, that is a much easier sell. In this example, we are going to build a pretty arbitrary application but it's good for explaining a couple of different concepts.",[636,129115,129117],{"id":129116},"hello-vue","Hello, Vue!",[22,129119,129120,129121,2755],{},"In this application you are going to display the text \"Hello, Vue!\" and the current date and time below it. You will then write some logic to update the current date/time so that the user will see the time change in real time. You are going to start off by creating a directory and adding the following to a new page ",[59,129122,20654],{},[52,129124,129126],{"className":15773,"code":129125,"language":15775,"meta":57,"style":57},"\u003C!DOCTYPE html>\n\u003Chtml lang=\"en\">\n \u003Chead>\n \u003Cmeta charset=\"UTF-8\" />\n \u003Cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n \u003Cmeta http-equiv=\"X-UA-Compatible\" content=\"ie=edge\" />\n \u003Ctitle>Hello, Vue.js\u003C/title>\n \u003C/head>\n \u003Cbody>\u003C/body>\n\u003C/html>\n",[59,129127,129128,129138,129152,129160,129174,129194,129214,129227,129235,129247],{"__ignoreMap":57},[62,129129,129130,129132,129134,129136],{"class":64,"line":65},[62,129131,15801],{"class":72},[62,129133,15804],{"class":1780},[62,129135,15807],{"class":122},[62,129137,1784],{"class":72},[62,129139,129140,129142,129144,129146,129148,129150],{"class":64,"line":76},[62,129141,760],{"class":72},[62,129143,15775],{"class":1780},[62,129145,20679],{"class":122},[62,129147,146],{"class":72},[62,129149,20684],{"class":1675},[62,129151,1784],{"class":72},[62,129153,129154,129156,129158],{"class":64,"line":82},[62,129155,33056],{"class":72},[62,129157,15824],{"class":1780},[62,129159,1784],{"class":72},[62,129161,129162,129164,129166,129168,129170,129172],{"class":64,"line":89},[62,129163,1789],{"class":72},[62,129165,20701],{"class":1780},[62,129167,20704],{"class":122},[62,129169,146],{"class":72},[62,129171,20709],{"class":1675},[62,129173,67133],{"class":72},[62,129175,129176,129178,129180,129182,129184,129186,129188,129190,129192],{"class":64,"line":95},[62,129177,1789],{"class":72},[62,129179,20701],{"class":1780},[62,129181,16107],{"class":122},[62,129183,146],{"class":72},[62,129185,20724],{"class":1675},[62,129187,20727],{"class":122},[62,129189,146],{"class":72},[62,129191,20732],{"class":1675},[62,129193,67133],{"class":72},[62,129195,129196,129198,129200,129202,129204,129206,129208,129210,129212],{"class":64,"line":101},[62,129197,1789],{"class":72},[62,129199,20701],{"class":1780},[62,129201,60649],{"class":122},[62,129203,146],{"class":72},[62,129205,60654],{"class":1675},[62,129207,20727],{"class":122},[62,129209,146],{"class":72},[62,129211,118701],{"class":1675},[62,129213,67133],{"class":72},[62,129215,129216,129218,129220,129223,129225],{"class":64,"line":107},[62,129217,1789],{"class":72},[62,129219,3196],{"class":1780},[62,129221,129222],{"class":72},">Hello, Vue.js\u003C/",[62,129224,3196],{"class":1780},[62,129226,1784],{"class":72},[62,129228,129229,129231,129233],{"class":64,"line":113},[62,129230,33187],{"class":72},[62,129232,15824],{"class":1780},[62,129234,1784],{"class":72},[62,129236,129237,129239,129241,129243,129245],{"class":64,"line":129},[62,129238,33056],{"class":72},[62,129240,11414],{"class":1780},[62,129242,15857],{"class":72},[62,129244,11414],{"class":1780},[62,129246,1784],{"class":72},[62,129248,129249,129251,129253],{"class":64,"line":134},[62,129250,1818],{"class":72},[62,129252,15775],{"class":1780},[62,129254,1784],{"class":72},[22,129256,129257],{},"Now that you have the basic part of your page setup it's time to add the Vue.js script. You can add the following lines of code right before the body closing tag.",[52,129259,129261],{"className":15773,"code":129260,"language":15775,"meta":57,"style":57},"\u003C!-- development version includes helpful console warnings -->\n\u003Cscript src=\"https://cdn.jsdelivr.net/npm/vue/dist/vue.js\">\u003C/script>\n\u003Cscript>\u003C/script>\n",[59,129262,129263,129268,129287],{"__ignoreMap":57},[62,129264,129265],{"class":64,"line":65},[62,129266,129267],{"class":85},"\u003C!-- development version includes helpful console warnings -->\n",[62,129269,129270,129272,129274,129276,129278,129281,129283,129285],{"class":64,"line":76},[62,129271,760],{"class":72},[62,129273,15846],{"class":1780},[62,129275,15849],{"class":122},[62,129277,146],{"class":72},[62,129279,129280],{"class":1675},"\"https://cdn.jsdelivr.net/npm/vue/dist/vue.js\"",[62,129282,15857],{"class":72},[62,129284,15846],{"class":1780},[62,129286,1784],{"class":72},[62,129288,129289,129291,129293,129295,129297],{"class":64,"line":82},[62,129290,760],{"class":72},[62,129292,15846],{"class":1780},[62,129294,15857],{"class":72},[62,129296,15846],{"class":1780},[62,129298,1784],{"class":72},[22,129300,129301,129302,2755],{},"I left the comment in there because it's important to note this is the development version of the script. If you were going to put this in production you would want to use the production script. You can read more about this is the ",[677,129303,129305],{"href":128893,"rel":129304},[681],"Vue.js Guide Documentation",[22,129307,129308],{},"The first thing you need to is add a root element to mount your Vue instance to.",[52,129310,129312],{"className":15773,"code":129311,"language":15775,"meta":57,"style":57},"\u003Cdiv id=\"app\">\u003C/div>\n",[59,129313,129314],{"__ignoreMap":57},[62,129315,129316,129318,129320,129322,129324,129326,129328,129330],{"class":64,"line":65},[62,129317,760],{"class":72},[62,129319,15944],{"class":1780},[62,129321,20831],{"class":122},[62,129323,146],{"class":72},[62,129325,49987],{"class":1675},[62,129327,15857],{"class":72},[62,129329,15944],{"class":1780},[62,129331,1784],{"class":72},[22,129333,129334],{},"And then create a new Vue instance and tell Vue what your root element is.",[52,129336,129338],{"className":15773,"code":129337,"language":15775,"meta":57,"style":57},"\u003Cscript>\n const app = new Vue({\n el: \"#app\"\n });\n\u003C/script>\n",[59,129339,129340,129348,129364,129372,129376],{"__ignoreMap":57},[62,129341,129342,129344,129346],{"class":64,"line":65},[62,129343,760],{"class":72},[62,129345,15846],{"class":1780},[62,129347,1784],{"class":72},[62,129349,129350,129352,129355,129357,129359,129362],{"class":64,"line":76},[62,129351,85414],{"class":68},[62,129353,129354],{"class":149}," app",[62,129356,2556],{"class":68},[62,129358,466],{"class":68},[62,129360,129361],{"class":122}," Vue",[62,129363,50544],{"class":72},[62,129365,129366,129369],{"class":64,"line":82},[62,129367,129368],{"class":72}," el: ",[62,129370,129371],{"class":1675},"\"#app\"\n",[62,129373,129374],{"class":64,"line":89},[62,129375,85488],{"class":72},[62,129377,129378,129380,129382],{"class":64,"line":95},[62,129379,1818],{"class":72},[62,129381,15846],{"class":1780},[62,129383,1784],{"class":72},[22,129385,129386],{},"What you want to do now is to create a couple of variables that hold values that you can then use to display information on the page. You do this by declaring properties on the data object.",[52,129388,129390],{"className":15773,"code":129389,"language":15775,"meta":57,"style":57},"\u003Cscript>\n const app = new Vue({\n el: \"#app\",\n data: {\n message: \"Hello Vue!\",\n now: new Date()\n }\n });\n\u003C/script>\n",[59,129391,129392,129400,129414,129423,129428,129438,129449,129453,129457],{"__ignoreMap":57},[62,129393,129394,129396,129398],{"class":64,"line":65},[62,129395,760],{"class":72},[62,129397,15846],{"class":1780},[62,129399,1784],{"class":72},[62,129401,129402,129404,129406,129408,129410,129412],{"class":64,"line":76},[62,129403,85414],{"class":68},[62,129405,129354],{"class":149},[62,129407,2556],{"class":68},[62,129409,466],{"class":68},[62,129411,129361],{"class":122},[62,129413,50544],{"class":72},[62,129415,129416,129418,129421],{"class":64,"line":82},[62,129417,129368],{"class":72},[62,129419,129420],{"class":1675},"\"#app\"",[62,129422,3338],{"class":72},[62,129424,129425],{"class":64,"line":89},[62,129426,129427],{"class":72}," data: {\n",[62,129429,129430,129433,129436],{"class":64,"line":95},[62,129431,129432],{"class":72}," message: ",[62,129434,129435],{"class":1675},"\"Hello Vue!\"",[62,129437,3338],{"class":72},[62,129439,129440,129443,129445,129447],{"class":64,"line":101},[62,129441,129442],{"class":72}," now: ",[62,129444,2426],{"class":68},[62,129446,61498],{"class":122},[62,129448,2223],{"class":72},[62,129450,129451],{"class":64,"line":107},[62,129452,223],{"class":72},[62,129454,129455],{"class":64,"line":113},[62,129456,85488],{"class":72},[62,129458,129459,129461,129463],{"class":64,"line":129},[62,129460,1818],{"class":72},[62,129462,15846],{"class":1780},[62,129464,1784],{"class":72},[22,129466,129467],{},"Now that your data is setup Vue gives us a really clean way to display that data on the page.",[52,129469,129471],{"className":15773,"code":129470,"language":15775,"meta":57,"style":57},"\u003Cdiv id=\"app\">\n \u003Ch1>{{ message }}\u003C/h1>\n \u003Cp>{{ now }}\u003C/p>\n\u003C/div>\n",[59,129472,129473,129487,129500,129513],{"__ignoreMap":57},[62,129474,129475,129477,129479,129481,129483,129485],{"class":64,"line":65},[62,129476,760],{"class":72},[62,129478,15944],{"class":1780},[62,129480,20831],{"class":122},[62,129482,146],{"class":72},[62,129484,49987],{"class":1675},[62,129486,1784],{"class":72},[62,129488,129489,129491,129493,129496,129498],{"class":64,"line":76},[62,129490,33056],{"class":72},[62,129492,4168],{"class":1780},[62,129494,129495],{"class":72},">{{ message }}\u003C/",[62,129497,4168],{"class":1780},[62,129499,1784],{"class":72},[62,129501,129502,129504,129506,129509,129511],{"class":64,"line":82},[62,129503,33056],{"class":72},[62,129505,22],{"class":1780},[62,129507,129508],{"class":72},">{{ now }}\u003C/",[62,129510,22],{"class":1780},[62,129512,1784],{"class":72},[62,129514,129515,129517,129519],{"class":64,"line":89},[62,129516,1818],{"class":72},[62,129518,15944],{"class":1780},[62,129520,1784],{"class":72},[22,129522,129523,129524,129529],{},"Give yourself a big pat on the back because just created your first Vue application. If you run this application you should get the heading \"Hello, Vue!\" and the current date/time below it. I am using Visual Studio Code and the ",[677,129525,129528],{"href":129526,"rel":129527},"https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer",[681],"Live Server extension"," to run mine.",[22,129531,129532],{},"This doesn't look like much but Vue is actually doing a lot under the hood for you. The data and the DOM are now linked, and everything is now reactive. How do we know? Open your browser’s JavaScript console (right now, on this page) and set app.message to a different value. You should see the rendered example above update accordingly.",[22,129534,129535],{},[653,129536],{"alt":57,"src":129537},"/images/blog/2019/04/30/2019-04-30_12-12-13-88a57b30-f402-4444-befb-262701bb347c.png",[22,129539,129540,129541,976,129544,976,129546,129549],{},"Vue also gives you the ability to \"hook\" into the lifecycle of a component. This means that you can listen for events like ",[59,129542,129543],{},"created",[59,129545,107449],{},[59,129547,129548],{},"destroyed"," and more. I don't want to get too much further into this because this article is meant to show you different ways to create a Vue application, not everything else. I do want to finish our application though.",[22,129551,129552,129553,129555],{},"The following code will update the variable now every second. When Vue is mounted you run a function every second using the ",[59,129554,113144],{}," method. Just like adding your own data on the Vue instance you can create methods by adding them to the methods object.",[52,129557,129559],{"className":15773,"code":129558,"language":15775,"meta":57,"style":57},"\u003Cscript>\n const app = new Vue({\n el: \"#app\",\n data: {\n message: \"Hello Vue!\",\n now: new Date()\n },\n methods: {\n updateDate() {\n this.now = new Date();\n }\n },\n mounted() {\n setInterval(() => {\n this.updateDate();\n }, 100);\n }\n });\n\u003C/script>\n",[59,129560,129561,129569,129583,129591,129595,129603,129613,129617,129621,129628,129643,129647,129651,129658,129669,129680,129688,129692,129696],{"__ignoreMap":57},[62,129562,129563,129565,129567],{"class":64,"line":65},[62,129564,760],{"class":72},[62,129566,15846],{"class":1780},[62,129568,1784],{"class":72},[62,129570,129571,129573,129575,129577,129579,129581],{"class":64,"line":76},[62,129572,85414],{"class":68},[62,129574,129354],{"class":149},[62,129576,2556],{"class":68},[62,129578,466],{"class":68},[62,129580,129361],{"class":122},[62,129582,50544],{"class":72},[62,129584,129585,129587,129589],{"class":64,"line":82},[62,129586,129368],{"class":72},[62,129588,129420],{"class":1675},[62,129590,3338],{"class":72},[62,129592,129593],{"class":64,"line":89},[62,129594,129427],{"class":72},[62,129596,129597,129599,129601],{"class":64,"line":95},[62,129598,129432],{"class":72},[62,129600,129435],{"class":1675},[62,129602,3338],{"class":72},[62,129604,129605,129607,129609,129611],{"class":64,"line":101},[62,129606,129442],{"class":72},[62,129608,2426],{"class":68},[62,129610,61498],{"class":122},[62,129612,2223],{"class":72},[62,129614,129615],{"class":64,"line":107},[62,129616,50124],{"class":72},[62,129618,129619],{"class":64,"line":113},[62,129620,124097],{"class":72},[62,129622,129623,129626],{"class":64,"line":129},[62,129624,129625],{"class":122}," updateDate",[62,129627,206],{"class":72},[62,129629,129630,129632,129635,129637,129639,129641],{"class":64,"line":134},[62,129631,2405],{"class":149},[62,129633,129634],{"class":72},".now ",[62,129636,146],{"class":68},[62,129638,466],{"class":68},[62,129640,61498],{"class":122},[62,129642,822],{"class":72},[62,129644,129645],{"class":64,"line":156},[62,129646,29042],{"class":72},[62,129648,129649],{"class":64,"line":161},[62,129650,50124],{"class":72},[62,129652,129653,129656],{"class":64,"line":167},[62,129654,129655],{"class":122}," mounted",[62,129657,206],{"class":72},[62,129659,129660,129663,129665,129667],{"class":64,"line":173},[62,129661,129662],{"class":122}," setInterval",[62,129664,797],{"class":72},[62,129666,21525],{"class":68},[62,129668,126],{"class":72},[62,129670,129671,129673,129675,129678],{"class":64,"line":179},[62,129672,2405],{"class":149},[62,129674,2755],{"class":72},[62,129676,129677],{"class":122},"updateDate",[62,129679,822],{"class":72},[62,129681,129682,129684,129686],{"class":64,"line":185},[62,129683,114951],{"class":72},[62,129685,102311],{"class":149},[62,129687,1133],{"class":72},[62,129689,129690],{"class":64,"line":191},[62,129691,223],{"class":72},[62,129693,129694],{"class":64,"line":209},[62,129695,85488],{"class":72},[62,129697,129698,129700,129702],{"class":64,"line":220},[62,129699,1818],{"class":72},[62,129701,15846],{"class":1780},[62,129703,1784],{"class":72},[22,129705,129706],{},"If you run the application now you should get the same display but every second the current date time is now updated. You didn't have to update the DOM manually because Vue has tied your data variables to the DOM for you and when they are updated, the DOM is updated.",[26,129708,105805],{"id":129709},"vue-cli",[22,129711,129712],{},"While dropping the script tag on a page got us up and running quickly it doesn't scale all that well. In cases where you want to build full-blown Single Page Applications (SPA) and take advantage of the toolchain, the Vue CLI is a great way to get up and running.",[22,129714,129715],{},"If you're not a huge fan of doing everything on the command line don't worry, the same CLI tool that you install here has a UI version.",[636,129717,129719],{"id":129718},"installing-the-vue-cli","Installing the Vue CLI",[22,129721,129722],{},"The first thing you need to do is install the Vue CLI. A requirement for this is having NodeJS and npm installed so if those are new to you take some time to install them and do a little reading on them to get a better understanding of what they are used for. To install the Vue CLI run the following command:",[52,129724,129726],{"className":1663,"code":129725,"language":1665,"meta":57,"style":57}," npm install -g @vue/cli\n",[59,129727,129728],{"__ignoreMap":57},[62,129729,129730,129733,129735,129737],{"class":64,"line":65},[62,129731,129732],{"class":122}," npm",[62,129734,32750],{"class":1675},[62,129736,51606],{"class":149},[62,129738,129739],{"class":1675}," @vue/cli\n",[22,129741,129742,129743,129745,129746,129749],{},"To ",[59,129744,14348],{}," a new application you can use the command ",[59,129747,129748],{},"vue create",". If you're not sure what commands are available you can run vue -h",[22,129751,129752],{},[653,129753],{"alt":57,"src":129754},"/images/blog/2019/04/30/2019-04-30_14-12-28-8dc58daf-3581-4eb4-a81c-bce59eb86341.png",[22,129756,129757],{},"The create command takes an app name so let's create a new vue application by running the following command:",[52,129759,129761],{"className":1663,"code":129760,"language":1665,"meta":57,"style":57},"vue create hello-vue\n",[59,129762,129763],{"__ignoreMap":57},[62,129764,129765,129767,129769],{"class":64,"line":65},[62,129766,105335],{"class":122},[62,129768,23301],{"class":1675},[62,129770,129771],{"class":1675}," hello-vue\n",[22,129773,129774,129775,129777],{},"This will create a new folder for you in the current directory called ",[59,129776,129116],{},". I will go through this in more detail in a later post but for now, just accept the defaults and a new application will be created for you.",[22,129779,129780],{},[653,129781],{"alt":57,"src":129782},"/images/blog/2019/04/30/2019-04-30_14-18-14-001b241f-0bda-4f21-b382-cc597bd97095.png",[22,129784,129785,129786,129789],{},"Open up ",[59,129787,129788],{},"src/components/HelloWorld.vue"," and replace everything in there with the following.",[52,129791,129793],{"className":105981,"code":129792,"language":105335,"meta":57,"style":57},"\u003Ctemplate>\n \u003Cdiv class=\"hello\">\n \u003Ch1>{{ msg }}\u003C/h1>\n \u003Cp>{{ now }}\u003C/p>\n \u003C/div>\n\u003C/template>\n\n\u003Cscript>\nexport default {\n name: \"Hello Vue\",\n props: {\n msg: String\n },\n data() {\n return {\n now: new Date()\n };\n },\n methods: {\n updateDate() {\n this.now = new Date();\n }\n },\n mounted() {\n setInterval(() => {\n this.updateDate();\n }, 100);\n }\n};\n\u003C/script>\n",[59,129794,129795,129803,129818,129830,129842,129850,129858,129862,129870,129878,129887,129891,129896,129900,129906,129912,129922,129926,129930,129934,129941,129955,129959,129963,129969,129980,129990,129998,130002,130006],{"__ignoreMap":57},[62,129796,129797,129799,129801],{"class":64,"line":65},[62,129798,760],{"class":72},[62,129800,105991],{"class":1780},[62,129802,1784],{"class":72},[62,129804,129805,129807,129809,129811,129813,129816],{"class":64,"line":76},[62,129806,33056],{"class":72},[62,129808,15944],{"class":1780},[62,129810,119],{"class":122},[62,129812,146],{"class":72},[62,129814,129815],{"class":1675},"\"hello\"",[62,129817,1784],{"class":72},[62,129819,129820,129822,129824,129826,129828],{"class":64,"line":82},[62,129821,1789],{"class":72},[62,129823,4168],{"class":1780},[62,129825,106144],{"class":72},[62,129827,4168],{"class":1780},[62,129829,1784],{"class":72},[62,129831,129832,129834,129836,129838,129840],{"class":64,"line":89},[62,129833,1789],{"class":72},[62,129835,22],{"class":1780},[62,129837,129508],{"class":72},[62,129839,22],{"class":1780},[62,129841,1784],{"class":72},[62,129843,129844,129846,129848],{"class":64,"line":95},[62,129845,33187],{"class":72},[62,129847,15944],{"class":1780},[62,129849,1784],{"class":72},[62,129851,129852,129854,129856],{"class":64,"line":101},[62,129853,1818],{"class":72},[62,129855,105991],{"class":1780},[62,129857,1784],{"class":72},[62,129859,129860],{"class":64,"line":107},[62,129861,79],{"emptyLinePlaceholder":13},[62,129863,129864,129866,129868],{"class":64,"line":113},[62,129865,760],{"class":72},[62,129867,15846],{"class":1780},[62,129869,1784],{"class":72},[62,129871,129872,129874,129876],{"class":64,"line":129},[62,129873,14767],{"class":68},[62,129875,106045],{"class":68},[62,129877,126],{"class":72},[62,129879,129880,129882,129885],{"class":64,"line":134},[62,129881,106052],{"class":72},[62,129883,129884],{"class":1675},"\"Hello Vue\"",[62,129886,3338],{"class":72},[62,129888,129889],{"class":64,"line":156},[62,129890,123075],{"class":72},[62,129892,129893],{"class":64,"line":161},[62,129894,129895],{"class":72}," msg: String\n",[62,129897,129898],{"class":64,"line":167},[62,129899,32848],{"class":72},[62,129901,129902,129904],{"class":64,"line":173},[62,129903,106190],{"class":122},[62,129905,206],{"class":72},[62,129907,129908,129910],{"class":64,"line":179},[62,129909,2599],{"class":68},[62,129911,126],{"class":72},[62,129913,129914,129916,129918,129920],{"class":64,"line":185},[62,129915,129442],{"class":72},[62,129917,2426],{"class":68},[62,129919,61498],{"class":122},[62,129921,2223],{"class":72},[62,129923,129924],{"class":64,"line":191},[62,129925,40087],{"class":72},[62,129927,129928],{"class":64,"line":209},[62,129929,32848],{"class":72},[62,129931,129932],{"class":64,"line":220},[62,129933,107160],{"class":72},[62,129935,129936,129939],{"class":64,"line":226},[62,129937,129938],{"class":122}," updateDate",[62,129940,206],{"class":72},[62,129942,129943,129945,129947,129949,129951,129953],{"class":64,"line":231},[62,129944,122700],{"class":149},[62,129946,129634],{"class":72},[62,129948,146],{"class":68},[62,129950,466],{"class":68},[62,129952,61498],{"class":122},[62,129954,822],{"class":72},[62,129956,129957],{"class":64,"line":236},[62,129958,223],{"class":72},[62,129960,129961],{"class":64,"line":242},[62,129962,32848],{"class":72},[62,129964,129965,129967],{"class":64,"line":247},[62,129966,106219],{"class":122},[62,129968,206],{"class":72},[62,129970,129971,129974,129976,129978],{"class":64,"line":252},[62,129972,129973],{"class":122}," setInterval",[62,129975,797],{"class":72},[62,129977,21525],{"class":68},[62,129979,126],{"class":72},[62,129981,129982,129984,129986,129988],{"class":64,"line":257},[62,129983,122700],{"class":149},[62,129985,2755],{"class":72},[62,129987,129677],{"class":122},[62,129989,822],{"class":72},[62,129991,129992,129994,129996],{"class":64,"line":271},[62,129993,115398],{"class":72},[62,129995,102311],{"class":149},[62,129997,1133],{"class":72},[62,129999,130000],{"class":64,"line":281},[62,130001,3731],{"class":72},[62,130003,130004],{"class":64,"line":286},[62,130005,107354],{"class":72},[62,130007,130008,130010,130012],{"class":64,"line":291},[62,130009,1818],{"class":72},[62,130011,15846],{"class":1780},[62,130013,1784],{"class":72},[22,130015,130016],{},"From the command line (or the integrated terminal) run your app using the following comand",[52,130018,130019],{"className":1663,"code":105832,"language":1665,"meta":57,"style":57},[59,130020,130021],{"__ignoreMap":57},[62,130022,130023,130025,130027],{"class":64,"line":65},[62,130024,32645],{"class":122},[62,130026,1716],{"class":1675},[62,130028,49787],{"class":1675},[22,130030,130031,130032],{},"When your application starts the command line should tell you where it's running but by default, it should be at ",[677,130033,17969],{"href":17969,"rel":130034},[681],[22,130036,130037],{},[653,130038],{"alt":57,"src":130039},"/images/blog/2019/04/30/2019-04-30_14-23-52-74842c90-97ed-4a17-8d54-7a0b3a201d95.png",[22,130041,130042],{},"That was just a quick walkthrough of using the CLI but I hope you saw just how easy it was to create a new application that has a strong infrastructure and is ready to scale with you as your needs grow.",[26,130044,130046],{"id":130045},"vue-ui","Vue UI",[22,130048,130049],{},"With the Vue CLI installed you can use the UI by running the following command from the command line",[52,130051,130053],{"className":1663,"code":130052,"language":1665,"meta":57,"style":57},"vue ui\n",[59,130054,130055],{"__ignoreMap":57},[62,130056,130057,130059],{"class":64,"line":65},[62,130058,105335],{"class":122},[62,130060,130061],{"class":1675}," ui\n",[22,130063,130064,130065,130069],{},"This will open a new application at ",[677,130066,130067],{"href":130067,"rel":130068},"http://localhost:8000/dashboard",[681]," that looks like this",[22,130071,130072],{},[653,130073],{"alt":57,"src":130074},"/images/blog/2019/04/30/2019-04-30_14-29-06-774dbcf1-d680-4083-94e7-049799559548.png",[22,130076,130077],{},"You can create a new project using the Vue UI and it will give you the same options the command line gives you.",[22,130079,130080],{},[653,130081],{"alt":57,"src":130082},"/images/blog/2019/04/30/2019-04-30_14-31-34-fe1e08dd-9393-47ac-b0e7-589e1b3771f5.png",[22,130084,130085],{},"And when your project is done you can run it right from the UI using the Project Tasks.",[22,130087,130088],{},[653,130089],{"alt":57,"src":130090},"/images/blog/2019/04/30/2019-04-30_14-34-11-eb6a9994-3a24-4f23-b6c2-e7280a48c788.png",[26,130092,123340],{"id":124428},[22,130094,130095,130096,2755],{},"While those are all great options to get up and running with Vue they also require you to have something of a development environment setup. If you haven't had a chance to give it a try I am here to tell you about an awesome online code editor for the web called ",[677,130097,123340],{"href":126772,"rel":130098},[681],[22,130100,130101],{},"The first thing you need to do is to sign up for a CodeSandbox account by sign in using your Github account. Once you're signed in click on create a sandbox and from there you can select from a large variety of templates. You can select Vue from the popular or client templates tab.",[22,130103,130104],{},[653,130105],{"alt":57,"src":130106},"/images/blog/2019/04/30/2019-04-27_10-52-25-6a3c9422-66c2-4de4-be7a-a8a7551f2317.png",[22,130108,130109],{},"In just seconds you have a new Vue application up and running! In the left pane, you have all of your project files, your editor in the middle and a browser preview of the application running.",[22,130111,130112],{},[653,130113],{"alt":57,"src":130114},"/images/blog/2019/04/30/2019-04-27_10-54-12-15e1c74a-803a-4d5b-ac61-b22b9422405d.png",[22,130116,129785,130117,129789],{},[59,130118,129788],{},[52,130120,130121],{"className":105981,"code":129792,"language":105335,"meta":57,"style":57},[59,130122,130123,130131,130145,130157,130169,130177,130185,130189,130197,130205,130213,130217,130221,130225,130231,130237,130247,130251,130255,130259,130265,130279,130283,130287,130293,130303,130313,130321,130325,130329],{"__ignoreMap":57},[62,130124,130125,130127,130129],{"class":64,"line":65},[62,130126,760],{"class":72},[62,130128,105991],{"class":1780},[62,130130,1784],{"class":72},[62,130132,130133,130135,130137,130139,130141,130143],{"class":64,"line":76},[62,130134,33056],{"class":72},[62,130136,15944],{"class":1780},[62,130138,119],{"class":122},[62,130140,146],{"class":72},[62,130142,129815],{"class":1675},[62,130144,1784],{"class":72},[62,130146,130147,130149,130151,130153,130155],{"class":64,"line":82},[62,130148,1789],{"class":72},[62,130150,4168],{"class":1780},[62,130152,106144],{"class":72},[62,130154,4168],{"class":1780},[62,130156,1784],{"class":72},[62,130158,130159,130161,130163,130165,130167],{"class":64,"line":89},[62,130160,1789],{"class":72},[62,130162,22],{"class":1780},[62,130164,129508],{"class":72},[62,130166,22],{"class":1780},[62,130168,1784],{"class":72},[62,130170,130171,130173,130175],{"class":64,"line":95},[62,130172,33187],{"class":72},[62,130174,15944],{"class":1780},[62,130176,1784],{"class":72},[62,130178,130179,130181,130183],{"class":64,"line":101},[62,130180,1818],{"class":72},[62,130182,105991],{"class":1780},[62,130184,1784],{"class":72},[62,130186,130187],{"class":64,"line":107},[62,130188,79],{"emptyLinePlaceholder":13},[62,130190,130191,130193,130195],{"class":64,"line":113},[62,130192,760],{"class":72},[62,130194,15846],{"class":1780},[62,130196,1784],{"class":72},[62,130198,130199,130201,130203],{"class":64,"line":129},[62,130200,14767],{"class":68},[62,130202,106045],{"class":68},[62,130204,126],{"class":72},[62,130206,130207,130209,130211],{"class":64,"line":134},[62,130208,106052],{"class":72},[62,130210,129884],{"class":1675},[62,130212,3338],{"class":72},[62,130214,130215],{"class":64,"line":156},[62,130216,123075],{"class":72},[62,130218,130219],{"class":64,"line":161},[62,130220,129895],{"class":72},[62,130222,130223],{"class":64,"line":167},[62,130224,32848],{"class":72},[62,130226,130227,130229],{"class":64,"line":173},[62,130228,106190],{"class":122},[62,130230,206],{"class":72},[62,130232,130233,130235],{"class":64,"line":179},[62,130234,2599],{"class":68},[62,130236,126],{"class":72},[62,130238,130239,130241,130243,130245],{"class":64,"line":185},[62,130240,129442],{"class":72},[62,130242,2426],{"class":68},[62,130244,61498],{"class":122},[62,130246,2223],{"class":72},[62,130248,130249],{"class":64,"line":191},[62,130250,40087],{"class":72},[62,130252,130253],{"class":64,"line":209},[62,130254,32848],{"class":72},[62,130256,130257],{"class":64,"line":220},[62,130258,107160],{"class":72},[62,130260,130261,130263],{"class":64,"line":226},[62,130262,129938],{"class":122},[62,130264,206],{"class":72},[62,130266,130267,130269,130271,130273,130275,130277],{"class":64,"line":231},[62,130268,122700],{"class":149},[62,130270,129634],{"class":72},[62,130272,146],{"class":68},[62,130274,466],{"class":68},[62,130276,61498],{"class":122},[62,130278,822],{"class":72},[62,130280,130281],{"class":64,"line":236},[62,130282,223],{"class":72},[62,130284,130285],{"class":64,"line":242},[62,130286,32848],{"class":72},[62,130288,130289,130291],{"class":64,"line":247},[62,130290,106219],{"class":122},[62,130292,206],{"class":72},[62,130294,130295,130297,130299,130301],{"class":64,"line":252},[62,130296,129973],{"class":122},[62,130298,797],{"class":72},[62,130300,21525],{"class":68},[62,130302,126],{"class":72},[62,130304,130305,130307,130309,130311],{"class":64,"line":257},[62,130306,122700],{"class":149},[62,130308,2755],{"class":72},[62,130310,129677],{"class":122},[62,130312,822],{"class":72},[62,130314,130315,130317,130319],{"class":64,"line":271},[62,130316,115398],{"class":72},[62,130318,102311],{"class":149},[62,130320,1133],{"class":72},[62,130322,130323],{"class":64,"line":281},[62,130324,3731],{"class":72},[62,130326,130327],{"class":64,"line":286},[62,130328,107354],{"class":72},[62,130330,130331,130333,130335],{"class":64,"line":291},[62,130332,1818],{"class":72},[62,130334,15846],{"class":1780},[62,130336,1784],{"class":72},[22,130338,130339],{},"And you should see the same application you have been working with. The nice thing about CodeSandbox is now you can take this and share the applications with friends to just show something off or get some advice.",[22,130341,130342],{},[677,130343,130344],{"href":130344,"rel":130345},"https://codesandbox.io/s/62o36qkmrr?fontsize=14",[681],[22,130347,130348],{},[653,130349],{"alt":57,"src":130350},"/images/blog/2019/04/30/2019-04-30_14-40-58-1d15756a-fa86-4d24-b8e2-bc57c97a6630.png",[26,130352,4098],{"id":4097},[22,130354,130355,130356,2755],{},"These are a few of my favorite resources to help get you going. For an in-depth look at everything in the Vue ecosystem checkout ",[677,130357,130360],{"href":130358,"rel":130359},"https://github.com/vuejs/awesome-vue",[681],"Awesome Vue",[915,130362,130363,130370,130377],{},[37,130364,130365],{},[677,130366,130369],{"href":130367,"rel":130368},"https://github.com/vuejs",[681],"Vue Core Repos",[37,130371,130372],{},[677,130373,130376],{"href":130374,"rel":130375},"https://medium.com/the-vue-point",[681],"The Vue Point (Official Blog)",[37,130378,130379],{},[677,130380,130383],{"href":130381,"rel":130382},"https://dev.to/t/vue",[681],"DEV Community",[636,130385,37576],{"id":86245},[915,130387,130388,130395],{},[37,130389,130390],{},[677,130391,130394],{"href":130392,"rel":130393},"https://news.vuejs.org/",[681],"The Official Vue News",[37,130396,130397],{},[677,130398,130401],{"href":130399,"rel":130400},"https://devchat.tv/views-on-vue/",[681],"Views on Vue",[636,130403,130405],{"id":130404},"frameworks","Frameworks",[915,130407,130408,130415,130422],{},[37,130409,130410],{},[677,130411,130414],{"href":130412,"rel":130413},"https://vuepress.vuejs.org/",[681],"VuePress",[37,130416,130417],{},[677,130418,130421],{"href":130419,"rel":130420},"https://nuxtjs.org/",[681],"Nuxt",[37,130423,130424],{},[677,130425,104836],{"href":104834,"rel":130426},[681],[26,130428,130430],{"id":130429},"where-to-go-from-here","Where to go from here?",[22,130432,130433],{},"No matter what framework you're using you need to keep up on your JavaScript skills. The more time you can spend improving your JavaScript skills the better. This will make sure that you are able to use any framework or library thrown your way.",[22,130435,130436,130437,130441],{},"I mentioned the ",[677,130438,130440],{"href":128893,"rel":130439},[681],"guide documentation"," earlier but it's worth mentioning again. This is the first place I would start and read through as much of this as you can but to also to make sure you're practicing the examples as you go. Repetition is an important step in the learning process and something that can't be overlooked. While you're going through the docs I would focus in on the core concepts of Vue.",[915,130443,130444,130447,130450,130453,130456,130459,130462,130465],{},[37,130445,130446],{},"Component Basics",[37,130448,130449],{},"Data Binding",[37,130451,130452],{},"Event Handling",[37,130454,130455],{},"Directives",[37,130457,130458],{},"Methods",[37,130460,130461],{},"Computed Properties",[37,130463,130464],{},"Vue Component Lifecycle",[37,130466,130467],{},"Props",[22,130469,130470],{},"Don't feel to build large applications at this point. Focus on building components and the rest will come in time.",[26,130472,1499],{"id":1498},[22,130474,130475,130476,130480],{},"If you follow me on Twitter (if you're not, stop what you're doing and ",[677,130477,130479],{"href":86334,"rel":130478},[681],"follow me now",") you know that I am a huge fan of Vue. I love writing component-based applications and I think Vue makes it not only easy but it really makes writing code fun. If you have any questions on your journey into Vue.js please don't hesitate to reach out. As always friends...",[22,130482,36004,130483,82545],{},[36006,130484],{},[1527,130486,130487],{},"html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .suaqH, html code.shiki .suaqH{--shiki-default:#22863A;--shiki-dark:#85E89D;--shiki-sepia:#22863A}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .s4-Ip, html code.shiki .s4-Ip{--shiki-default:#6A737D;--shiki-dark:#6A737D;--shiki-sepia:#6A737D}html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}",{"title":57,"searchDepth":76,"depth":76,"links":130489},[130490,130495,130498,130501,130502,130503,130507,130508],{"id":128859,"depth":76,"text":128860,"children":130491},[130492,130493,130494],{"id":128880,"depth":82,"text":128871},{"id":129008,"depth":82,"text":128874},{"id":129083,"depth":82,"text":128877},{"id":129103,"depth":76,"text":129104,"children":130496},[130497],{"id":129116,"depth":82,"text":129117},{"id":129709,"depth":76,"text":105805,"children":130499},[130500],{"id":129718,"depth":82,"text":129719},{"id":130045,"depth":76,"text":130046},{"id":124428,"depth":76,"text":123340},{"id":4097,"depth":76,"text":4098,"children":130504},[130505,130506],{"id":86245,"depth":82,"text":37576},{"id":130404,"depth":82,"text":130405},{"id":130429,"depth":76,"text":130430},{"id":1498,"depth":76,"text":1499},{"_id":130510,"path":130511,"title":130512,"description":130513,"meta":130514,"body":130519},"content/blog/2019/04/25/my-new-blog-post-workflow.md","/blog/2019/04/25/my-new-blog-post-workflow","My new blog post workflow","In this article I will outline for you the tools and process I use to create a new blog post",{"slug":130515,"date":130516,"published":13,"tags":130517,"author":17,"cover":130518,"excerpt":-1},"my-new-blog-post-workflow","2019-04-25T13:53:35.054Z",[60034],"super-snapper-zIwAchjDirM-unsplash.jpg",{"type":19,"value":130520,"toc":130842},[130521,130524,130528,130531,130534,130545,130548,130551,130554,130556,130559,130563,130566,130570,130573,130581,130584,130587,130590,130595,130598,130605,130608,130611,130616,130625,130629,130632,130635,130640,130643,130646,130651,130654,130659,130662,130666,130669,130713,130716,130721,130724,130728,130731,130734,130737,130741,130744,130749,130757,130761,130764,130767,130770,130774,130777,130782,130791,130796,130799,130802,130806,130814,130817,130825,130828,130833,130835,130838],[22,130522,130523],{},"Well, I have been blogging for a few months now on my new website and I thought I would share how I write all of my blog posts. I am really happy with my writing process and it how it allows me to write from any computer and quickly go from idea to live post. In this article, I will talk a little bit about how I used to write articles and what I was looking for in a new workflow.",[26,130525,130527],{"id":130526},"writing-requirements","Writing Requirements",[22,130529,130530],{},"If you have been following me you know that I am coming from writing all of my articles using the WordPress administrator. I would say that 90% of my WordPress posts were using the classic editor but I was one of the few people that really liked the Gutenberg block editor.",[22,130532,130533],{},"I write a lot of documentation for personal projects and work. Almost everything I write these days is in Markdown so as far as requirements go I only have a few:",[915,130535,130536,130539,130542],{},[37,130537,130538],{},"Clean & Distraction free writing",[37,130540,130541],{},"Markdown",[37,130543,130544],{},"Spelling & Grammar checking",[636,130546,130538],{"id":130547},"clean-distraction-free-writing",[22,130549,130550],{},"I can be easily distracted when I sit down to start writing so for me I need a distraction-free zone. If I am writing in a notebook this means no tv on in the background and somewhere comfortable by myself where I can collect my thoughts.",[22,130552,130553],{},"If I am writing in a text editor application that the document needs to be the only thing open. This is especially true when I am working in the browser. If there are any tabs open or the lure of something else is right there I might be pulled in another direction.",[636,130555,130541],{"id":29692},[22,130557,130558],{},"I really like Markdown because it is the easiest format that can transfer between platforms. I remember clearly when I started writing in Markdown and for the life of me, I couldn't understand why people liked it. Now, I couldn't imagine writing in any other format. For me, I am more productive writing in Markdown than anything else.",[636,130560,130562],{"id":130561},"spelling-grammar-checking","Spelling & Grammar Checking",[22,130564,130565],{},"I feel like an important aspect of being productive is to know your strengths and weaknesses. If you can spend more of your time on your strengths and less time worrying about your weaknesses you can be more productive. I am pretty darn awful when it comes to spelling and grammar and this is where I need help. So until someone wants to volunteer to be my full-time editor I will rely on tools & services to help me out with this.",[26,130567,130569],{"id":130568},"visual-studio-code-markdown","Visual Studio Code Markdown",[22,130571,130572],{},"For the last 6 months or so I have been writing documentation, tutorials, and exercises on a full-time basis in markdown using Visual Studio Code. For the most part, I really do enjoy it but there is one problem that is a deal breaker for me.",[22,130574,130575,130576,130580],{},"I am a huge fan of ",[677,130577,60111],{"href":130578,"rel":130579},"https://www.grammarly.com/",[681]," and use it for almost everything in the browser. This could be for writing an important email to the non-trivial social media status update. It is my go to service and I just can't imagine writing without it.",[22,130582,130583],{},"Well it turns out there is no way to use Grammarly in Visual Studio Code. So what I used to do is write an entire markdown document in visual studio code and the copy and paste the text content into a Google Document or Grammarly has an editor you can use.",[22,130585,130586],{},"As you can imagine this isn't very efficient and I got really tired of doing this. I have reached out to Grammarly on Twitter on a number of occasions asking them to please add support to Visual Studio Code. I am still hoping that this becomes a reality one day but until then I was on the hunt to find another process.",[22,130588,130589],{},"Another component of this that is not a deal breaker but it just bothers me from time to time is the markdown preview window. I don't like having my Markdown file open and the need for a preview window to see what the result looks like.",[22,130591,130592],{},[653,130593],{"alt":57,"src":130594},"/images/blog/2019/04/25/2019-04-25_09-37-18-fa09bb0d-9460-41fc-95ce-dbe3222c21b4.png",[26,130596,130597],{"id":85169},"Notion",[22,130599,130600,130604],{},[677,130601,130597],{"href":130602,"rel":130603},"https://www.notion.so",[681]," was one of those tools that I kept hearing about but I just didn't get it. Now granted they have made some major improvements over the last year but I remember trying it a number of times and saying I just don't get it.",[22,130606,130607],{},"The reason I kept trying it is that I kept hearing about it on podcasts and Twitter and I kept asking myself what do they know that I don't. Well, it turns out that this is one of those tools you can't really appreciate playing around with for just 5 min.",[22,130609,130610],{},"After I spent a little time over a weekend setting up Notion I really started to see the power behind it. This was a replacement for a number of tools I used and became a one-stop shop for everything I needed. Below is a screenshot of my personal Notion workspace. If you're interested in how I use Notion for almost everything let me know and I will put together a post or video walking you through how I use it.",[22,130612,130613],{},[653,130614],{"alt":57,"src":130615},"/images/blog/2019/04/25/2019-04-25_08-22-10-45f2e734-1c6b-4f79-963e-b560d0606e9b.png",[22,130617,130618,130619,130624],{},"If you're new to Notion I would also suggest checking out ",[677,130620,130623],{"href":130621,"rel":130622},"https://www.notion.so/181e961aeb5c4ee6915307c0dfd5156d",[681],"their new template gallery",". This is a great place to get some ideas and templates for your own workspace. I think most of the battle with the platform is understanding what to use it for and then how to set it up.",[636,130626,130628],{"id":130627},"blogging-on-notion","Blogging on Notion",[22,130630,130631],{},"The one feature that I absolutely love about Notion is that you can use 1 set of data and have multiple views for it. If you're not quite sure what that means, that's ok I am going to explain that now.",[22,130633,130634],{},"I have set up a kanban board for blog posts and this is where I have a view into all of my ideas, what's up next, what I am working on now and what has been completed. Having a place where you can quickly add an idea for a blog post with some notes, images or sample code is crucial for my writing process.",[22,130636,130637],{},[653,130638],{"alt":57,"src":130639},"/images/blog/2019/04/25/2019-04-25_08-25-43-599d95ee-f20d-478c-9f84-41fcca921b54.png",[22,130641,130642],{},"I did something like this in Asana but over there I had to create a new content calendar to see what would be posted and when.",[22,130644,130645],{},"In Notion, you can set up some properties for each new post, which essentially is just a new page. This allows me to define a publish date for the post for when I expect this article to go live.",[22,130647,130648],{},[653,130649],{"alt":57,"src":130650},"/images/blog/2019/04/25/2019-04-25_08-28-37-4a42f7e2-b6de-4dd3-bfa1-ecbc46a972d5.png",[22,130652,130653],{},"So far nothing new but he is where the real power of Notion comes into play. You can create a different view of the same set of data. So now I can create a new calendar view for by blog posts and based on the published date I can get a calendar view of all my blog posts. This really helps me stay on track and let's me know what I have published and when.",[22,130655,130656],{},[653,130657],{"alt":57,"src":130658},"/images/blog/2019/04/25/2019-04-25_08-30-45-9f7af7e4-476e-4777-a79c-76197151e32a.png",[22,130660,130661],{},"I also like using this view to see if I have been posting about a certain topic too frequently. I try and break my posts up so it's not focused on a single topic and this really helps out.",[636,130663,130665],{"id":130664},"markdown-block-editor","Markdown & Block Editor",[22,130667,130668],{},"I really enjoy the writing process in Notion. Everything you do is just a block so when you start writing it's a block of text. If you hit enter and you know you're going to add a new headline (or any block type for that matter) you just type a forward slash (/) and you are given a list of block types. This is just a sample of the types of blocks you can use when creating a page in Notion.",[915,130670,130671,130674,130677,130680,130683,130686,130689,130692,130695,130698,130701,130704,130707,130710],{},[37,130672,130673],{},"Text",[37,130675,130676],{},"Page",[37,130678,130679],{},"To-do List",[37,130681,130682],{},"Heading 1, 2 & 3",[37,130684,130685],{},"Bulleted, Numbered & Toggle List",[37,130687,130688],{},"Quote",[37,130690,130691],{},"Link to Page",[37,130693,130694],{},"Mention a Person or Page",[37,130696,130697],{},"Date or Reminder",[37,130699,130700],{},"Database (A large list of blocks here)",[37,130702,130703],{},"Images, Bookmarks, video, audio",[37,130705,130706],{},"Code",[37,130708,130709],{},"Files",[37,130711,130712],{},"Embeds (Things like tweets, Gists, Google Maps & More)",[22,130714,130715],{},"Most of my blog posts are text, images and code. So I really appreciate that working with these content types are so easy. Here is a code block from a previous blog post. I enjoy the nice code formatting out of the box and the ability to determine what langauge that code is.",[22,130717,130718],{},[653,130719],{"alt":57,"src":130720},"/images/blog/2019/04/25/2019-04-25_08-36-25-5ba41d92-9cfd-460c-9bb8-cec0acff8574.png",[22,130722,130723],{},"The other really nice part of the writing process I enjoy is that because it's web-based I can do it from any computer or phone. Yes, I use my phone sometimes to write something I don't want to forget or adding a link to the article so I remember to reference it later.",[636,130725,130727],{"id":130726},"grammarly-support","Grammarly Support",[22,130729,130730],{},"Remember when I said that Grammarly support was a must? Well the good news is that it works, sorta. When I first started writing in Notion it didn't work at all. The Chrome plugin would kick in and show me suggestions but whenever I tried to apply that suggestion something in Notion would kick in and revert it back to what it was. The other problem is that it sometimes takes awhile for the plugin to notice problems.",[22,130732,130733],{},"The weird thing is that just this week it sorta started working for me. Sometimes I have to hard refresh the page but when I do get Grammarly suggestions now I can hover over them and accept the change. I reached out to Notion and asked them if they fixed anything and they did not.",[22,130735,130736],{},"While this sorta works it's not working as I would hope and this is something I absolutely need going forward so I hope they are working on this.",[636,130738,130740],{"id":130739},"export-to-markdown","Export to Markdown",[22,130742,130743],{},"When my blog post is ready to go I just go to the upper right hand corner, click on the 3 little dots and export as Markdown. It can be confusing as there is also an option to Export All as Markdown. What that feature is for is in the situation where you have pages embedded inside of pages.",[22,130745,130746],{},[653,130747],{"alt":57,"src":130748},"/images/blog/2019/04/25/2019-04-25_08-56-24-1d34f10b-cdc5-428b-9f67-5340472771e5.png",[22,130750,130751,130752,130756],{},"What I really appreciate about the export is that I have all of my code and images in Markdown and I am ready to post. From there I will generate a new blog post using a custom generator ",[677,130753,130755],{"href":125988,"rel":130754},[681],"I wrote and blogged about here",". Then I copy all of my images into my new post folder and all of the markdown into the new markdown file my generator created.",[636,130758,130760],{"id":130759},"whats-missing","What's Missing",[22,130762,130763],{},"This process is far from perfect and there are some things that don't work well and some things I wish they would add. As I mentioned earlier the Grammarly support is a must for me and I hope they can fix that up a bit. The export as Markdown works but it isn't perfect.",[22,130765,130766],{},"The code blocks don't export in Markdown format so there are no backticks with the language definition around them. This means that I have to go through and manually fix these before I post which is a bit tedious and annoying.",[22,130768,130769],{},"The other part of the export process is that when I create a table it doesn't export it as a simple table in Markdown format. It thinks of the table as a database and therefore exports it as another page and this doesn't work for me.",[636,130771,130773],{"id":130772},"wish-list","Wish List",[22,130775,130776],{},"Notion asked me on Twitter what would be the top 5 things on my wish list would be and here is how I responded.",[22,130778,130779],{},[653,130780],{"alt":57,"src":130781},"/images/blog/2019/04/25/2019-04-25_08-47-50-93f60af8-b691-41ce-ab49-e30761f57c9c.png",[22,130783,130784,130785,130790],{},"A few of those I have already talked about but I want to talk quickly about the last 2. I am a huge fan of mind maps as a teaching tool. I ",[677,130786,130789],{"href":130787,"rel":130788},"https://coggle.it/diagram/WqgTTNMJtPiHph_q/t/java-development-in-2018",[681],"created this map"," of what to learn as a Java Developer in 2018 and it was really well received.",[22,130792,130793],{},[653,130794],{"alt":57,"src":130795},"/images/blog/2019/04/25/2019-04-25_09-14-56-c49f496f-084f-46e1-b4dc-05484696df21.png",[22,130797,130798],{},"I would love the ability to create Mind Maps within Notion and then include them in my posts. I'm still not sure if that would mean Notion creating a whole new tool for Mind Mapping or just embedding something like Coggle.",[22,130800,130801],{},"The other feature I would love to see is a drawing block. If I had an iPad Pro with a pencil and I could quickly sketch something out or add a drawing to a post I think that would be super cool. I am not the greatest artist but I could see this coming in super handy sometimes.",[26,130803,130805],{"id":130804},"cross-posting-on-devto","Cross posting on dev.to",[22,130807,130808,130809,130813],{},"The final piece of the blogging process is to cross-post my article on ",[677,130810,130811],{"href":130811,"rel":130812},"https://www.dev.to",[681],". If you're not familiar with DEV it's a great community for developers. I found that when I posted my articles on platforms like Reddit I got a lot of hate thrown at me for being a spammer. I mean my articles are meant to help others, what part of this process is spamming?",[22,130815,130816],{},"DEV allows me to not only post my articles but I get some really great feedback from the community. Everyone there is so nice and I have never had the engagement I do there on any other platform. I usually wait a day or 2 after it appears on my blog to post it there but when I do DEV allows me to add a canonical URL to the front matter for each post.",[22,130818,130819,130820,130824],{},"I also create all of my blog post cover images using the dimensions of 1000w x 492h because this is the size DEV would like them in. That size works out well for my blog as well but now I have a cover image for both platforms. If you haven't checked out ",[677,130821,37657],{"href":130822,"rel":130823},"https://dev.to",[681]," yet, I highly recommend doing so.",[22,130826,130827],{},"Because you can add custom properties to each post I have even added a DEV publish date so I know when this will go live there.",[22,130829,130830],{},[653,130831],{"alt":57,"src":130832},"/images/blog/2019/04/25/2019-04-25_09-42-53-41efac07-a9a3-48c2-8ac4-4c1151d89b1a.png",[26,130834,1499],{"id":1498},[22,130836,130837],{},"So that is my blogging process which I am sure will change again at some point but for now, I am really happy. I would love to hear from you on what your process is and any tips and tricks you are willing to share. Are you using Notion for anything, what are your thoughts on it as a tool and or a blogging platform? Until next time...",[22,130839,36004,130840,82545],{},[36006,130841],{},{"title":57,"searchDepth":76,"depth":76,"links":130843},[130844,130849,130850,130858,130859],{"id":130526,"depth":76,"text":130527,"children":130845},[130846,130847,130848],{"id":130547,"depth":82,"text":130538},{"id":29692,"depth":82,"text":130541},{"id":130561,"depth":82,"text":130562},{"id":130568,"depth":76,"text":130569},{"id":85169,"depth":76,"text":130597,"children":130851},[130852,130853,130854,130855,130856,130857],{"id":130627,"depth":82,"text":130628},{"id":130664,"depth":82,"text":130665},{"id":130726,"depth":82,"text":130727},{"id":130739,"depth":82,"text":130740},{"id":130759,"depth":82,"text":130760},{"id":130772,"depth":82,"text":130773},{"id":130804,"depth":76,"text":130805},{"id":1498,"depth":76,"text":1499},{"_id":130861,"path":130862,"title":130863,"description":130864,"meta":130865,"body":130870},"content/blog/2019/04/23/gridsome-blog-post-generator.md","/blog/2019/04/23/gridsome-blog-post-generator","Gridsome Blog Post Generator","In this article I will show you how to crate a blog post generator for your static site.",{"slug":130866,"date":130867,"published":13,"tags":130868,"author":17,"cover":130869,"excerpt":-1},"gridsome-blog-post-generator","2019-04-23T19:28:51.186Z",[105462],"nick-morrison-FHnnjk1Yj7Y-unsplash.jpg",{"type":19,"value":130871,"toc":132358},[130872,130884,130887,130890,130893,130897,130900,130907,130910,130949,130952,130984,130987,131016,131022,131055,131060,131074,131077,131091,131095,131098,131109,131112,131120,131123,131126,131135,131166,131169,131185,131188,131211,131219,131235,131243,131246,131426,131433,131436,131444,131447,131474,131477,131480,131484,131487,131491,131494,131503,131543,131547,131550,131559,131562,131698,131705,131739,131742,131782,131786,131794,131816,131820,131823,131853,131877,131921,131925,131928,131948,131956,132059,132061,132064,132150,132159,132210,132213,132222,132238,132241,132299,132302,132339,132341,132344,132352,132355],[22,130873,130874,130875,34867,130880,130883],{},"If you're using something like ",[677,130876,130879],{"href":130877,"rel":130878},"https://www.gatsbyjs.org/",[681],"Gatsby",[677,130881,104836],{"href":104834,"rel":130882},[681]," as your blogging platform there is no UI to create a new blog post. The process usually starts with creating a directory or series of directories depending on your post format and then creating a new markdown file.",[22,130885,130886],{},"From there you have to add a bunch front matter that describes your post. This is usually specific to your blog requirements but you might have things like title, slug, author, date and so on.",[22,130888,130889],{},"This become tedious and even worse a copy/paste project over and over again which I don't like to do. In fact anytime I catch myself copying/pasting something more then a couple times it's probably time to find a solution to that problem.",[22,130891,130892],{},"In this article I am going to walk you through the blog post generator that I wrote. There have been a few iterations of this script and I have definitely learned a few tricks from others who have done something similar.",[26,130894,130896],{"id":130895},"creating-initializing-the-script","Creating & Initializing the Script",[22,130898,130899],{},"The first thing you need to decide is where is this script going to go. There really is no wrong or right answer so for me, I just created a folder off of my root called scripts. I figured this can be a place for random scripts I might need and If I find a better place for them later I can refactor. This, by the way, is something I do every single time I write code, find a fast way to get it done and make it work and make it pretty later on.",[22,130901,130902,130903,130906],{},"The first thing I am going to do is to create a script called ",[59,130904,130905],{},"newpost.js"," in my scripts folder. Next, we need to how this script is going to be structured. In some cases, we could just write it top down but for this instance, it won't work.",[22,130908,130909],{},"It is common to wrap code in a function and execute that function and there are a few ways that we could do that. We could just write a normal function that contains all of our logic and then call that function at the end of the script to kick things off.",[52,130911,130913],{"className":32791,"code":130912,"language":32793,"meta":57,"style":57},"function newPost() {\n console.log(\"create new post...\");\n}\nnewPost();\n",[59,130914,130915,130924,130938,130942],{"__ignoreMap":57},[62,130916,130917,130919,130922],{"class":64,"line":65},[62,130918,21046],{"class":68},[62,130920,130921],{"class":122}," newPost",[62,130923,206],{"class":72},[62,130925,130926,130929,130931,130933,130936],{"class":64,"line":76},[62,130927,130928],{"class":72}," console.",[62,130930,58271],{"class":122},[62,130932,2109],{"class":72},[62,130934,130935],{"class":1675},"\"create new post...\"",[62,130937,1133],{"class":72},[62,130939,130940],{"class":64,"line":82},[62,130941,379],{"class":72},[62,130943,130944,130947],{"class":64,"line":89},[62,130945,130946],{"class":122},"newPost",[62,130948,822],{"class":72},[22,130950,130951],{},"If all you're going to do though is call the function there is a better way of approaching this. You can write what's called a Self Executing Function, also referred to as an Immediately Invoked Function Expression or IIFE. To accomplish that",[52,130953,130955],{"className":32791,"code":130954,"language":32793,"meta":57,"style":57},"(function newPost() {\n console.log(\"create new post...\");\n})();\n",[59,130956,130957,130967,130979],{"__ignoreMap":57},[62,130958,130959,130961,130963,130965],{"class":64,"line":65},[62,130960,2109],{"class":72},[62,130962,21046],{"class":68},[62,130964,130921],{"class":122},[62,130966,206],{"class":72},[62,130968,130969,130971,130973,130975,130977],{"class":64,"line":76},[62,130970,130928],{"class":72},[62,130972,58271],{"class":122},[62,130974,2109],{"class":72},[62,130976,130935],{"class":1675},[62,130978,1133],{"class":72},[62,130980,130981],{"class":64,"line":82},[62,130982,130983],{"class":72},"})();\n",[22,130985,130986],{},"You can also write this using an arrow function",[52,130988,130990],{"className":32791,"code":130989,"language":32793,"meta":57,"style":57},"(() => {\n console.log(\"create new post...\");\n})();\n",[59,130991,130992,131000,131012],{"__ignoreMap":57},[62,130993,130994,130996,130998],{"class":64,"line":65},[62,130995,797],{"class":72},[62,130997,21525],{"class":68},[62,130999,126],{"class":72},[62,131001,131002,131004,131006,131008,131010],{"class":64,"line":76},[62,131003,130928],{"class":72},[62,131005,58271],{"class":122},[62,131007,2109],{"class":72},[62,131009,130935],{"class":1675},[62,131011,1133],{"class":72},[62,131013,131014],{"class":64,"line":82},[62,131015,130983],{"class":72},[22,131017,131018,131019,131021],{},"And just like any normal function if you're going to be performing an asynchronous task you can use the ",[59,131020,21516],{}," keyword. In this case we are going to pull in a library to help us write our command line application so we are going to start with the following.",[52,131023,131025],{"className":32791,"code":131024,"language":32793,"meta":57,"style":57},"(async () => {\n console.log(\"create new post...\");\n})();\n",[59,131026,131027,131039,131051],{"__ignoreMap":57},[62,131028,131029,131031,131033,131035,131037],{"class":64,"line":65},[62,131030,2109],{"class":72},[62,131032,21516],{"class":68},[62,131034,127483],{"class":72},[62,131036,21525],{"class":68},[62,131038,126],{"class":72},[62,131040,131041,131043,131045,131047,131049],{"class":64,"line":76},[62,131042,130928],{"class":72},[62,131044,58271],{"class":122},[62,131046,2109],{"class":72},[62,131048,130935],{"class":1675},[62,131050,1133],{"class":72},[62,131052,131053],{"class":64,"line":82},[62,131054,130983],{"class":72},[22,131056,131057,131058],{},"Before you can test this out you need to add a new script to your ",[59,131059,32733],{},[52,131061,131063],{"className":32791,"code":131062,"language":32793,"meta":57,"style":57},"\"newpost\": \"node ./scripts/newpost.js\"\n",[59,131064,131065],{"__ignoreMap":57},[62,131066,131067,131070,131072],{"class":64,"line":65},[62,131068,131069],{"class":1675},"\"newpost\"",[62,131071,3696],{"class":72},[62,131073,126079],{"class":1675},[22,131075,131076],{},"At this point I would give the script a quick test just to make sure everything is working as expected.",[52,131078,131080],{"className":1663,"code":131079,"language":1665,"meta":57,"style":57},"npm run newpost\n",[59,131081,131082],{"__ignoreMap":57},[62,131083,131084,131086,131088],{"class":64,"line":65},[62,131085,32645],{"class":122},[62,131087,1716],{"class":1675},[62,131089,131090],{"class":1675}," newpost\n",[26,131092,131094],{"id":131093},"accepting-user-input","Accepting user input",[22,131096,131097],{},"Now that you have script ready to go it's time to start building out some functionality. The first thing you need to do is to ask for some details about the new post. This is obviously going to be different for everyone based on your needs but here are the pieces of data I want to ask for and the pieces of data I can infer.",[915,131099,131100,131103,131106],{},[37,131101,131102],{},"Title",[37,131104,131105],{},"Excerpt",[37,131107,131108],{},"Tags",[22,131110,131111],{},"These are a list of items that I can determine based off of what the user entered for above or when the script was run.",[915,131113,131114,131116,131118],{},[37,131115,41300],{},[37,131117,46398],{},[37,131119,99601],{},[22,131121,131122],{},"And that is really all I need to get started. As I said before this might be different for you but you can adjust accordingly.",[636,131124,131125],{"id":131125},"inquirer",[22,131127,131128,131129,131134],{},"To help with asking for user input we are going to ",[677,131130,131133],{"href":131131,"rel":131132},"https://www.npmjs.com/package/inquirer",[681],"install the package inquirer",". Inquirer is a collection of common interactive command line user interfaces. Inquirer should ease the process of:",[915,131136,131137,131143,131148,131154,131160],{},[37,131138,131139,131140],{},"providing ",[4534,131141,131142],{},"error feedback",[37,131144,131145],{},[4534,131146,131147],{},"asking questions",[37,131149,131150,131153],{},[4534,131151,131152],{},"parsing"," input",[37,131155,131156,131159],{},[4534,131157,131158],{},"validating"," answers",[37,131161,131162,131163],{},"managing ",[4534,131164,131165],{},"hierarchical prompts",[22,131167,131168],{},"To get started you can install it as a dev dependency by running the following command:",[52,131170,131172],{"className":1663,"code":131171,"language":1665,"meta":57,"style":57},"npm install -D inquirer\n",[59,131173,131174],{"__ignoreMap":57},[62,131175,131176,131178,131180,131182],{"class":64,"line":65},[62,131177,32645],{"class":122},[62,131179,32750],{"class":1675},[62,131181,32753],{"class":149},[62,131183,131184],{"class":1675}," inquirer\n",[22,131186,131187],{},"and require it in your script",[52,131189,131191],{"className":32791,"code":131190,"language":32793,"meta":57,"style":57},"const inquirer = require(\"inquirer\");\n",[59,131192,131193],{"__ignoreMap":57},[62,131194,131195,131197,131200,131202,131204,131206,131209],{"class":64,"line":65},[62,131196,110541],{"class":68},[62,131198,131199],{"class":149}," inquirer",[62,131201,2556],{"class":68},[62,131203,120735],{"class":122},[62,131205,2109],{"class":72},[62,131207,131208],{"class":1675},"\"inquirer\"",[62,131210,1133],{"class":72},[22,131212,131213,131214,131218],{},"This package can do at lot more than we will use it for so if you have a chance ",[677,131215,131217],{"href":131131,"rel":131216},[681],"check out the documentation",". The first thing you need to do is ask for the process arguments.",[52,131220,131222],{"className":32791,"code":131221,"language":32793,"meta":57,"style":57},"const args = process.argv;\n",[59,131223,131224],{"__ignoreMap":57},[62,131225,131226,131228,131230,131232],{"class":64,"line":65},[62,131227,110541],{"class":68},[62,131229,62255],{"class":149},[62,131231,2556],{"class":68},[62,131233,131234],{"class":72}," process.argv;\n",[29685,131236,131237],{},[22,131238,131239,131240,131242],{},"The process.argv property returns an array containing the command line arguments passed when the Node.js process was launched. The first element will be process.execPath. See process.argv0 if access to the original value of argv",[62,131241,1130],{}," is needed. The second element will be the path to the JavaScript file being executed. The remaining elements will be any additional command line arguments.",[22,131244,131245],{},"If you wanted to you could check for the existence of user supplied arguments and take those in but in this example I am going to say as long as there are no custom arguments let's ask the user for some data.",[52,131247,131249],{"className":32791,"code":131248,"language":32793,"meta":57,"style":57},"if (args.length \u003C 3) {\n const { title, excerpt, tags } = await inquirer.prompt([\n {\n type: \"input\",\n name: \"title\",\n message: \"Post Title:\"\n },\n {\n type: \"input\",\n name: \"excerpt\",\n message: \"Post description:\"\n },\n {\n type: \"input\",\n name: \"tags\",\n message: \"Tags (comma separated):\"\n }\n ]);\n} else {\n log(error(\"Please don't provide any arguments to the new post generator\"));\n}\n",[59,131250,131251,131266,131297,131301,131311,131319,131326,131330,131334,131342,131351,131358,131362,131366,131374,131382,131389,131393,131398,131406,131422],{"__ignoreMap":57},[62,131252,131253,131255,131258,131260,131262,131264],{"class":64,"line":65},[62,131254,34116],{"class":68},[62,131256,131257],{"class":72}," (args.",[62,131259,14193],{"class":149},[62,131261,5607],{"class":68},[62,131263,21946],{"class":149},[62,131265,768],{"class":72},[62,131267,131268,131270,131272,131274,131276,131279,131281,131283,131285,131287,131289,131292,131294],{"class":64,"line":76},[62,131269,85414],{"class":68},[62,131271,21785],{"class":72},[62,131273,3196],{"class":149},[62,131275,976],{"class":72},[62,131277,131278],{"class":149},"excerpt",[62,131280,976],{"class":72},[62,131282,41306],{"class":149},[62,131284,21795],{"class":72},[62,131286,146],{"class":68},[62,131288,21707],{"class":68},[62,131290,131291],{"class":72}," inquirer.",[62,131293,2204],{"class":122},[62,131295,131296],{"class":72},"([\n",[62,131298,131299],{"class":64,"line":82},[62,131300,49857],{"class":72},[62,131302,131303,131306,131309],{"class":64,"line":89},[62,131304,131305],{"class":72}," type: ",[62,131307,131308],{"class":1675},"\"input\"",[62,131310,3338],{"class":72},[62,131312,131313,131315,131317],{"class":64,"line":95},[62,131314,120974],{"class":72},[62,131316,42161],{"class":1675},[62,131318,3338],{"class":72},[62,131320,131321,131323],{"class":64,"line":101},[62,131322,129432],{"class":72},[62,131324,131325],{"class":1675},"\"Post Title:\"\n",[62,131327,131328],{"class":64,"line":107},[62,131329,50124],{"class":72},[62,131331,131332],{"class":64,"line":113},[62,131333,49857],{"class":72},[62,131335,131336,131338,131340],{"class":64,"line":129},[62,131337,131305],{"class":72},[62,131339,131308],{"class":1675},[62,131341,3338],{"class":72},[62,131343,131344,131346,131349],{"class":64,"line":134},[62,131345,120974],{"class":72},[62,131347,131348],{"class":1675},"\"excerpt\"",[62,131350,3338],{"class":72},[62,131352,131353,131355],{"class":64,"line":156},[62,131354,129432],{"class":72},[62,131356,131357],{"class":1675},"\"Post description:\"\n",[62,131359,131360],{"class":64,"line":161},[62,131361,50124],{"class":72},[62,131363,131364],{"class":64,"line":167},[62,131365,49857],{"class":72},[62,131367,131368,131370,131372],{"class":64,"line":173},[62,131369,131305],{"class":72},[62,131371,131308],{"class":1675},[62,131373,3338],{"class":72},[62,131375,131376,131378,131380],{"class":64,"line":179},[62,131377,120974],{"class":72},[62,131379,42220],{"class":1675},[62,131381,3338],{"class":72},[62,131383,131384,131386],{"class":64,"line":185},[62,131385,129432],{"class":72},[62,131387,131388],{"class":1675},"\"Tags (comma separated):\"\n",[62,131390,131391],{"class":64,"line":191},[62,131392,223],{"class":72},[62,131394,131395],{"class":64,"line":209},[62,131396,131397],{"class":72}," ]);\n",[62,131399,131400,131402,131404],{"class":64,"line":220},[62,131401,63241],{"class":72},[62,131403,12783],{"class":68},[62,131405,126],{"class":72},[62,131407,131408,131411,131413,131415,131417,131420],{"class":64,"line":226},[62,131409,131410],{"class":122}," log",[62,131412,2109],{"class":72},[62,131414,21875],{"class":122},[62,131416,2109],{"class":72},[62,131418,131419],{"class":1675},"\"Please don't provide any arguments to the new post generator\"",[62,131421,6979],{"class":72},[62,131423,131424],{"class":64,"line":231},[62,131425,379],{"class":72},[22,131427,131428,131429,131432],{},"We will talk about the log line in a bit but for now let's focus on inquirer. When we were setting up the script I said that we needed to mark the self executing function as async and this is why. ",[59,131430,131431],{},"inquirer.prompt"," returns a promise so we will use await here.",[22,131434,131435],{},"We are asking for 3 different pieces of data from the user",[915,131437,131438,131440,131442],{},[37,131439,3196],{},[37,131441,131278],{},[37,131443,41306],{},[22,131445,131446],{},"We could have just created a single variable to hold the responses but instead we are destructuring the responses into 3 variables.",[52,131448,131450],{"className":32791,"code":131449,"language":32793,"meta":57,"style":57},"const { title, excerpt, tags } = ...\n",[59,131451,131452],{"__ignoreMap":57},[62,131453,131454,131456,131458,131460,131462,131464,131466,131468,131470,131472],{"class":64,"line":65},[62,131455,110541],{"class":68},[62,131457,21785],{"class":72},[62,131459,3196],{"class":149},[62,131461,976],{"class":72},[62,131463,131278],{"class":149},[62,131465,976],{"class":72},[62,131467,41306],{"class":149},[62,131469,21795],{"class":72},[62,131471,146],{"class":68},[62,131473,53674],{"class":68},[22,131475,131476],{},"Each object in the array argument supplied to the prompt method is a Question. In our example we are asking for simple input, defining the name of the question and what the message should display to the user. Again these can get much more complex so check out the documentation if you have more specific needs.",[22,131478,131479],{},"Now that we have the answers from our user we can use those to build out our new post.",[26,131481,131483],{"id":131482},"creating-the-post-directory","Creating the post directory",[22,131485,131486],{},"Before we start to create any folder or files you need to do a little more setup.",[636,131488,131490],{"id":131489},"post-slug","Post Slug",[22,131492,131493],{},"Now that I have the title of the post I need to create a slug. A slug is a URL friendly version of my title that helps when it comes to SEO. If the title of my post was 'My First Post' a slug would be 'my-first-post'.",[22,131495,131496,131497,131502],{},"Now in this simple example this is something we could probably handle on our own but this can get pretty complex. For this, I am going to install in a ",[677,131498,131501],{"href":131499,"rel":131500},"https://www.npmjs.com/package/slugify",[681],"package called slugify",", require it and then create a slug.",[52,131504,131506],{"className":32791,"code":131505,"language":32793,"meta":57,"style":57},"const slugify = require(\"slugify\");\n\nconst slug = slugify(title);\n",[59,131507,131508,131526,131530],{"__ignoreMap":57},[62,131509,131510,131512,131515,131517,131519,131521,131524],{"class":64,"line":65},[62,131511,110541],{"class":68},[62,131513,131514],{"class":149}," slugify",[62,131516,2556],{"class":68},[62,131518,120735],{"class":122},[62,131520,2109],{"class":72},[62,131522,131523],{"class":1675},"\"slugify\"",[62,131525,1133],{"class":72},[62,131527,131528],{"class":64,"line":76},[62,131529,79],{"emptyLinePlaceholder":13},[62,131531,131532,131534,131537,131539,131541],{"class":64,"line":82},[62,131533,110541],{"class":68},[62,131535,131536],{"class":149}," slug",[62,131538,2556],{"class":68},[62,131540,131514],{"class":122},[62,131542,110710],{"class":72},[636,131544,131546],{"id":131545},"folder-url-format","Folder & URL Format",[22,131548,131549],{},"Each of my blog posts use the following format",[52,131551,131553],{"className":15773,"code":131552,"language":15775,"meta":57,"style":57},"https://www.danvega.dev/{year}/{month}/{day}/{slug}\n",[59,131554,131555],{"__ignoreMap":57},[62,131556,131557],{"class":64,"line":65},[62,131558,131552],{"class":72},[22,131560,131561],{},"So far we have the slug but now I need to extract some parts for the date. Since we are running the generator right now I am going to assume we want to post this today and use that as the basis for our date. You would think this would be easier but working with dates is one of those things that never seems easy in any language.",[52,131563,131565],{"className":32791,"code":131564,"language":32793,"meta":57,"style":57},"const createdOn = new Date();\nconst year = createdOn.getFullYear();\nconst month = `${createdOn.getMonth() + 1 \u003C 10 ? \"0\" : \"\"}${createdOn.getMonth() + 1}`;\nconst day = `${createdOn.getDate() \u003C 10 ? \"0\" : \"\"}${createdOn.getDate()}`;\n",[59,131566,131567,131582,131599,131654],{"__ignoreMap":57},[62,131568,131569,131571,131574,131576,131578,131580],{"class":64,"line":65},[62,131570,110541],{"class":68},[62,131572,131573],{"class":149}," createdOn",[62,131575,2556],{"class":68},[62,131577,466],{"class":68},[62,131579,61498],{"class":122},[62,131581,822],{"class":72},[62,131583,131584,131586,131589,131591,131594,131597],{"class":64,"line":76},[62,131585,110541],{"class":68},[62,131587,131588],{"class":149}," year",[62,131590,2556],{"class":68},[62,131592,131593],{"class":72}," createdOn.",[62,131595,131596],{"class":122},"getFullYear",[62,131598,822],{"class":72},[62,131600,131601,131603,131606,131608,131610,131613,131615,131618,131620,131622,131624,131626,131628,131630,131633,131635,131638,131640,131642,131644,131646,131648,131650,131652],{"class":64,"line":82},[62,131602,110541],{"class":68},[62,131604,131605],{"class":149}," month",[62,131607,2556],{"class":68},[62,131609,121072],{"class":1675},[62,131611,131612],{"class":72},"createdOn",[62,131614,2755],{"class":1675},[62,131616,131617],{"class":122},"getMonth",[62,131619,5398],{"class":1675},[62,131621,1148],{"class":68},[62,131623,22038],{"class":149},[62,131625,5607],{"class":68},[62,131627,30327],{"class":149},[62,131629,64807],{"class":68},[62,131631,131632],{"class":1675}," \"0\"",[62,131634,57090],{"class":68},[62,131636,131637],{"class":1675}," \"\"}${",[62,131639,131612],{"class":72},[62,131641,2755],{"class":1675},[62,131643,131617],{"class":122},[62,131645,5398],{"class":1675},[62,131647,1148],{"class":68},[62,131649,22038],{"class":149},[62,131651,21727],{"class":1675},[62,131653,153],{"class":72},[62,131655,131656,131658,131661,131663,131665,131667,131669,131671,131673,131675,131677,131679,131681,131683,131685,131687,131689,131691,131694,131696],{"class":64,"line":89},[62,131657,110541],{"class":68},[62,131659,131660],{"class":149}," day",[62,131662,2556],{"class":68},[62,131664,121072],{"class":1675},[62,131666,131612],{"class":72},[62,131668,2755],{"class":1675},[62,131670,42183],{"class":122},[62,131672,5398],{"class":1675},[62,131674,760],{"class":68},[62,131676,30327],{"class":149},[62,131678,64807],{"class":68},[62,131680,131632],{"class":1675},[62,131682,57090],{"class":68},[62,131684,131637],{"class":1675},[62,131686,131612],{"class":72},[62,131688,2755],{"class":1675},[62,131690,42183],{"class":122},[62,131692,131693],{"class":1675},"()",[62,131695,21727],{"class":1675},[62,131697,153],{"class":72},[22,131699,131700,131701,131704],{},"Now that we have our date parts we can create a variable called ",[59,131702,131703],{},"blogPostFolder"," that will be a path to the folder where the new markdown file will be created.",[52,131706,131708],{"className":32791,"code":131707,"language":32793,"meta":57,"style":57},"const blogPostFolder = `./blog/${year}/${month}/${day}`;\n",[59,131709,131710],{"__ignoreMap":57},[62,131711,131712,131714,131717,131719,131722,131724,131727,131730,131732,131735,131737],{"class":64,"line":65},[62,131713,110541],{"class":68},[62,131715,131716],{"class":149}," blogPostFolder",[62,131718,2556],{"class":68},[62,131720,131721],{"class":1675}," `./blog/${",[62,131723,8604],{"class":72},[62,131725,131726],{"class":1675},"}/${",[62,131728,131729],{"class":72},"month",[62,131731,131726],{"class":1675},[62,131733,131734],{"class":72},"day",[62,131736,21727],{"class":1675},[62,131738,153],{"class":72},[22,131740,131741],{},"And finally I am just going to clean up the tags and turn them into a list.",[52,131743,131745],{"className":32791,"code":131744,"language":32793,"meta":57,"style":57},"const tagsList = tags.split(\",\").map(t => t.trim());\n",[59,131746,131747],{"__ignoreMap":57},[62,131748,131749,131751,131754,131756,131759,131761,131763,131765,131767,131769,131771,131773,131775,131778,131780],{"class":64,"line":65},[62,131750,110541],{"class":68},[62,131752,131753],{"class":149}," tagsList",[62,131755,2556],{"class":68},[62,131757,131758],{"class":72}," tags.",[62,131760,6894],{"class":122},[62,131762,2109],{"class":72},[62,131764,75727],{"class":1675},[62,131766,15503],{"class":72},[62,131768,4200],{"class":122},[62,131770,2109],{"class":72},[62,131772,85080],{"class":889},[62,131774,85402],{"class":68},[62,131776,131777],{"class":72}," t.",[62,131779,110699],{"class":122},[62,131781,1091],{"class":72},[26,131783,131785],{"id":131784},"creating-files-folders","Creating Files & Folders",[22,131787,131788,131789,2755],{},"Now that you have all the variables in place it's time to start creating some files and folders. To do so you need to require the ",[677,131790,131793],{"href":131791,"rel":131792},"https://nodejs.org/api/fs.html",[681],"File System Module",[52,131795,131797],{"className":32791,"code":131796,"language":32793,"meta":57,"style":57},"const fs = require(\"fs\");\n",[59,131798,131799],{"__ignoreMap":57},[62,131800,131801,131803,131805,131807,131809,131811,131814],{"class":64,"line":65},[62,131802,110541],{"class":68},[62,131804,120730],{"class":149},[62,131806,2556],{"class":68},[62,131808,120735],{"class":122},[62,131810,2109],{"class":72},[62,131812,131813],{"class":1675},"\"fs\"",[62,131815,1133],{"class":72},[636,131817,131819],{"id":131818},"creating-recursive-directories-in-node","Creating Recursive Directories in node",[22,131821,131822],{},"We already created a variable for our blog post folder location so let's start there. The first thing you will want to do is to check to see if it already exists because if it does we don't need to create it. This will almost never be the case for me because It is hard enough for me to crank out 1 per week but let's play it safe in case I get ambitious one day.",[52,131824,131826],{"className":32791,"code":131825,"language":32793,"meta":57,"style":57},"if (!fs.existsSync(blogPostFolder)) {\n // create directory\n}\n",[59,131827,131828,131844,131849],{"__ignoreMap":57},[62,131829,131830,131832,131834,131836,131839,131841],{"class":64,"line":65},[62,131831,34116],{"class":68},[62,131833,744],{"class":72},[62,131835,6277],{"class":68},[62,131837,131838],{"class":72},"fs.",[62,131840,121031],{"class":122},[62,131842,131843],{"class":72},"(blogPostFolder)) {\n",[62,131845,131846],{"class":64,"line":76},[62,131847,131848],{"class":85}," // create directory\n",[62,131850,131851],{"class":64,"line":82},[62,131852,379],{"class":72},[22,131854,131855,131856,131860,131861,131864,131865,131867,131868,131870,131871,131876],{},"This is the tricky part that can trip some people up and indeed got me the first time. If you're just creating a single directory ",[677,131857,121138],{"href":131858,"rel":131859},"https://nodejs.org/api/fs.html#fs_fs_mkdirsync_path_options",[681]," with no options will work just fine. What I mean by that is let's say that you already have the folder ",[59,131862,131863],{},"blog/2019/04/"," created and you just needed to create the day ",[59,131866,45730],{}," folder than this will work fine. If you need to recursively (more than 1 level deep) create folders you need to pass an option to the ",[59,131869,121138],{}," method. I ",[677,131872,131875],{"href":131873,"rel":131874},"https://www.danvega.dev/blog/2019/02/20/node-recursive-directories",[681],"wrote an article"," that goes a little more into this if you're interested.",[52,131878,131880],{"className":32791,"code":131879,"language":32793,"meta":57,"style":57},"if (!fs.existsSync(blogPostFolder)) {\n fs.mkdirSync(blogPostFolder, {\n recursive: true\n });\n}\n",[59,131881,131882,131896,131906,131913,131917],{"__ignoreMap":57},[62,131883,131884,131886,131888,131890,131892,131894],{"class":64,"line":65},[62,131885,34116],{"class":68},[62,131887,744],{"class":72},[62,131889,6277],{"class":68},[62,131891,131838],{"class":72},[62,131893,121031],{"class":122},[62,131895,131843],{"class":72},[62,131897,131898,131901,131903],{"class":64,"line":76},[62,131899,131900],{"class":72}," fs.",[62,131902,121138],{"class":122},[62,131904,131905],{"class":72},"(blogPostFolder, {\n",[62,131907,131908,131911],{"class":64,"line":82},[62,131909,131910],{"class":72}," recursive: ",[62,131912,51914],{"class":149},[62,131914,131915],{"class":64,"line":89},[62,131916,85488],{"class":72},[62,131918,131919],{"class":64,"line":95},[62,131920,379],{"class":72},[636,131922,131924],{"id":131923},"front-matter","Front Matter",[22,131926,131927],{},"In each Markdown file, we define the blog post using something called front matter. These are variables inside of a YAML declaration block",[52,131929,131931],{"className":32791,"code":131930,"language":32793,"meta":57,"style":57},"---\nkey: value\n---\n",[59,131932,131933,131937,131944],{"__ignoreMap":57},[62,131934,131935],{"class":64,"line":65},[62,131936,121228],{"class":68},[62,131938,131939,131941],{"class":64,"line":76},[62,131940,14914],{"class":122},[62,131942,131943],{"class":72},": value\n",[62,131945,131946],{"class":64,"line":82},[62,131947,121228],{"class":68},[22,131949,131950,131951,2755],{},"To help us create the front matter we are going to bring in a package called ",[677,131952,131955],{"href":131953,"rel":131954},"https://www.npmjs.com/package/json-to-pretty-yaml",[681],"json-to-pretty-yaml",[52,131957,131959],{"className":32791,"code":131958,"language":32793,"meta":57,"style":57},"const jsToYaml = require(\"json-to-pretty-yaml\");\n\nconst yaml = jsToYaml.stringify({\n slug,\n title,\n date: createdOn.toISOString(),\n published: true,\n description: excerpt,\n author: \"Dan Vega\",\n tags: tagsList,\n cover: \"\"\n});\n",[59,131960,131961,131979,131983,131999,132004,132009,132019,132028,132033,132043,132048,132055],{"__ignoreMap":57},[62,131962,131963,131965,131968,131970,131972,131974,131977],{"class":64,"line":65},[62,131964,110541],{"class":68},[62,131966,131967],{"class":149}," jsToYaml",[62,131969,2556],{"class":68},[62,131971,120735],{"class":122},[62,131973,2109],{"class":72},[62,131975,131976],{"class":1675},"\"json-to-pretty-yaml\"",[62,131978,1133],{"class":72},[62,131980,131981],{"class":64,"line":76},[62,131982,79],{"emptyLinePlaceholder":13},[62,131984,131985,131987,131990,131992,131995,131997],{"class":64,"line":82},[62,131986,110541],{"class":68},[62,131988,131989],{"class":149}," yaml",[62,131991,2556],{"class":68},[62,131993,131994],{"class":72}," jsToYaml.",[62,131996,112339],{"class":122},[62,131998,50544],{"class":72},[62,132000,132001],{"class":64,"line":89},[62,132002,132003],{"class":72}," slug,\n",[62,132005,132006],{"class":64,"line":95},[62,132007,132008],{"class":72}," title,\n",[62,132010,132011,132014,132017],{"class":64,"line":101},[62,132012,132013],{"class":72}," date: createdOn.",[62,132015,132016],{"class":122},"toISOString",[62,132018,4651],{"class":72},[62,132020,132021,132024,132026],{"class":64,"line":107},[62,132022,132023],{"class":72}," published: ",[62,132025,21775],{"class":149},[62,132027,3338],{"class":72},[62,132029,132030],{"class":64,"line":113},[62,132031,132032],{"class":72}," description: excerpt,\n",[62,132034,132035,132038,132041],{"class":64,"line":129},[62,132036,132037],{"class":72}," author: ",[62,132039,132040],{"class":1675},"\"Dan Vega\"",[62,132042,3338],{"class":72},[62,132044,132045],{"class":64,"line":134},[62,132046,132047],{"class":72}," tags: tagsList,\n",[62,132049,132050,132053],{"class":64,"line":156},[62,132051,132052],{"class":72}," cover: ",[62,132054,123805],{"class":1675},[62,132056,132057],{"class":64,"line":161},[62,132058,85531],{"class":72},[636,132060,130541],{"id":29692},[22,132062,132063],{},"With our front matter in place it's time to create our markdown file. I am going to bring in a package called prettier to format our markdown and make it, well, prettier ☺️",[52,132065,132067],{"className":32791,"code":132066,"language":32793,"meta":57,"style":57},"const prettier = require(\"prettier\");\n\nconst markdown = prettier.format(`---\\n${yaml}\\n---\\n`, {\n parser: \"markdown\",\n singleQuote: true\n});\n",[59,132068,132069,132087,132091,132129,132139,132146],{"__ignoreMap":57},[62,132070,132071,132073,132076,132078,132080,132082,132085],{"class":64,"line":65},[62,132072,110541],{"class":68},[62,132074,132075],{"class":149}," prettier",[62,132077,2556],{"class":68},[62,132079,120735],{"class":122},[62,132081,2109],{"class":72},[62,132083,132084],{"class":1675},"\"prettier\"",[62,132086,1133],{"class":72},[62,132088,132089],{"class":64,"line":76},[62,132090,79],{"emptyLinePlaceholder":13},[62,132092,132093,132095,132098,132100,132103,132105,132107,132110,132112,132115,132117,132119,132121,132123,132125,132127],{"class":64,"line":82},[62,132094,110541],{"class":68},[62,132096,132097],{"class":149}," markdown",[62,132099,2556],{"class":68},[62,132101,132102],{"class":72}," prettier.",[62,132104,61567],{"class":122},[62,132106,2109],{"class":72},[62,132108,132109],{"class":1675},"`---",[62,132111,4501],{"class":149},[62,132113,132114],{"class":1675},"${",[62,132116,10995],{"class":72},[62,132118,90795],{"class":1675},[62,132120,4501],{"class":149},[62,132122,53645],{"class":1675},[62,132124,4501],{"class":149},[62,132126,31872],{"class":1675},[62,132128,107180],{"class":72},[62,132130,132131,132134,132137],{"class":64,"line":89},[62,132132,132133],{"class":72}," parser: ",[62,132135,132136],{"class":1675},"\"markdown\"",[62,132138,3338],{"class":72},[62,132140,132141,132144],{"class":64,"line":95},[62,132142,132143],{"class":72}," singleQuote: ",[62,132145,51914],{"class":149},[62,132147,132148],{"class":64,"line":101},[62,132149,85531],{"class":72},[22,132151,132152,132153,132155,132156,2755],{},"Now that you have the content for the file all that's left to do is create the file. You will be using the File System module again but this time you will use the ",[59,132154,121101],{}," method. You will write this file to the blog post folder that you created earlier and the slug will be the name of the file with the file extension ",[59,132157,132158],{},"md",[52,132160,132162],{"className":32791,"code":132161,"language":32793,"meta":57,"style":57},"fs.writeFileSync(`${blogPostFolder}/${slug}.md`, markdown);\n\nlog(success(`Post ${title} was created successfully`));\n",[59,132163,132164,132186,132190],{"__ignoreMap":57},[62,132165,132166,132168,132170,132172,132174,132176,132178,132180,132183],{"class":64,"line":65},[62,132167,131838],{"class":72},[62,132169,121101],{"class":122},[62,132171,2109],{"class":72},[62,132173,122404],{"class":1675},[62,132175,131703],{"class":72},[62,132177,131726],{"class":1675},[62,132179,41300],{"class":72},[62,132181,132182],{"class":1675},"}.md`",[62,132184,132185],{"class":72},", markdown);\n",[62,132187,132188],{"class":64,"line":76},[62,132189,79],{"emptyLinePlaceholder":13},[62,132191,132192,132194,132196,132198,132200,132203,132205,132208],{"class":64,"line":82},[62,132193,58271],{"class":122},[62,132195,2109],{"class":72},[62,132197,61084],{"class":122},[62,132199,2109],{"class":72},[62,132201,132202],{"class":1675},"`Post ${",[62,132204,3196],{"class":72},[62,132206,132207],{"class":1675},"} was created successfully`",[62,132209,6979],{"class":72},[26,132211,132212],{"id":36206},"Logging",[22,132214,132215,132216,132221],{},"To add some styling to my terminal logging I am using a ",[677,132217,132220],{"href":132218,"rel":132219},"https://www.npmjs.com/package/chalk",[681],"package called chalk",". To install it locally run the following command:",[52,132223,132225],{"className":1663,"code":132224,"language":1665,"meta":57,"style":57},"npm install -D chalk\n",[59,132226,132227],{"__ignoreMap":57},[62,132228,132229,132231,132233,132235],{"class":64,"line":65},[62,132230,32645],{"class":122},[62,132232,32750],{"class":1675},[62,132234,32753],{"class":149},[62,132236,132237],{"class":1675}," chalk\n",[22,132239,132240],{},"And then add the following variable declarations to the top of your script.",[52,132242,132244],{"className":32791,"code":132243,"language":32793,"meta":57,"style":57},"const chalk = require(\"chalk\");\nconst log = console.log;\nconst error = chalk.bold.red;\nconst success = chalk.bold.green.inverse;\n",[59,132245,132246,132264,132275,132287],{"__ignoreMap":57},[62,132247,132248,132250,132253,132255,132257,132259,132262],{"class":64,"line":65},[62,132249,110541],{"class":68},[62,132251,132252],{"class":149}," chalk",[62,132254,2556],{"class":68},[62,132256,120735],{"class":122},[62,132258,2109],{"class":72},[62,132260,132261],{"class":1675},"\"chalk\"",[62,132263,1133],{"class":72},[62,132265,132266,132268,132270,132272],{"class":64,"line":76},[62,132267,110541],{"class":68},[62,132269,111579],{"class":149},[62,132271,2556],{"class":68},[62,132273,132274],{"class":72}," console.log;\n",[62,132276,132277,132279,132282,132284],{"class":64,"line":82},[62,132278,110541],{"class":68},[62,132280,132281],{"class":149}," error",[62,132283,2556],{"class":68},[62,132285,132286],{"class":72}," chalk.bold.red;\n",[62,132288,132289,132291,132294,132296],{"class":64,"line":89},[62,132290,110541],{"class":68},[62,132292,132293],{"class":149}," success",[62,132295,2556],{"class":68},[62,132297,132298],{"class":72}," chalk.bold.green.inverse;\n",[22,132300,132301],{},"This allows me to write the following statements when I want to log errors or success and have some stylish log statements.",[52,132303,132305],{"className":32791,"code":132304,"language":32793,"meta":57,"style":57},"log(success(`Post ${title} was created successfully`));\nlog(error(\"Please don't provide any arguments to the new post generator\"));\n",[59,132306,132307,132325],{"__ignoreMap":57},[62,132308,132309,132311,132313,132315,132317,132319,132321,132323],{"class":64,"line":65},[62,132310,58271],{"class":122},[62,132312,2109],{"class":72},[62,132314,61084],{"class":122},[62,132316,2109],{"class":72},[62,132318,132202],{"class":1675},[62,132320,3196],{"class":72},[62,132322,132207],{"class":1675},[62,132324,6979],{"class":72},[62,132326,132327,132329,132331,132333,132335,132337],{"class":64,"line":76},[62,132328,58271],{"class":122},[62,132330,2109],{"class":72},[62,132332,21875],{"class":122},[62,132334,2109],{"class":72},[62,132336,131419],{"class":1675},[62,132338,6979],{"class":72},[26,132340,1499],{"id":1498},[22,132342,132343],{},"The plan here was to show you exactly how to create your own blog post generator but I hope you learned something more here. When you're building out projects like this and you find that you need something you can just create it yourself.",[22,132345,132346,132347,2755],{},"If you have written something similar or solved a problem for your project I would love to hear about it. If you want to check out the src for my blog post generator along with the code for my entire website ",[677,132348,132351],{"href":132349,"rel":132350},"https://github.com/danvega/danvega-dev/blob/master/scripts/newpost.js",[681],"you can check it out here",[22,132353,132354],{},"FYI - I created the post you are reading using this exact script 🤯",[1527,132356,132357],{},"html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}html pre.shiki code .spoOn, html code.shiki .spoOn{--shiki-default:#E36209;--shiki-dark:#FFAB70;--shiki-sepia:#E36209}html pre.shiki code .s4-Ip, html code.shiki .s4-Ip{--shiki-default:#6A737D;--shiki-dark:#6A737D;--shiki-sepia:#6A737D}",{"title":57,"searchDepth":76,"depth":76,"links":132359},[132360,132361,132364,132368,132373,132374],{"id":130895,"depth":76,"text":130896},{"id":131093,"depth":76,"text":131094,"children":132362},[132363],{"id":131125,"depth":82,"text":131125},{"id":131482,"depth":76,"text":131483,"children":132365},[132366,132367],{"id":131489,"depth":82,"text":131490},{"id":131545,"depth":82,"text":131546},{"id":131784,"depth":76,"text":131785,"children":132369},[132370,132371,132372],{"id":131818,"depth":82,"text":131819},{"id":131923,"depth":82,"text":131924},{"id":29692,"depth":82,"text":130541},{"id":36206,"depth":76,"text":132212},{"id":1498,"depth":76,"text":1499},{"_id":132376,"path":132377,"title":132378,"description":132379,"meta":132380,"body":132385},"content/blog/2019/04/19/npm-scripts-parallel.md","/blog/2019/04/19/npm-scripts-parallel","How to run multiple npm scripts in parallel","In this article I will talk about a problem I ran into recently and a couple of the solutions I found.",{"slug":132381,"date":132382,"published":13,"tags":132383,"author":17,"cover":132384,"excerpt":-1},"npm-scripts-parallel","2019-04-19T16:51:56.537Z",[32645,32634,32793],"./npm-parallel-cover.png",{"type":19,"value":132386,"toc":132750},[132387,132395,132408,132411,132419,132422,132425,132431,132434,132443,132459,132462,132475,132478,132493,132498,132501,132515,132518,132522,132530,132546,132549,132564,132568,132578,132581,132587,132634,132637,132647,132650,132656,132659,132663,132666,132675,132724,132730,132732,132743,132747],[22,132388,132389,132390,132394],{},"I was working on an exercise for ",[677,132391,132393],{"href":117501,"rel":132392},[681],"our students"," and In the process I learned something new. When our students finish their exercises they are asked to run some tests to validate that their solution works. We also use these tests as a way to grade certain exercises.",[22,132396,132397,132398,132403,132404,2755],{},"The problem is that I needed to run some end to end tests using ",[677,132399,132402],{"href":132400,"rel":132401},"http://www.cypress.io",[681],"Cypress"," but before doing so I needed the project to be running. In some of our vanilla JavaScript examples you can run the them right from Visual Studio Code using the ",[677,132405,132407],{"href":129526,"rel":132406},[681],"Live Server Extension",[22,132409,132410],{},"This works for development but about when it comes time to test? I suppose the instructions for the exercise could state \"make sure your project is running before running the tests\" but this introduces a couple of problems.",[22,132412,132413,132414,132418],{},"First, if we do it this way I'm not sure we can always assume that the project will be running at ",[677,132415,132416],{"href":132416,"rel":132417},"http://localhost:5500/my-project",[681],". That might be the case for most but I never like to just assume it. I could probably stick this in some configuration but again it doesn't feel right to me.",[22,132420,132421],{},"Second, what happens we want to grade our students exercises? This would mean that our instructors would have to start each project, run the test and record their score. This is a very a very tedious workflow and something we want to avoid. We would also want this automated in case we run everything through some continuous integration build.",[26,132423,132424],{"id":34880},"Creating a new project",[22,132426,132427,132428,2755],{},"If you want to follow along you can create your own project but it isn't necessary. This article will assume you have some experience building modern web applications. To get started create a new folder and create a new package.json by running the command ",[59,132429,132430],{},"npm -y",[636,132432,132402],{"id":132433},"cypress",[22,132435,132436,132437,132442],{},"We won't be getting into what Cypress is but if you haven't heard of it or had a chance to play around with it I highly suggest ",[677,132438,132441],{"href":132439,"rel":132440},"https://www.cypress.io/",[681],"checking it out",". You can install cypress using the following command:",[52,132444,132446],{"className":1663,"code":132445,"language":1665,"meta":57,"style":57},"npm install -D cypress\n",[59,132447,132448],{"__ignoreMap":57},[62,132449,132450,132452,132454,132456],{"class":64,"line":65},[62,132451,32645],{"class":122},[62,132453,32750],{"class":1675},[62,132455,32753],{"class":149},[62,132457,132458],{"class":1675}," cypress\n",[22,132460,132461],{},"Installing Cypress gives you access to some command line tools like the ability to run a headless version of the tests or to open them up in chrome.",[52,132463,132465],{"className":1663,"code":132464,"language":1665,"meta":57,"style":57},"./node_modules/.bin/cypress open\n",[59,132466,132467],{"__ignoreMap":57},[62,132468,132469,132472],{"class":64,"line":65},[62,132470,132471],{"class":122},"./node_modules/.bin/cypress",[62,132473,132474],{"class":1675}," open\n",[22,132476,132477],{},"With that we can add a new test to our scripts section in our package.json",[52,132479,132481],{"className":1663,"code":132480,"language":1665,"meta":57,"style":57},"\"test:e2e\": \"./node_modules/.bin/cypress open\"\n",[59,132482,132483],{"__ignoreMap":57},[62,132484,132485,132488,132490],{"class":64,"line":65},[62,132486,132487],{"class":122},"\"test:e2e\"",[62,132489,1266],{"class":149},[62,132491,132492],{"class":1675}," \"./node_modules/.bin/cypress open\"\n",[22,132494,132495],{},[646,132496,132497],{},"Running Cypress Tests in VueJS",[22,132499,132500],{},"When working in a framework like VueJS the framework solves this problem for us. You can write end to end tests using Cypress and when you're ready to test you just run the command",[52,132502,132504],{"className":1663,"code":132503,"language":1665,"meta":57,"style":57},"npm run test:e2e\n",[59,132505,132506],{"__ignoreMap":57},[62,132507,132508,132510,132512],{"class":64,"line":65},[62,132509,32645],{"class":122},[62,132511,1716],{"class":1675},[62,132513,132514],{"class":1675}," test:e2e\n",[22,132516,132517],{},"This will start the application up and then run the cypress integration tests. When the tests are finished you get the test results and the application is shut down. This is the preferred workflow and something we will try and mimic.",[636,132519,132521],{"id":132520},"http-server","Http Server",[22,132523,132524,132525,132529],{},"An easy solution to the problem of asking everyone to run the project manually is to install a small http server. You can do some searching and find one that fits your needs but for us ",[677,132526,132520],{"href":132527,"rel":132528},"https://www.npmjs.com/package/http-server",[681]," was tiny and fast, 2 qualities we were looking for. You can install this globally but for this demo we will just install it as a dev dependency by running the following command:",[52,132531,132533],{"className":1663,"code":132532,"language":1665,"meta":57,"style":57},"npm install -D http-server\n",[59,132534,132535],{"__ignoreMap":57},[62,132536,132537,132539,132541,132543],{"class":64,"line":65},[62,132538,32645],{"class":122},[62,132540,32750],{"class":1675},[62,132542,32753],{"class":149},[62,132544,132545],{"class":1675}," http-server\n",[22,132547,132548],{},"Now that you have it installed you can add a new script to start your http server.",[52,132550,132552],{"className":1663,"code":132551,"language":1665,"meta":57,"style":57},"\"start\": \"./node_modules/.bin/http-server\"\n",[59,132553,132554],{"__ignoreMap":57},[62,132555,132556,132559,132561],{"class":64,"line":65},[62,132557,132558],{"class":122},"\"start\"",[62,132560,1266],{"class":149},[62,132562,132563],{"class":1675}," \"./node_modules/.bin/http-server\"\n",[26,132565,132567],{"id":132566},"running-your-npm-scripts-in-parallel","Running your npm scripts in parallel",[22,132569,132570,132571,132574,132575,2755],{},"Now that you have both of scripts created you need to find a way to run them both. In the first iteration of the exercise I asked the students to run both of them. This meant that in Visual Studio Code you need to open up a terminal, run ",[59,132572,132573],{},"npm run start"," and then open up a new terminal instance and run ",[59,132576,132577],{},"npm run test:e2e",[22,132579,132580],{},"This isn't a huge deal but when you're introducing new concepts to someone you want to remove as much friction as possible. The point of this exercise was not how to run multiple scripts it was to just run the tests to make sure the code they wrote was correct.",[22,132582,132583,132584,132586],{},"I was already aware that I could run 2 scripts one after another using the ",[59,132585,74610],{}," operator. This means that If I had 2 scripts the following script would work.",[52,132588,132590],{"className":1663,"code":132589,"language":1665,"meta":57,"style":57},"\"scripts\": {\n \"one\": \"./node_modules/.bin/one\",\n \"two\": \"./node_modules/.bin/two\",\n \"start\": \"npm run one && npm run two\"\n}\n",[59,132591,132592,132600,132610,132620,132630],{"__ignoreMap":57},[62,132593,132594,132596,132598],{"class":64,"line":65},[62,132595,32921],{"class":122},[62,132597,1266],{"class":149},[62,132599,126],{"class":1675},[62,132601,132602,132605,132607],{"class":64,"line":76},[62,132603,132604],{"class":122}," \"one\"",[62,132606,1266],{"class":149},[62,132608,132609],{"class":1675}," \"./node_modules/.bin/one\",\n",[62,132611,132612,132615,132617],{"class":64,"line":82},[62,132613,132614],{"class":122}," \"two\"",[62,132616,1266],{"class":149},[62,132618,132619],{"class":1675}," \"./node_modules/.bin/two\",\n",[62,132621,132622,132625,132627],{"class":64,"line":89},[62,132623,132624],{"class":122}," \"start\"",[62,132626,1266],{"class":149},[62,132628,132629],{"class":1675}," \"npm run one && npm run two\"\n",[62,132631,132632],{"class":64,"line":95},[62,132633,379],{"class":72},[22,132635,132636],{},"This works by running the scripts sequentially and it also means that the first script has to complete before the 2nd script will run. In the case of an HTTP Server it stays running waiting to accept new requests. With the HTTP server never finishing the end to end tests would never run so it was back to the board for me.",[22,132638,132639,132640,132642,132643,132646],{},"After some searching around I did find a couple packages, one of which I will talk about later in this article. I also came across some documentation that said using ",[59,132641,74610],{}," will run your scripts sequentially while ",[59,132644,132645],{},"&"," will run them in parallel.",[22,132648,132649],{},"This was a real mind blown 🤯moment for me. I immediately tried this and it worked which prompted me to send out the following tweet.",[22,132651,132652],{},[677,132653,132654],{"href":132654,"rel":132655},"https://twitter.com/therealdanvega/status/1116403685452668928",[681],[22,132657,132658],{},"I got a lot of responses and most of them were similar to my reaction which was wow, I didn't know it could do that. I did have a couple though that spoiled my party and and asked about Windows.",[26,132660,132662],{"id":132661},"what-about-windows-dan","What about windows Dan?",[22,132664,132665],{},"I am primarily mac user but I do have bootcamp on my mac so that I can jump into Windows when need be. When people started asking me about this my initial thought was this probably isn't going to work on Windows. Sure enough after a quick test, this actually doesn't work on Windows because cmd.exe doesn't support it.",[22,132667,132668,132669,132674],{},"No worries though as there is still a cross platform solution to this problem. I did some more searching around and came across a few npm packages that looked like they would work. I ended up installing ",[677,132670,132673],{"href":132671,"rel":132672},"https://www.npmjs.com/package/npm-run-all",[681],"npm-run-all"," which worked out great.",[52,132676,132678],{"className":1663,"code":132677,"language":1665,"meta":57,"style":57},"npm install -D npm-run-all\n\n\"start\": \"./node_modules/.bin/http-server\",\n\"cypress\": \"./node_modules/.bin/cypress run\",\n\"test\": \"npm-run-all -p start cypress\"\n",[59,132679,132680,132691,132695,132704,132714],{"__ignoreMap":57},[62,132681,132682,132684,132686,132688],{"class":64,"line":65},[62,132683,32645],{"class":122},[62,132685,32750],{"class":1675},[62,132687,32753],{"class":149},[62,132689,132690],{"class":1675}," npm-run-all\n",[62,132692,132693],{"class":64,"line":76},[62,132694,79],{"emptyLinePlaceholder":13},[62,132696,132697,132699,132701],{"class":64,"line":82},[62,132698,132558],{"class":122},[62,132700,1266],{"class":149},[62,132702,132703],{"class":1675}," \"./node_modules/.bin/http-server\",\n",[62,132705,132706,132709,132711],{"class":64,"line":89},[62,132707,132708],{"class":122},"\"cypress\"",[62,132710,1266],{"class":149},[62,132712,132713],{"class":1675}," \"./node_modules/.bin/cypress run\",\n",[62,132715,132716,132719,132721],{"class":64,"line":95},[62,132717,132718],{"class":122},"\"test\"",[62,132720,1266],{"class":149},[62,132722,132723],{"class":1675}," \"npm-run-all -p start cypress\"\n",[22,132725,132726,132727,132729],{},"According to some people much smarter than me this is probably a safer route than using ",[59,132728,132645],{}," which I don't quite understand but sounds good to me.",[26,132731,1499],{"id":1498},[22,132733,132734,132735,132737,132738,132742],{},"I feel so lucky to be in a position where I get to learn something every single day. This was one of those days though where I was genuinely excited to learn about the single ampersand ",[59,132736,132645],{}," operator. If you have learned something lately that warrants the head exploding 🤯emoji please ",[677,132739,132741],{"href":86334,"rel":132740},[681],"reach out to me on twitter"," because I would love to hear about it. As always....",[22,132744,36004,132745,82545],{},[36006,132746],{},[1527,132748,132749],{},"html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}",{"title":57,"searchDepth":76,"depth":76,"links":132751},[132752,132756,132757,132758],{"id":34880,"depth":76,"text":132424,"children":132753},[132754,132755],{"id":132433,"depth":82,"text":132402},{"id":132520,"depth":82,"text":132521},{"id":132566,"depth":76,"text":132567},{"id":132661,"depth":76,"text":132662},{"id":1498,"depth":76,"text":1499},{"_id":132760,"path":132761,"title":132762,"description":132763,"meta":132764,"body":132769},"content/blog/2019/04/18/vue-tips-avoid-direct-dom-manipulation.md","/blog/2019/04/18/vue-tips-avoid-direct-dom-manipulation","Tips for Vue Developers: Avoid directly manipulating the DOM","In this article I will look at an example of where manipulating the DOM in our components might not be the best approach.",{"slug":132765,"date":132766,"published":13,"tags":132767,"author":17,"cover":132768,"excerpt":-1},"vue-tips-avoid-direct-dom-manipulation","2019-04-18T20:24:32.502Z",[105335,32793],"./vue-dom.png",{"type":19,"value":132770,"toc":133494},[132771,132777,132811,132814,132838,132841,132844,132920,132924,132927,132961,132968,133001,133004,133077,133081,133084,133091,133116,133166,133170,133176,133179,133239,133242,133318,133321,133472,133481,133486,133488,133491],[22,132772,132773,132774],{},"I was working with a student today on a final capstone project and they were having some issues with data binding. When we started digging through the code one thing stuck out to me as a big red flag. There was a checkbox with an id and then a change event handler ",[59,132775,132776],{},"toggleStatus()",[52,132778,132780],{"className":15773,"code":132779,"language":15775,"meta":57,"style":57},"\u003Cinput type=\"checkbox\" id=\"thecheckbox\" @change=\"toggleStatus\" />\n",[59,132781,132782],{"__ignoreMap":57},[62,132783,132784,132786,132788,132790,132792,132795,132797,132799,132802,132804,132806,132809],{"class":64,"line":65},[62,132785,760],{"class":72},[62,132787,8890],{"class":1780},[62,132789,16099],{"class":122},[62,132791,146],{"class":72},[62,132793,132794],{"class":1675},"\"checkbox\"",[62,132796,20831],{"class":122},[62,132798,146],{"class":72},[62,132800,132801],{"class":1675},"\"thecheckbox\"",[62,132803,122476],{"class":122},[62,132805,146],{"class":72},[62,132807,132808],{"class":1675},"\"toggleStatus\"",[62,132810,67133],{"class":72},[22,132812,132813],{},"Before we could even get to the root of the issue I saw some code that looked like this.",[52,132815,132817],{"className":32791,"code":132816,"language":32793,"meta":57,"style":57},"const isChecked = document.getElementById(\"thecheckbox\");\n",[59,132818,132819],{"__ignoreMap":57},[62,132820,132821,132823,132826,132828,132830,132832,132834,132836],{"class":64,"line":65},[62,132822,110541],{"class":68},[62,132824,132825],{"class":149}," isChecked",[62,132827,2556],{"class":68},[62,132829,21445],{"class":72},[62,132831,21448],{"class":122},[62,132833,2109],{"class":72},[62,132835,132801],{"class":1675},[62,132837,1133],{"class":72},[22,132839,132840],{},"When you're working with Vue you kind of have to forget that you have access to the DOM. The reason I say this is because if you're directly accessing the DOM there is probably a better way to do this. I want to be clear that you're not violating any rules here and nobody is going to yell at you for it but we should think about this problem in another way.",[22,132842,132843],{},"In our toggle status method imagine that you needed to know if that checkbox was checked to determine what action to take. This is a very common process in any application.",[52,132845,132847],{"className":32791,"code":132846,"language":32793,"meta":57,"style":57},"methods: {\n toggleStatus() {\n const isChecked = document.getElementById('thecheckbox').checked;\n if( isChecked ) {\n // do something\n } else {\n // do something else\n }\n }\n}\n",[59,132848,132849,132856,132863,132883,132890,132895,132903,132908,132912,132916],{"__ignoreMap":57},[62,132850,132851,132854],{"class":64,"line":65},[62,132852,132853],{"class":122},"methods",[62,132855,3688],{"class":72},[62,132857,132858,132861],{"class":64,"line":76},[62,132859,132860],{"class":122}," toggleStatus",[62,132862,206],{"class":72},[62,132864,132865,132867,132869,132871,132873,132875,132877,132880],{"class":64,"line":82},[62,132866,36857],{"class":68},[62,132868,132825],{"class":149},[62,132870,2556],{"class":68},[62,132872,21445],{"class":72},[62,132874,21448],{"class":122},[62,132876,2109],{"class":72},[62,132878,132879],{"class":1675},"'thecheckbox'",[62,132881,132882],{"class":72},").checked;\n",[62,132884,132885,132887],{"class":64,"line":89},[62,132886,14187],{"class":68},[62,132888,132889],{"class":72},"( isChecked ) {\n",[62,132891,132892],{"class":64,"line":95},[62,132893,132894],{"class":85}," // do something\n",[62,132896,132897,132899,132901],{"class":64,"line":101},[62,132898,121127],{"class":72},[62,132900,12783],{"class":68},[62,132902,126],{"class":72},[62,132904,132905],{"class":64,"line":107},[62,132906,132907],{"class":85}," // do something else\n",[62,132909,132910],{"class":64,"line":113},[62,132911,223],{"class":72},[62,132913,132914],{"class":64,"line":129},[62,132915,3731],{"class":72},[62,132917,132918],{"class":64,"line":134},[62,132919,379],{"class":72},[636,132921,132923],{"id":132922},"a-better-approach","A Better Approach",[22,132925,132926],{},"A better approach for this is to understand that each instance of this component has its own state. This means that we can bind controls in the component to our data. I am going to refactor our previous example by first creating a variable called isChecked and default it to false. This is because by default I want this checkbox unchecked.",[52,132928,132930],{"className":32791,"code":132929,"language":32793,"meta":57,"style":57},"data() {\n return {\n isChecked = false\n }\n}\n",[59,132931,132932,132938,132944,132953,132957],{"__ignoreMap":57},[62,132933,132934,132936],{"class":64,"line":65},[62,132935,106264],{"class":122},[62,132937,206],{"class":72},[62,132939,132940,132942],{"class":64,"line":76},[62,132941,82091],{"class":68},[62,132943,126],{"class":72},[62,132945,132946,132949,132951],{"class":64,"line":82},[62,132947,132948],{"class":72}," isChecked ",[62,132950,146],{"class":68},[62,132952,73447],{"class":149},[62,132954,132955],{"class":64,"line":89},[62,132956,3731],{"class":72},[62,132958,132959],{"class":64,"line":95},[62,132960,379],{"class":72},[22,132962,132963,132964,132967],{},"Now that we have some default state for the checkbox we can use this in our component. You can bind the checkbox checked attribute to the variable ",[59,132965,132966],{},"isChecked",". When the component first loads it will be unchecked but is now bound to our variable so any time that it changes our checkbox will be updated.",[52,132969,132971],{"className":15773,"code":132970,"language":15775,"meta":57,"style":57},"\u003Cinput type=\"checkbox\" :checked=\"isChecked\" @change=\"toggleStatus\" />\n",[59,132972,132973],{"__ignoreMap":57},[62,132974,132975,132977,132979,132981,132983,132985,132988,132990,132993,132995,132997,132999],{"class":64,"line":65},[62,132976,760],{"class":72},[62,132978,8890],{"class":1780},[62,132980,16099],{"class":122},[62,132982,146],{"class":72},[62,132984,132794],{"class":1675},[62,132986,132987],{"class":122}," :checked",[62,132989,146],{"class":72},[62,132991,132992],{"class":1675},"\"isChecked\"",[62,132994,122476],{"class":122},[62,132996,146],{"class":72},[62,132998,132808],{"class":1675},[62,133000,67133],{"class":72},[22,133002,133003],{},"Now in our toggle status method we can just use the components data in our if expression. We can also flip the status of the checkbox by setting it to the opposite of whatever it is currently.",[52,133005,133007],{"className":32791,"code":133006,"language":32793,"meta":57,"style":57},"methods: {\n toggleStatus() {\n if(this.isChecked) {\n // do sommething\n } else {\n // do something else\n }\n this.isChecked = !this.isChecked;\n }\n}\n",[59,133008,133009,133015,133021,133032,133037,133045,133049,133053,133069,133073],{"__ignoreMap":57},[62,133010,133011,133013],{"class":64,"line":65},[62,133012,132853],{"class":122},[62,133014,3688],{"class":72},[62,133016,133017,133019],{"class":64,"line":76},[62,133018,132860],{"class":122},[62,133020,206],{"class":72},[62,133022,133023,133025,133027,133029],{"class":64,"line":82},[62,133024,14187],{"class":68},[62,133026,2109],{"class":72},[62,133028,1295],{"class":149},[62,133030,133031],{"class":72},".isChecked) {\n",[62,133033,133034],{"class":64,"line":89},[62,133035,133036],{"class":85}," // do sommething\n",[62,133038,133039,133041,133043],{"class":64,"line":95},[62,133040,121127],{"class":72},[62,133042,12783],{"class":68},[62,133044,126],{"class":72},[62,133046,133047],{"class":64,"line":101},[62,133048,132907],{"class":85},[62,133050,133051],{"class":64,"line":107},[62,133052,223],{"class":72},[62,133054,133055,133057,133060,133062,133064,133066],{"class":64,"line":113},[62,133056,39124],{"class":149},[62,133058,133059],{"class":72},".isChecked ",[62,133061,146],{"class":68},[62,133063,64810],{"class":68},[62,133065,1295],{"class":149},[62,133067,133068],{"class":72},".isChecked;\n",[62,133070,133071],{"class":64,"line":129},[62,133072,3731],{"class":72},[62,133074,133075],{"class":64,"line":134},[62,133076,379],{"class":72},[26,133078,133080],{"id":133079},"accessing-the-dom-using-refs","Accessing the DOM using $refs",[22,133082,133083],{},"What if you absolutely need access to the DOM? There are instances where you might need a reference to an element to perform some type of manipulation. I have seen this come up while working with 3rd party components and when working with parent/child components.",[22,133085,133086,133087,133090],{},"I am going to start with a very simple example but this case where you shouldn't use $refs. Say you had a button in your component and you wanted to get access to it so you could change the text. You can assign a ref attribute to the button and later get access to it using the ",[59,133088,133089],{},"$refs"," object.",[52,133092,133094],{"className":15773,"code":133093,"language":15775,"meta":57,"style":57},"\u003Cbutton ref=\"myButton\">My Button\u003C/button>\n",[59,133095,133096],{"__ignoreMap":57},[62,133097,133098,133100,133102,133104,133106,133109,133112,133114],{"class":64,"line":65},[62,133099,760],{"class":72},[62,133101,16275],{"class":1780},[62,133103,115086],{"class":122},[62,133105,146],{"class":72},[62,133107,133108],{"class":1675},"\"myButton\"",[62,133110,133111],{"class":72},">My Button\u003C/",[62,133113,16275],{"class":1780},[62,133115,1784],{"class":72},[52,133117,133119],{"className":32791,"code":133118,"language":32793,"meta":57,"style":57},"methods: {\n onButtonClick() {\n const btn = this.$refs.myButton;\n btn.innerText = 'New Button Text'\n }\n}\n",[59,133120,133121,133127,133134,133148,133158,133162],{"__ignoreMap":57},[62,133122,133123,133125],{"class":64,"line":65},[62,133124,132853],{"class":122},[62,133126,3688],{"class":72},[62,133128,133129,133132],{"class":64,"line":76},[62,133130,133131],{"class":122}," onButtonClick",[62,133133,206],{"class":72},[62,133135,133136,133138,133141,133143,133145],{"class":64,"line":82},[62,133137,36857],{"class":68},[62,133139,133140],{"class":149}," btn",[62,133142,2556],{"class":68},[62,133144,9961],{"class":149},[62,133146,133147],{"class":72},".$refs.myButton;\n",[62,133149,133150,133153,133155],{"class":64,"line":89},[62,133151,133152],{"class":72}," btn.innerText ",[62,133154,146],{"class":68},[62,133156,133157],{"class":1675}," 'New Button Text'\n",[62,133159,133160],{"class":64,"line":95},[62,133161,3731],{"class":72},[62,133163,133164],{"class":64,"line":101},[62,133165,379],{"class":72},[26,133167,133169],{"id":133168},"practical-example-using-refs-in-vue","Practical example using $refs in Vue",[22,133171,133172,133173,2755],{},"Again, this isn't the most practical example because we are just doing the same thing we did in our earlier example. As I said earlier you might come across this issue when you're working with parent/child components. For this example let's say that you have a checkout form and in that component, you have a child component called ",[59,133174,133175],{},"CustomerForm.vue",[22,133177,133178],{},"In our checkout form component, we want to programmatically set the focus of an input in our customer form component. The way we can do this is to assign a ref to our customer form within our checkout form.",[52,133180,133182],{"className":15773,"code":133181,"language":15775,"meta":57,"style":57},"\u003Ctemplate>\n \u003Cdiv id=\"checkout\">\n \u003Ccustomer-form ref=\"customer\" />\n \u003C/div>\n\u003C/template>\n",[59,133183,133184,133192,133207,133223,133231],{"__ignoreMap":57},[62,133185,133186,133188,133190],{"class":64,"line":65},[62,133187,760],{"class":72},[62,133189,105991],{"class":1780},[62,133191,1784],{"class":72},[62,133193,133194,133196,133198,133200,133202,133205],{"class":64,"line":76},[62,133195,33056],{"class":72},[62,133197,15944],{"class":1780},[62,133199,20831],{"class":122},[62,133201,146],{"class":72},[62,133203,133204],{"class":1675},"\"checkout\"",[62,133206,1784],{"class":72},[62,133208,133209,133211,133214,133216,133218,133221],{"class":64,"line":82},[62,133210,1789],{"class":72},[62,133212,133213],{"class":1780},"customer-form",[62,133215,115086],{"class":122},[62,133217,146],{"class":72},[62,133219,133220],{"class":1675},"\"customer\"",[62,133222,67133],{"class":72},[62,133224,133225,133227,133229],{"class":64,"line":89},[62,133226,33187],{"class":72},[62,133228,15944],{"class":1780},[62,133230,1784],{"class":72},[62,133232,133233,133235,133237],{"class":64,"line":95},[62,133234,1818],{"class":72},[62,133236,105991],{"class":1780},[62,133238,1784],{"class":72},[22,133240,133241],{},"In the customer form, we will then assign a ref to the first name input box.",[52,133243,133245],{"className":15773,"code":133244,"language":15775,"meta":57,"style":57},"\u003Ctemplate>\n \u003Cdiv id=\"customer-form\">\n First Name:\n \u003Cinput type=\"text\" placeholder=\"Enter your first name\" ref=\"firstName\" />\n \u003C/div>\n\u003C/template>\n",[59,133246,133247,133255,133270,133275,133302,133310],{"__ignoreMap":57},[62,133248,133249,133251,133253],{"class":64,"line":65},[62,133250,760],{"class":72},[62,133252,105991],{"class":1780},[62,133254,1784],{"class":72},[62,133256,133257,133259,133261,133263,133265,133268],{"class":64,"line":76},[62,133258,33056],{"class":72},[62,133260,15944],{"class":1780},[62,133262,20831],{"class":122},[62,133264,146],{"class":72},[62,133266,133267],{"class":1675},"\"customer-form\"",[62,133269,1784],{"class":72},[62,133271,133272],{"class":64,"line":82},[62,133273,133274],{"class":72}," First Name:\n",[62,133276,133277,133279,133281,133283,133285,133287,133289,133291,133294,133296,133298,133300],{"class":64,"line":89},[62,133278,1789],{"class":72},[62,133280,8890],{"class":1780},[62,133282,16099],{"class":122},[62,133284,146],{"class":72},[62,133286,16152],{"class":1675},[62,133288,20963],{"class":122},[62,133290,146],{"class":72},[62,133292,133293],{"class":1675},"\"Enter your first name\"",[62,133295,115086],{"class":122},[62,133297,146],{"class":72},[62,133299,22681],{"class":1675},[62,133301,67133],{"class":72},[62,133303,133304,133306,133308],{"class":64,"line":95},[62,133305,33187],{"class":72},[62,133307,15944],{"class":1780},[62,133309,1784],{"class":72},[62,133311,133312,133314,133316],{"class":64,"line":101},[62,133313,1818],{"class":72},[62,133315,105991],{"class":1780},[62,133317,1784],{"class":72},[22,133319,133320],{},"Now in the checkout form in our mounted method, we can focus on the customer form first name input.",[52,133322,133324],{"className":105981,"code":133323,"language":105335,"meta":57,"style":57},"\u003Ctemplate>\n \u003Cdiv id=\"checkout\">\n \u003Ccustomer-form ref=\"customer\" />\n \u003C/div>\n\u003C/template>\n\n\u003Cscript>\nimport CustomerForm from \"@/components/CustomerForm\";\n\nexport default {\n name: \"checkout-form\",\n components: {\n CustomerForm\n },\n mounted() {\n this.$refs.customer.$refs.firstName.focus();\n }\n};\n\u003C/script>\n",[59,133325,133326,133334,133348,133362,133370,133378,133382,133390,133404,133408,133416,133425,133429,133434,133438,133444,133456,133460,133464],{"__ignoreMap":57},[62,133327,133328,133330,133332],{"class":64,"line":65},[62,133329,760],{"class":72},[62,133331,105991],{"class":1780},[62,133333,1784],{"class":72},[62,133335,133336,133338,133340,133342,133344,133346],{"class":64,"line":76},[62,133337,33056],{"class":72},[62,133339,15944],{"class":1780},[62,133341,20831],{"class":122},[62,133343,146],{"class":72},[62,133345,133204],{"class":1675},[62,133347,1784],{"class":72},[62,133349,133350,133352,133354,133356,133358,133360],{"class":64,"line":82},[62,133351,1789],{"class":72},[62,133353,133213],{"class":1780},[62,133355,115086],{"class":122},[62,133357,146],{"class":72},[62,133359,133220],{"class":1675},[62,133361,67133],{"class":72},[62,133363,133364,133366,133368],{"class":64,"line":89},[62,133365,33187],{"class":72},[62,133367,15944],{"class":1780},[62,133369,1784],{"class":72},[62,133371,133372,133374,133376],{"class":64,"line":95},[62,133373,1818],{"class":72},[62,133375,105991],{"class":1780},[62,133377,1784],{"class":72},[62,133379,133380],{"class":64,"line":101},[62,133381,79],{"emptyLinePlaceholder":13},[62,133383,133384,133386,133388],{"class":64,"line":107},[62,133385,760],{"class":72},[62,133387,15846],{"class":1780},[62,133389,1784],{"class":72},[62,133391,133392,133394,133397,133399,133402],{"class":64,"line":113},[62,133393,27875],{"class":68},[62,133395,133396],{"class":72}," CustomerForm ",[62,133398,3507],{"class":68},[62,133400,133401],{"class":1675}," \"@/components/CustomerForm\"",[62,133403,153],{"class":72},[62,133405,133406],{"class":64,"line":129},[62,133407,79],{"emptyLinePlaceholder":13},[62,133409,133410,133412,133414],{"class":64,"line":134},[62,133411,14767],{"class":68},[62,133413,106045],{"class":68},[62,133415,126],{"class":72},[62,133417,133418,133420,133423],{"class":64,"line":156},[62,133419,106052],{"class":72},[62,133421,133422],{"class":1675},"\"checkout-form\"",[62,133424,3338],{"class":72},[62,133426,133427],{"class":64,"line":161},[62,133428,106062],{"class":72},[62,133430,133431],{"class":64,"line":167},[62,133432,133433],{"class":72}," CustomerForm\n",[62,133435,133436],{"class":64,"line":173},[62,133437,32848],{"class":72},[62,133439,133440,133442],{"class":64,"line":179},[62,133441,106219],{"class":122},[62,133443,206],{"class":72},[62,133445,133446,133448,133451,133454],{"class":64,"line":185},[62,133447,39124],{"class":149},[62,133449,133450],{"class":72},".$refs.customer.$refs.firstName.",[62,133452,133453],{"class":122},"focus",[62,133455,822],{"class":72},[62,133457,133458],{"class":64,"line":191},[62,133459,3731],{"class":72},[62,133461,133462],{"class":64,"line":209},[62,133463,107354],{"class":72},[62,133465,133466,133468,133470],{"class":64,"line":220},[62,133467,1818],{"class":72},[62,133469,15846],{"class":1780},[62,133471,1784],{"class":72},[22,133473,133474,133475,133480],{},"Just a little note from the ",[677,133476,133479],{"href":133477,"rel":133478},"https://vuejs.org/v2/guide/components-edge-cases.html#Accessing-Child-Component-Instances-amp-Child-Elements",[681],"Vue Documentation"," that you need to be aware of.",[29685,133482,133483],{},[22,133484,133485],{},"$refs are only populated after the component has been rendered, and they are not reactive. It is only meant as an escape hatch for direct child manipulation - you should avoid accessing $refs from within templates or computed properties.",[26,133487,1499],{"id":1498},[22,133489,133490],{},"I want to stress that you aren't doing anything wrong by accessing the DOM in your Vue application. I just think that in most cases there is a better approach and I hope this example was able to show that. If you know of a good use case for manipulating the DOM directly please reach out to me and let me know.",[1527,133492,133493],{},"html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .suaqH, html code.shiki .suaqH{--shiki-default:#22863A;--shiki-dark:#85E89D;--shiki-sepia:#22863A}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}html pre.shiki code .s4-Ip, html code.shiki .s4-Ip{--shiki-default:#6A737D;--shiki-dark:#6A737D;--shiki-sepia:#6A737D}",{"title":57,"searchDepth":76,"depth":76,"links":133495},[133496,133497,133498,133499],{"id":132922,"depth":82,"text":132923},{"id":133079,"depth":76,"text":133080},{"id":133168,"depth":76,"text":133169},{"id":1498,"depth":76,"text":1499},{"_id":133501,"path":133502,"title":133503,"description":133504,"meta":133505,"body":133510},"content/blog/2019/04/11/dont-call-it-a-comeback.md","/blog/2019/04/11/dont-call-it-a-comeback","Personal Blogs: Don't Call it a comeback!","I'm not sure if you have noticed or not but the personal blog is making a comeback",{"slug":133506,"date":133507,"published":13,"tags":133508,"author":17,"cover":133509,"excerpt":-1},"dont-call-it-a-comeback","2019-04-11T11:38:39.435Z",[60034],"./personal-blog-4b0867c8-0c1b-421c-afaf-7fc7e7986a77.png",{"type":19,"value":133511,"toc":133716},[133512,133515,133517,133520,133523,133526,133534,133542,133547,133550,133553,133556,133560,133563,133566,133569,133572,133575,133579,133582,133585,133596,133599,133602,133605,133608,133611,133620,133623,133626,133637,133640,133643,133646,133649,133656,133661,133664,133667,133670,133673,133676,133679,133682,133684,133695,133700,133702,133709,133712],[22,133513,133514],{},"I'm not sure if you have noticed or not but the personal blog is making a comeback. There aren't just more people blogging these days, there are more people building their own blogs which are really great to see. In this article, I am to give you some background on why I started blogging which I believe are good reasons for you to start blogging if you haven't already.",[26,133516,60044],{"id":60043},[22,133518,133519],{},"I have been blogging since October of 2005 and have written somewhere near 1,000 blog posts. If that sounds like a lot I can assure you its weird for me to say out loud because I can't believe it. I can, however, remember clearly when and why I started blogging.",[22,133521,133522],{},"In 2005 I was a ColdFusion developer and I wanted some practice building real applications. Like most junior developers I knew the only way I was going to get better was by doing. You have to remember that at this time we didn't have Github or the vast amount of resources that we have today.",[22,133524,133525],{},"What we did have, especially in the ColdFusion community is a number of people who loved to blog and share their experiences. I looked up to a lot of these developers and wanted to follow in their footsteps. I thought this was a great opportunity to set up my own blog.",[22,133527,133528,133529,133533],{},"I found an open source blogging platform called ",[677,133530,60051],{"href":133531,"rel":133532},"http://www.blogcfc.com/",[681]," that was written in ColdFusion. Before I could even start blogging I learned how to set up and work with an open source project which was a great experience. I got a chance to see how someone outside of me or the company I worked for at the time was creating and distributing software.",[22,133535,133536,133537,2755],{},"I really loved the idea of theming the blog and giving it a unique look to match my personality. Now, I have never been known for my design skills but It is still something I enjoy doing to this day. Here is a screenshot of an early version of my blog using the ",[677,133538,133541],{"href":133539,"rel":133540},"https://archive.org/web/",[681],"Wayback Machine",[22,133543,133544],{},[653,133545],{"alt":57,"src":133546},"/images/blog/2019/04/11/2019-04-09_21-55-34-783a547c-404d-4337-9402-6648cae05340.png",[22,133548,133549],{},"Now that I had my new blog up and running I can remember a lot of my early posts were talking about things that I learned while setting up that blog. My motivation for blogging always came down to 2 simple objectives.",[22,133551,133552],{},"First I wanted to help others that were trying to do the same thing I was doing but allow them to avoid all of the mistakes I made. If I could help someone avoid the discomfort of yelling at the monitor and banging their heads against the desk (I speak from experience) then I considered it a success.",[22,133554,133555],{},"Second, I wanted a reference for a problem that I just solved that I could come back to later. I have to tell you one of the coolest things for me is googling something that you're stuck on only to find your own blog post that solves your issue. This has a great way of reminding you why you do what you do.",[26,133557,133559],{"id":133558},"why-developers-stopped-building-personal-blogs","Why developers stopped building personal blogs",[22,133561,133562],{},"Now that you have some background on me I can talk a little bit on why I think developers stopped creating their own blogs.",[636,133564,86336],{"id":133565},"twitter",[22,133567,133568],{},"I am really starting to date myself in this article but when I started blogging Twitter wasn't even a thing yet. In fact I didn't even join Twitter until May of 2008 and I can't even remember when I began seriously using it a platform to curate information.",[22,133570,133571],{},"The reason I bring up Twitter is that a lot of bloggers used their blog to share information & links with others. If I had a cool tool, product or link to a website I would just blog about it. Now, unless I have more to say about this cool thing I just found I will just share it on Twitter.",[22,133573,133574],{},"Now this isn't a bad thing because I am big fan of Twitter but I really enjoy a developers insight and experience into using this tool that I can come back to later.",[636,133576,133578],{"id":133577},"wordpress","WordPress",[22,133580,133581],{},"WordPress is an amazing CMS + Blogging platform that is used by something like 33% of the entire internet. That number is staggering but not shocking if you have ever used it. When I moved away from ColdFusion there were a lot of features that I was looking for and WordPress seem to have them all, so I switched.",[22,133583,133584],{},"I have been using it for awhile now and have really enjoyed it for the most part. The 3 main issues I have had with it are:",[915,133586,133587,133590,133593],{},[37,133588,133589],{},"Hosting: WordPress is a resource hog and hosting runs me $30 month",[37,133591,133592],{},"Plugins: Installing lots of plugins can cause performance issue",[37,133594,133595],{},"PHP: I am not a PHP developer nor do I want to be.",[22,133597,133598],{},"I think the last one is really the big issue for me. I know enough to get in there and hack some things together but whenever I want to update themes it just seemed overwhelming for me. I think this could be the case for a lot of developers. Maybe they moved to WordPress, bought some theme and just used it as a blogging platform.",[22,133600,133601],{},"I am not saying there is anything wrong with this at all but it could be something that led to developers like myself from building a personal blog. Again when I say building I just mean creating something from scratch and giving it some style to match their personality.",[636,133603,52754],{"id":133604},"medium",[22,133606,133607],{},"In 2012 Medium launched and it offered a platform for writers to start writing without having to setup a blog. A few years ago I started writing on there and at first I really liked it but I always came back to the fact that I didn't own my content. I also didn't want to have to migrate my 700 posts at the time so it quickly became a place where I would syndicate posts from my own blog.",[22,133609,133610],{},"Fast forward to today and I see a lot of developers that did move to Medium, moving off of it. Medium took a bunch of funding so you had to know at some point that they needed to start making money. If you're reading a post on Medium these days you might be all of a sudden hit with a paywall. I also don't think its a great writing experience for developers.",[22,133612,133613],{},[4534,133614,133615,133616,2755],{},"If you're looking for a solution like Medium but for developers, I would suggest taking a look at ",[677,133617,133618],{"href":133618,"rel":133619},"http://www.dev.to",[681],[26,133621,133622],{"id":133506},"Don't call it a comeback",[22,133624,133625],{},"I gave some reasons why I think there was a period where developers weren't building their own blogs. I'm here to tell you that times, they are a changing! Here are a few things that I think have contributed to the comeback.",[915,133627,133628,133631,133634],{},[37,133629,133630],{},"JAMStack (Static Site Generators)",[37,133632,133633],{},"Evolution of JavaScript Frameworks",[37,133635,133636],{},"JavaScript Rocks 🤘🏻",[636,133638,133630],{"id":133639},"jamstack-static-site-generators",[22,133641,133642],{},"I think that static site generators are a huge reason for the comeback. I know for me personally as great as a platform like WordPress was it was just too much for what I wanted to do. My visitors are coming to my site to read my content and view information on the courses that I have available. They don't visit my site for long load times and the occasional random WordPress is out of resources error.",[22,133644,133645],{},"Static site generators allow us to build our entire site so that when our visitors get the content they are looking at HTML/CSS/JS and it is blazing fast. This has always been the appeal of static sites but until recently they have been pretty limited in what they can do. With advancements in JavaScript frameworks, tooling and cloud computing we now have a pretty awesome workflow.",[22,133647,133648],{},"I write a lot of documentation, tutorials, and exercises in Markdown so starting with a simple markdown file is very appealing to me. From there I am able to just write a new blog post and when I am ready to commit the file to my git repository and push it. My hosting provider builds my static site, deploys it and its live for everyone to see.",[22,133650,133651,133652,133655],{},"I have mentioned Gridsome a few times in this article and it's the one that I selected because it uses Vue. There are plenty of great options out there including a very popular one called ",[677,133653,130879],{"href":130877,"rel":133654},[681],". I would suggest looking at your options, trying out a couple that fit your stack and go from there.",[22,133657,133658],{},[4534,133659,133660],{},"P.S If you're interested in hearing more on JAMStack tweet at me and tell me to write that article! I love everything about it and have a lot to say on the subject.",[636,133662,133633],{"id":133663},"evolution-of-javascript-frameworks",[22,133665,133666],{},"We wouldn't have these amazing static site generators without the evolution of JavaScript frameworks. In the past 5 years we have seen frameworks like Angular, React & Vue rise to the top. These frameworks along with the JavaScript language itself have really made front end development fun again.",[22,133668,133669],{},"No matter what your project requirements are chances are any of the big 3 frameworks have an answer for you. Developers love to get caught up in framework wars but if you just like to build stuff you can't go wrong with any of them.",[22,133671,133672],{},"I always tell people that they shouldn't listen to anyone when it comes to \"which framework should I choose\". Try building some components or applications using each of them and decide for yourself.",[636,133674,133636],{"id":133675},"javascript-rocks",[22,133677,133678],{},"Behind every great framework is a great language. JavaScript has taken a lot of flak over the years (rightfully so) but it has stood the test of time as \"the language of the web\". I had to learn JavaScript before jQuery was cool and I can't tell you how good you have it today.",[22,133680,133681],{},"In the last few years, JavaScript has really come into its own and I think ES6 brought a lot of features to the language that got developers excited. There is just something very clean and elegant about some well-written JavaScript and I can't wait to see what the future of the language brings.",[26,133683,8474],{"id":89724},[22,133685,133686,133687,133690,133691,2755],{},"If you're not reading this article on my blog and want to check it out head over to ",[677,133688,8472],{"href":8472,"rel":133689},[681],". I also have a few articles on why I moved over to Gridsome and how I built certain features. If you're interested my entire site is open source and you can ",[677,133692,133694],{"href":113931,"rel":133693},[681],"grab the code here",[22,133696,133697],{},[653,133698],{"alt":57,"src":133699},"/images/blog/2019/04/11/2019-04-10_11-55-41-4bdf4a5b-10e8-476d-a340-26267e9fed0e.png",[26,133701,1499],{"id":1498},[22,133703,133704,133705,133708],{},"I had so much fun building my new personal website using ",[677,133706,104836],{"href":104834,"rel":133707},[681],". It really took me back to what I love about being a software developer. I like to build things and tell people how I did it so that others can learn and avoid making the same mistakes that I did. If you're new to this blog that is what you can expect from so I hope you will visit again.",[22,133710,133711],{},"If you moved your blog or created a new on top of a new framework or static site generator please let me know. I would love to check it out and I will retweet some of my favorites.",[22,133713,36004,133714,82545],{},[36006,133715],{},{"title":57,"searchDepth":76,"depth":76,"links":133717},[133718,133719,133724,133729,133730],{"id":60043,"depth":76,"text":60044},{"id":133558,"depth":76,"text":133559,"children":133720},[133721,133722,133723],{"id":133565,"depth":82,"text":86336},{"id":133577,"depth":82,"text":133578},{"id":133604,"depth":82,"text":52754},{"id":133506,"depth":76,"text":133622,"children":133725},[133726,133727,133728],{"id":133639,"depth":82,"text":133630},{"id":133663,"depth":82,"text":133633},{"id":133675,"depth":82,"text":133636},{"id":89724,"depth":76,"text":8474},{"id":1498,"depth":76,"text":1499},{"_id":133732,"path":133733,"title":133734,"description":133735,"meta":133736,"body":133741},"content/blog/2019/04/09/adding-twitter-cards-to-gridsome.md","/blog/2019/04/09/adding-twitter-cards-to-gridsome","Adding Twitter Cards to Gridsome","In this tutorial I will show you how to add Twitter Cards to your Gridsome Blog.",{"slug":133737,"date":133738,"published":13,"tags":133739,"author":17,"cover":133740,"excerpt":-1},"adding-twitter-cards-to-gridsome","2019-04-09T19:21:53.368Z",[105335,113419],"./twittter-cards-gridsome-cover-936ee6d4-220e-4800-873c-9d700fa44469.png",{"type":19,"value":133742,"toc":135117},[133743,133751,133754,133758,133761,133768,133818,133821,134001,134004,134105,134108,134112,134120,134272,134279,134391,134402,134405,134461,134464,134473,134530,134533,134683,134692,135098,135101,135106,135108,135114],[22,133744,45274,133745,133750],{},[677,133746,133749],{"href":133747,"rel":133748},"https://www.danvega.dev/blog",[681],"previous article",", I showed you how you could add Twitter Cards to your blog. If you haven't gone through that post yet I would suggest doing so before reading this one.",[22,133752,133753],{},"In this article, I am going to show you how to add Twitter Cards to your Gridsome Blog. This is not only for Twitter cards but more of a general example on adding meta tags to your blog posts.",[26,133755,133757],{"id":133756},"blog-posts-in-gridsome","Blog Posts in Gridsome",[22,133759,133760],{},"I am going to make an assumption with this article that you have at least played around with Gridsome. If you're looking for a begginers guide to Gridsome I have one coming but give me a shout out on Twitter and let me know that you're interested in it.",[22,133762,133763,133764,133767],{},"In my ",[59,133765,133766],{},"gridsome.config.js"," I have defined the @gridsome/source-filesystem plugin and the key part here is the type name. This is the GraphQL type and template name. A .vue file in src/templates must match the typeName to have a template for it.",[52,133769,133771],{"className":32791,"code":133770,"language":32793,"meta":57,"style":57},"plugins: [{\n use: '@gridsome/source-filesystem',\n options: {\n path: 'blog/**/*.md',\n typeName: 'Post',\n route: '/blog/:year/:month/:day/:slug',\n",[59,133772,133773,133779,133788,133793,133802,133810],{"__ignoreMap":57},[62,133774,133775,133777],{"class":64,"line":65},[62,133776,92866],{"class":122},[62,133778,128598],{"class":72},[62,133780,133781,133784,133786],{"class":64,"line":76},[62,133782,133783],{"class":72}," use: ",[62,133785,128606],{"class":1675},[62,133787,3338],{"class":72},[62,133789,133790],{"class":64,"line":82},[62,133791,133792],{"class":72}," options: {\n",[62,133794,133795,133798,133800],{"class":64,"line":89},[62,133796,133797],{"class":72}," path: ",[62,133799,128621],{"class":1675},[62,133801,3338],{"class":72},[62,133803,133804,133806,133808],{"class":64,"line":95},[62,133805,128657],{"class":72},[62,133807,120891],{"class":1675},[62,133809,3338],{"class":72},[62,133811,133812,133814,133816],{"class":64,"line":101},[62,133813,128667],{"class":72},[62,133815,128640],{"class":1675},[62,133817,3338],{"class":72},[22,133819,133820],{},"If we look at the Post template the first thing you will see is the markup for each blog post. Again this is a single blog post, there is a separate template for the page that lists posts.",[52,133822,133824],{"className":15773,"code":133823,"language":15775,"meta":57,"style":57},"\u003CLayout>\n \u003Cdiv class=\"article content\">\n \u003Ch1 class=\"title is-2 article-title\">{{ $page.post.title }}\u003C/h1>\n \u003Csmall class=\"about\">{{ formatCreatedOn }} • ☕️ {{ $page.post.timeToRead }} min read\u003C/small>\n \u003Cg-image v-if=\"$page.post.cover\" :src=\"$page.post.cover\" class=\"cover\"/>\n \u003Carticle v-html=\"$page.post.content\" class=\"article\"/>\n \u003Cconvert-kit uid=\"44cc02ed05\" script=\"https://f.convertkit.com/44cc02ed05/38739557e4.js\">\u003C/convert-kit>\n \u003C/div>\n \u003Cbulma-tag :tags=\"$page.post.tags\"/>\n\u003C/Layout>\n",[59,133825,133826,133835,133850,133870,133889,133915,133940,133968,133976,133993],{"__ignoreMap":57},[62,133827,133828,133830,133833],{"class":64,"line":65},[62,133829,760],{"class":72},[62,133831,133832],{"class":23824},"Layout",[62,133834,1784],{"class":72},[62,133836,133837,133839,133841,133843,133845,133848],{"class":64,"line":76},[62,133838,33056],{"class":72},[62,133840,15944],{"class":1780},[62,133842,119],{"class":122},[62,133844,146],{"class":72},[62,133846,133847],{"class":1675},"\"article content\"",[62,133849,1784],{"class":72},[62,133851,133852,133854,133856,133858,133860,133863,133866,133868],{"class":64,"line":82},[62,133853,1789],{"class":72},[62,133855,4168],{"class":1780},[62,133857,119],{"class":122},[62,133859,146],{"class":72},[62,133861,133862],{"class":1675},"\"title is-2 article-title\"",[62,133864,133865],{"class":72},">{{ $page.post.title }}\u003C/",[62,133867,4168],{"class":1780},[62,133869,1784],{"class":72},[62,133871,133872,133874,133876,133878,133880,133882,133885,133887],{"class":64,"line":89},[62,133873,1789],{"class":72},[62,133875,105126],{"class":1780},[62,133877,119],{"class":122},[62,133879,146],{"class":72},[62,133881,30803],{"class":1675},[62,133883,133884],{"class":72},">{{ formatCreatedOn }} • ☕️ {{ $page.post.timeToRead }} min read\u003C/",[62,133886,105126],{"class":1780},[62,133888,1784],{"class":72},[62,133890,133891,133893,133895,133897,133899,133901,133903,133905,133907,133909,133911,133913],{"class":64,"line":95},[62,133892,1789],{"class":72},[62,133894,120199],{"class":1780},[62,133896,107033],{"class":122},[62,133898,146],{"class":72},[62,133900,121452],{"class":1675},[62,133902,120202],{"class":122},[62,133904,146],{"class":72},[62,133906,121452],{"class":1675},[62,133908,119],{"class":122},[62,133910,146],{"class":72},[62,133912,121459],{"class":1675},[62,133914,60640],{"class":72},[62,133916,133917,133919,133921,133924,133926,133929,133931,133933,133936,133938],{"class":64,"line":101},[62,133918,1789],{"class":72},[62,133920,39547],{"class":1780},[62,133922,133923],{"class":122}," v-html",[62,133925,146],{"class":72},[62,133927,133928],{"class":1675},"\"$page.post.content\"",[62,133930,119],{"class":122},[62,133932,146],{"class":72},[62,133934,133935],{"class":1675},"\"article\"",[62,133937,6936],{"class":23824},[62,133939,1784],{"class":72},[62,133941,133942,133944,133947,133950,133952,133955,133957,133959,133962,133964,133966],{"class":64,"line":107},[62,133943,1789],{"class":72},[62,133945,133946],{"class":1780},"convert-kit",[62,133948,133949],{"class":122}," uid",[62,133951,146],{"class":72},[62,133953,133954],{"class":1675},"\"44cc02ed05\"",[62,133956,111806],{"class":122},[62,133958,146],{"class":72},[62,133960,133961],{"class":1675},"\"https://f.convertkit.com/44cc02ed05/38739557e4.js\"",[62,133963,15857],{"class":72},[62,133965,133946],{"class":1780},[62,133967,1784],{"class":72},[62,133969,133970,133972,133974],{"class":64,"line":113},[62,133971,33187],{"class":72},[62,133973,15944],{"class":1780},[62,133975,1784],{"class":72},[62,133977,133978,133980,133983,133986,133988,133991],{"class":64,"line":129},[62,133979,33056],{"class":72},[62,133981,133982],{"class":1780},"bulma-tag",[62,133984,133985],{"class":122}," :tags",[62,133987,146],{"class":72},[62,133989,133990],{"class":1675},"\"$page.post.tags\"",[62,133992,60640],{"class":72},[62,133994,133995,133997,133999],{"class":64,"line":134},[62,133996,1818],{"class":72},[62,133998,133832],{"class":23824},[62,134000,1784],{"class":72},[22,134002,134003],{},"This is all of the content for this blog post, so where is that coming from? If you look below the markup there is a GraphQL page query that pulls the information for this post based on the path.",[52,134005,134007],{"className":32791,"code":134006,"language":32793,"meta":57,"style":57},"\u003Cpage-query>\nquery Post ($path: String!) {\n post: post (path: $path) {\n title\n content\n timeToRead\n cover\n slug\n date\n excerpt\n tags {\n id\n title\n path\n }\n }\n}\n\u003C/page-query>\n",[59,134008,134009,134018,134023,134033,134037,134042,134047,134052,134057,134062,134067,134072,134076,134080,134085,134089,134093,134097],{"__ignoreMap":57},[62,134010,134011,134013,134016],{"class":64,"line":65},[62,134012,760],{"class":72},[62,134014,134015],{"class":149},"page-query",[62,134017,1784],{"class":72},[62,134019,134020],{"class":64,"line":76},[62,134021,134022],{"class":72},"query Post ($path: String!) {\n",[62,134024,134025,134028,134030],{"class":64,"line":82},[62,134026,134027],{"class":72}," post: ",[62,134029,38838],{"class":122},[62,134031,134032],{"class":72}," (path: $path) {\n",[62,134034,134035],{"class":64,"line":89},[62,134036,83608],{"class":72},[62,134038,134039],{"class":64,"line":95},[62,134040,134041],{"class":72}," content\n",[62,134043,134044],{"class":64,"line":101},[62,134045,134046],{"class":72}," timeToRead\n",[62,134048,134049],{"class":64,"line":107},[62,134050,134051],{"class":72}," cover\n",[62,134053,134054],{"class":64,"line":113},[62,134055,134056],{"class":72}," slug\n",[62,134058,134059],{"class":64,"line":129},[62,134060,134061],{"class":72}," date\n",[62,134063,134064],{"class":64,"line":134},[62,134065,134066],{"class":72}," excerpt\n",[62,134068,134069],{"class":64,"line":156},[62,134070,134071],{"class":72}," tags {\n",[62,134073,134074],{"class":64,"line":161},[62,134075,83628],{"class":72},[62,134077,134078],{"class":64,"line":167},[62,134079,83633],{"class":72},[62,134081,134082],{"class":64,"line":173},[62,134083,134084],{"class":72}," path\n",[62,134086,134087],{"class":64,"line":179},[62,134088,223],{"class":72},[62,134090,134091],{"class":64,"line":185},[62,134092,3731],{"class":72},[62,134094,134095],{"class":64,"line":191},[62,134096,379],{"class":72},[62,134098,134099,134101,134103],{"class":64,"line":209},[62,134100,1818],{"class":72},[62,134102,134015],{"class":149},[62,134104,1784],{"class":72},[22,134106,134107],{},"Now that you are all caught up let's jump in to how we can create a Twitter card for each blog post.",[26,134109,134111],{"id":134110},"gridsome-meta-tags","Gridsome Meta Tags",[22,134113,126784,134114,134119],{},[677,134115,134118],{"href":134116,"rel":134117},"https://www.danvega.dev/blog/2019/02/18/twitter-cards-meta-tags",[681],"read my previous article"," you know that it isn't that hard to create a twitter card. All we need to do is add some meta tags that end up looking something like this.",[52,134121,134123],{"className":15773,"code":134122,"language":15775,"meta":57,"style":57},"\u003Chead>\n \u003Cmeta name=\"twitter:card\" content=\"summary_large_image\" />\n \u003Cmeta name=\"twitter:description\" content=\"How to create your first npm package and publish it.\"/>\n \u003Cmeta name=\"twitter:title\" content=\"Creating your first npm package\" />\n \u003Cmeta name=\"twitter:site\" content=\"@therealdanvega\" />\n \u003Cmeta name=\"twitter:image\" content=\"https://www.danvega.me/assets/static/npm_cover.bd64798.eced3da.png\"/>\n \u003Cmeta name=\"twitter:creator\" content=\"@therealdanvega\" />\n\u003C/head>\n",[59,134124,134125,134133,134155,134177,134199,134221,134243,134264],{"__ignoreMap":57},[62,134126,134127,134129,134131],{"class":64,"line":65},[62,134128,760],{"class":72},[62,134130,15824],{"class":1780},[62,134132,1784],{"class":72},[62,134134,134135,134137,134139,134141,134143,134146,134148,134150,134153],{"class":64,"line":76},[62,134136,33056],{"class":72},[62,134138,20701],{"class":1780},[62,134140,16107],{"class":122},[62,134142,146],{"class":72},[62,134144,134145],{"class":1675},"\"twitter:card\"",[62,134147,20727],{"class":122},[62,134149,146],{"class":72},[62,134151,134152],{"class":1675},"\"summary_large_image\"",[62,134154,67133],{"class":72},[62,134156,134157,134159,134161,134163,134165,134168,134170,134172,134175],{"class":64,"line":82},[62,134158,33056],{"class":72},[62,134160,20701],{"class":1780},[62,134162,16107],{"class":122},[62,134164,146],{"class":72},[62,134166,134167],{"class":1675},"\"twitter:description\"",[62,134169,20727],{"class":122},[62,134171,146],{"class":72},[62,134173,134174],{"class":1675},"\"How to create your first npm package and publish it.\"",[62,134176,60640],{"class":72},[62,134178,134179,134181,134183,134185,134187,134190,134192,134194,134197],{"class":64,"line":89},[62,134180,33056],{"class":72},[62,134182,20701],{"class":1780},[62,134184,16107],{"class":122},[62,134186,146],{"class":72},[62,134188,134189],{"class":1675},"\"twitter:title\"",[62,134191,20727],{"class":122},[62,134193,146],{"class":72},[62,134195,134196],{"class":1675},"\"Creating your first npm package\"",[62,134198,67133],{"class":72},[62,134200,134201,134203,134205,134207,134209,134212,134214,134216,134219],{"class":64,"line":95},[62,134202,33056],{"class":72},[62,134204,20701],{"class":1780},[62,134206,16107],{"class":122},[62,134208,146],{"class":72},[62,134210,134211],{"class":1675},"\"twitter:site\"",[62,134213,20727],{"class":122},[62,134215,146],{"class":72},[62,134217,134218],{"class":1675},"\"@therealdanvega\"",[62,134220,67133],{"class":72},[62,134222,134223,134225,134227,134229,134231,134234,134236,134238,134241],{"class":64,"line":101},[62,134224,33056],{"class":72},[62,134226,20701],{"class":1780},[62,134228,16107],{"class":122},[62,134230,146],{"class":72},[62,134232,134233],{"class":1675},"\"twitter:image\"",[62,134235,20727],{"class":122},[62,134237,146],{"class":72},[62,134239,134240],{"class":1675},"\"https://www.danvega.me/assets/static/npm_cover.bd64798.eced3da.png\"",[62,134242,60640],{"class":72},[62,134244,134245,134247,134249,134251,134253,134256,134258,134260,134262],{"class":64,"line":107},[62,134246,33056],{"class":72},[62,134248,20701],{"class":1780},[62,134250,16107],{"class":122},[62,134252,146],{"class":72},[62,134254,134255],{"class":1675},"\"twitter:creator\"",[62,134257,20727],{"class":122},[62,134259,146],{"class":72},[62,134261,134218],{"class":1675},[62,134263,67133],{"class":72},[62,134265,134266,134268,134270],{"class":64,"line":113},[62,134267,1818],{"class":72},[62,134269,15824],{"class":1780},[62,134271,1784],{"class":72},[22,134273,134274,134275,134278],{},"The challenge here is that you want each blog post to generate tags specific to that article. Fortunately for us Gridsome gives us a really easy way to populate metadata in our components. All you need to do is add the ",[59,134276,134277],{},"metaInfo"," object to your components script section.",[52,134280,134282],{"className":32791,"code":134281,"language":32793,"meta":57,"style":57},"\u003Cscript>\nexport default {\n name: 'About',\n metaInfo: {\n title: 'About us',\n meta: [\n { name: 'author', content: 'John Doe' }\n ],\n link: [\n { rel: 'stylesheet', href: '/css/index.css' },\n ]\n // etc...\n }\n}\n\u003C/script>\n",[59,134283,134284,134292,134296,134305,134310,134320,134325,134341,134346,134351,134366,134370,134375,134379,134383],{"__ignoreMap":57},[62,134285,134286,134288,134290],{"class":64,"line":65},[62,134287,760],{"class":72},[62,134289,15846],{"class":1780},[62,134291,1784],{"class":72},[62,134293,134294],{"class":64,"line":76},[62,134295,122082],{"class":72},[62,134297,134298,134300,134303],{"class":64,"line":82},[62,134299,106052],{"class":72},[62,134301,134302],{"class":1675},"'About'",[62,134304,3338],{"class":72},[62,134306,134307],{"class":64,"line":89},[62,134308,134309],{"class":72}," metaInfo: {\n",[62,134311,134312,134315,134318],{"class":64,"line":95},[62,134313,134314],{"class":72}," title: ",[62,134316,134317],{"class":1675},"'About us'",[62,134319,3338],{"class":72},[62,134321,134322],{"class":64,"line":101},[62,134323,134324],{"class":72}," meta: [\n",[62,134326,134327,134330,134333,134336,134339],{"class":64,"line":107},[62,134328,134329],{"class":72}," { name: ",[62,134331,134332],{"class":1675},"'author'",[62,134334,134335],{"class":72},", content: ",[62,134337,134338],{"class":1675},"'John Doe'",[62,134340,122649],{"class":72},[62,134342,134343],{"class":64,"line":113},[62,134344,134345],{"class":72}," ],\n",[62,134347,134348],{"class":64,"line":129},[62,134349,134350],{"class":72}," link: [\n",[62,134352,134353,134356,134359,134361,134364],{"class":64,"line":134},[62,134354,134355],{"class":72}," { rel: ",[62,134357,134358],{"class":1675},"'stylesheet'",[62,134360,122191],{"class":72},[62,134362,134363],{"class":1675},"'/css/index.css'",[62,134365,122633],{"class":72},[62,134367,134368],{"class":64,"line":156},[62,134369,3726],{"class":72},[62,134371,134372],{"class":64,"line":161},[62,134373,134374],{"class":85}," // etc...\n",[62,134376,134377],{"class":64,"line":167},[62,134378,3731],{"class":72},[62,134380,134381],{"class":64,"line":173},[62,134382,379],{"class":72},[62,134384,134385,134387,134389],{"class":64,"line":179},[62,134386,1818],{"class":72},[62,134388,15846],{"class":1780},[62,134390,1784],{"class":72},[22,134392,134393],{},[4534,134394,134395,134396,134401],{},"Under the hood Gridsome uses ",[677,134397,134400],{"href":134398,"rel":134399},"https://github.com/nuxt/vue-meta",[681],"Vue Meta"," if you're interested in learning how it works.",[22,134403,134404],{},"In the metaInfo object you can define the following properties.",[11922,134406,134407,134416],{},[11925,134408,134409],{},[11928,134410,134411,134414],{},[11931,134412,134413],{},"Property",[11931,134415,11939],{},[11941,134417,134418,134425,134432,134439,134446,134454],{},[11928,134419,134420,134422],{},[11946,134421,1527],{},[11946,134423,134424],{},"Adds a style tag",[11928,134426,134427,134429],{},[11946,134428,15846],{},[11946,134430,134431],{},"Adds a script tag",[11928,134433,134434,134436],{},[11946,134435,20701],{},[11946,134437,134438],{},"Adds a meta tag",[11928,134440,134441,134443],{},[11946,134442,3196],{},[11946,134444,134445],{},"Changes title text",[11928,134447,134448,134451],{},[11946,134449,134450],{},"titleTemplate",[11946,134452,134453],{},"Dynamic title text",[11928,134455,134456,134458],{},[11946,134457,33106],{},[11946,134459,134460],{},"Adds a link tag",[26,134462,134111],{"id":134463},"gridsome-meta-tags-1",[22,134465,134466,134467,134469,134470,134472],{},"If you notice one of the properties in the ",[59,134468,134277],{}," object that you can add is ",[59,134471,20701],{},". This will allow us to create a meta tag using the following format:",[52,134474,134476],{"className":32791,"code":134475,"language":32793,"meta":57,"style":57},"metaInfo: {\n return {\n title: this.$page.post.title,\n meta: [\n { name: 'META_NAME_HERE' content: 'META_CONTENT_HERE' }\n ],\n };\n},\n",[59,134477,134478,134484,134490,134499,134503,134518,134522,134526],{"__ignoreMap":57},[62,134479,134480,134482],{"class":64,"line":65},[62,134481,134277],{"class":122},[62,134483,3688],{"class":72},[62,134485,134486,134488],{"class":64,"line":76},[62,134487,82091],{"class":68},[62,134489,126],{"class":72},[62,134491,134492,134494,134496],{"class":64,"line":82},[62,134493,134314],{"class":72},[62,134495,1295],{"class":149},[62,134497,134498],{"class":72},".$page.post.title,\n",[62,134500,134501],{"class":64,"line":89},[62,134502,134324],{"class":72},[62,134504,134505,134507,134510,134513,134516],{"class":64,"line":95},[62,134506,134329],{"class":72},[62,134508,134509],{"class":1675},"'META_NAME_HERE'",[62,134511,134512],{"class":72}," content: ",[62,134514,134515],{"class":1675},"'META_CONTENT_HERE'",[62,134517,122649],{"class":72},[62,134519,134520],{"class":64,"line":101},[62,134521,134345],{"class":72},[62,134523,134524],{"class":64,"line":107},[62,134525,82135],{"class":72},[62,134527,134528],{"class":64,"line":113},[62,134529,72286],{"class":72},[22,134531,134532],{},"With that you have everything you need to create your meta tags. In the following snippet I am creating a description meta tag and each of the tags that I need for my Twitter card. For each of the tags I am populating the content with data from the post that we are on. The script property that I am adding is the Twitter API that we need to include for the cards to work.",[52,134534,134536],{"className":32791,"code":134535,"language":32793,"meta":57,"style":57},"metaInfo() {\n return {\n title: this.$page.post.title,\n meta: [\n { name: \"description\", content: this.$page.post.excerpt },\n // twitter-card: https://cards-dev.twitter.com/validator\n { name: \"twitter:card\", content: \"summary_large_image\" },\n { name: \"twitter:description\", content: this.$page.post.excerpt },\n { name: \"twitter:title\", content: this.$page.post.title },\n { name: \"twitter:site\", content: \"@therealdanvega\" },\n { name: \"twitter:image\", content: this.getCoverImage },\n { name: \"twitter:creator\", content: \"@therealdanvega\" }\n ],\n script: [{ src: \"https://platform.twitter.com/widgets.js\", async: true }]\n };\n },\n",[59,134537,134538,134544,134550,134558,134563,134577,134582,134594,134606,134619,134631,134644,134656,134660,134675,134679],{"__ignoreMap":57},[62,134539,134540,134542],{"class":64,"line":65},[62,134541,134277],{"class":122},[62,134543,206],{"class":72},[62,134545,134546,134548],{"class":64,"line":76},[62,134547,2599],{"class":68},[62,134549,126],{"class":72},[62,134551,134552,134554,134556],{"class":64,"line":82},[62,134553,115670],{"class":72},[62,134555,1295],{"class":149},[62,134557,134498],{"class":72},[62,134559,134560],{"class":64,"line":89},[62,134561,134562],{"class":72}," meta: [\n",[62,134564,134565,134568,134570,134572,134574],{"class":64,"line":95},[62,134566,134567],{"class":72}," { name: ",[62,134569,24100],{"class":1675},[62,134571,134335],{"class":72},[62,134573,1295],{"class":149},[62,134575,134576],{"class":72},".$page.post.excerpt },\n",[62,134578,134579],{"class":64,"line":101},[62,134580,134581],{"class":85}," // twitter-card: https://cards-dev.twitter.com/validator\n",[62,134583,134584,134586,134588,134590,134592],{"class":64,"line":107},[62,134585,134567],{"class":72},[62,134587,134145],{"class":1675},[62,134589,134335],{"class":72},[62,134591,134152],{"class":1675},[62,134593,122633],{"class":72},[62,134595,134596,134598,134600,134602,134604],{"class":64,"line":113},[62,134597,134567],{"class":72},[62,134599,134167],{"class":1675},[62,134601,134335],{"class":72},[62,134603,1295],{"class":149},[62,134605,134576],{"class":72},[62,134607,134608,134610,134612,134614,134616],{"class":64,"line":129},[62,134609,134567],{"class":72},[62,134611,134189],{"class":1675},[62,134613,134335],{"class":72},[62,134615,1295],{"class":149},[62,134617,134618],{"class":72},".$page.post.title },\n",[62,134620,134621,134623,134625,134627,134629],{"class":64,"line":134},[62,134622,134567],{"class":72},[62,134624,134211],{"class":1675},[62,134626,134335],{"class":72},[62,134628,134218],{"class":1675},[62,134630,122633],{"class":72},[62,134632,134633,134635,134637,134639,134641],{"class":64,"line":156},[62,134634,134567],{"class":72},[62,134636,134233],{"class":1675},[62,134638,134335],{"class":72},[62,134640,1295],{"class":149},[62,134642,134643],{"class":72},".getCoverImage },\n",[62,134645,134646,134648,134650,134652,134654],{"class":64,"line":161},[62,134647,134567],{"class":72},[62,134649,134255],{"class":1675},[62,134651,134335],{"class":72},[62,134653,134218],{"class":1675},[62,134655,122649],{"class":72},[62,134657,134658],{"class":64,"line":167},[62,134659,49905],{"class":72},[62,134661,134662,134665,134668,134671,134673],{"class":64,"line":173},[62,134663,134664],{"class":72}," script: [{ src: ",[62,134666,134667],{"class":1675},"\"https://platform.twitter.com/widgets.js\"",[62,134669,134670],{"class":72},", async: ",[62,134672,21775],{"class":149},[62,134674,122213],{"class":72},[62,134676,134677],{"class":64,"line":179},[62,134678,40087],{"class":72},[62,134680,134681],{"class":64,"line":185},[62,134682,32848],{"class":72},[22,134684,134685,134686,134691],{},"For more information on what each of these meta tags do you can check out the ",[677,134687,134690],{"href":134688,"rel":134689},"https://developer.twitter.com/en/docs/tweets/optimize-with-cards/guides/getting-started.html",[681],"Twitter Cards Documentation",". This will produce html in your head that looks like this",[52,134693,134695],{"className":15773,"code":134694,"language":15775,"meta":57,"style":57}," \u003Chead>\n \u003Ctitle data-vue-tag=\"true\">How to add Twitter Card Meta Tags to your Blog - Dan Vega\u003C/title>\n \u003Cmeta data-vue-tag=\"true\" charset=\"utf-8\" />\n \u003Cmeta data-vue-tag=\"true\" name=\"generator\" content=\"Gridsome v0.5.0\" />\n \u003Cmeta data-vue-tag=\"true\" data-key=\"viewport\" name=\"viewport\" content=\"width=device-width, initial-scale=1, viewport-fit=cover\" />\n \u003Cmeta data-vue-tag=\"true\" data-key=\"description\" name=\"description\" content=\"Person blog of Dan Vega\" />\n \u003Cmeta data-vue-tag=\"true\" data-key=\"format-detection\" name=\"format-detection\" content=\"telephone=no\" />\n \u003Cmeta data-vue-tag=\"true\" name=\"description\" content=\"In this tutorial you will learn what a Twitter Card is along with step by step instructions how to add them to your blog and validate that they are working.\" />\n \u003Cmeta data-vue-tag=\"true\" name=\"twitter:card\" content=\"summary_large_image\" />\n \u003Cmeta data-vue-tag=\"true\" name=\"twitter:description\" content=\"In this tutorial you will learn what a Twitter Card is along with step by step instructions how to add them to your blog and validate that they are working.\" />\n \u003Cmeta data-vue-tag=\"true\" name=\"twitter:title\" content=\"How to add Twitter Card Meta Tags to your Blog\" />\n \u003Cmeta data-vue-tag=\"true\" name=\"twitter:site\" content=\"@therealdanvega\" />\n \u003Cmeta data-vue-tag=\"true\" name=\"twitter:image\" content=\"https://www.danvega.dev/assets/static/twitter-cards.bd64798.89214ec.png\" />\n \u003Cmeta data-vue-tag=\"true\" name=\"twitter:creator\" content=\"@therealdanvega\" />\n \u003Cscript data-vue-tag=\"true\" src=\"https://platform.twitter.com/widgets.js\" async=\"true\">\u003C/script>\n \u003C/head>\n",[59,134696,134697,134705,134725,134745,134773,134807,134840,134874,134901,134927,134953,134980,135006,135033,135059,135090],{"__ignoreMap":57},[62,134698,134699,134701,134703],{"class":64,"line":65},[62,134700,33056],{"class":72},[62,134702,15824],{"class":1780},[62,134704,1784],{"class":72},[62,134706,134707,134709,134711,134714,134716,134718,134721,134723],{"class":64,"line":76},[62,134708,1789],{"class":72},[62,134710,3196],{"class":1780},[62,134712,134713],{"class":122}," data-vue-tag",[62,134715,146],{"class":72},[62,134717,68406],{"class":1675},[62,134719,134720],{"class":72},">How to add Twitter Card Meta Tags to your Blog - Dan Vega\u003C/",[62,134722,3196],{"class":1780},[62,134724,1784],{"class":72},[62,134726,134727,134729,134731,134733,134735,134737,134739,134741,134743],{"class":64,"line":82},[62,134728,1789],{"class":72},[62,134730,20701],{"class":1780},[62,134732,134713],{"class":122},[62,134734,146],{"class":72},[62,134736,68406],{"class":1675},[62,134738,20704],{"class":122},[62,134740,146],{"class":72},[62,134742,60637],{"class":1675},[62,134744,67133],{"class":72},[62,134746,134747,134749,134751,134753,134755,134757,134759,134761,134764,134766,134768,134771],{"class":64,"line":89},[62,134748,1789],{"class":72},[62,134750,20701],{"class":1780},[62,134752,134713],{"class":122},[62,134754,146],{"class":72},[62,134756,68406],{"class":1675},[62,134758,16107],{"class":122},[62,134760,146],{"class":72},[62,134762,134763],{"class":1675},"\"generator\"",[62,134765,20727],{"class":122},[62,134767,146],{"class":72},[62,134769,134770],{"class":1675},"\"Gridsome v0.5.0\"",[62,134772,67133],{"class":72},[62,134774,134775,134777,134779,134781,134783,134785,134788,134790,134792,134794,134796,134798,134800,134802,134805],{"class":64,"line":95},[62,134776,1789],{"class":72},[62,134778,20701],{"class":1780},[62,134780,134713],{"class":122},[62,134782,146],{"class":72},[62,134784,68406],{"class":1675},[62,134786,134787],{"class":122}," data-key",[62,134789,146],{"class":72},[62,134791,20724],{"class":1675},[62,134793,16107],{"class":122},[62,134795,146],{"class":72},[62,134797,20724],{"class":1675},[62,134799,20727],{"class":122},[62,134801,146],{"class":72},[62,134803,134804],{"class":1675},"\"width=device-width, initial-scale=1, viewport-fit=cover\"",[62,134806,67133],{"class":72},[62,134808,134809,134811,134813,134815,134817,134819,134821,134823,134825,134827,134829,134831,134833,134835,134838],{"class":64,"line":101},[62,134810,1789],{"class":72},[62,134812,20701],{"class":1780},[62,134814,134713],{"class":122},[62,134816,146],{"class":72},[62,134818,68406],{"class":1675},[62,134820,134787],{"class":122},[62,134822,146],{"class":72},[62,134824,24100],{"class":1675},[62,134826,16107],{"class":122},[62,134828,146],{"class":72},[62,134830,24100],{"class":1675},[62,134832,20727],{"class":122},[62,134834,146],{"class":72},[62,134836,134837],{"class":1675},"\"Person blog of Dan Vega\"",[62,134839,67133],{"class":72},[62,134841,134842,134844,134846,134848,134850,134852,134854,134856,134859,134861,134863,134865,134867,134869,134872],{"class":64,"line":107},[62,134843,1789],{"class":72},[62,134845,20701],{"class":1780},[62,134847,134713],{"class":122},[62,134849,146],{"class":72},[62,134851,68406],{"class":1675},[62,134853,134787],{"class":122},[62,134855,146],{"class":72},[62,134857,134858],{"class":1675},"\"format-detection\"",[62,134860,16107],{"class":122},[62,134862,146],{"class":72},[62,134864,134858],{"class":1675},[62,134866,20727],{"class":122},[62,134868,146],{"class":72},[62,134870,134871],{"class":1675},"\"telephone=no\"",[62,134873,67133],{"class":72},[62,134875,134876,134878,134880,134882,134884,134886,134888,134890,134892,134894,134896,134899],{"class":64,"line":113},[62,134877,1789],{"class":72},[62,134879,20701],{"class":1780},[62,134881,134713],{"class":122},[62,134883,146],{"class":72},[62,134885,68406],{"class":1675},[62,134887,16107],{"class":122},[62,134889,146],{"class":72},[62,134891,24100],{"class":1675},[62,134893,20727],{"class":122},[62,134895,146],{"class":72},[62,134897,134898],{"class":1675},"\"In this tutorial you will learn what a Twitter Card is along with step by step instructions how to add them to your blog and validate that they are working.\"",[62,134900,67133],{"class":72},[62,134902,134903,134905,134907,134909,134911,134913,134915,134917,134919,134921,134923,134925],{"class":64,"line":129},[62,134904,1789],{"class":72},[62,134906,20701],{"class":1780},[62,134908,134713],{"class":122},[62,134910,146],{"class":72},[62,134912,68406],{"class":1675},[62,134914,16107],{"class":122},[62,134916,146],{"class":72},[62,134918,134145],{"class":1675},[62,134920,20727],{"class":122},[62,134922,146],{"class":72},[62,134924,134152],{"class":1675},[62,134926,67133],{"class":72},[62,134928,134929,134931,134933,134935,134937,134939,134941,134943,134945,134947,134949,134951],{"class":64,"line":134},[62,134930,1789],{"class":72},[62,134932,20701],{"class":1780},[62,134934,134713],{"class":122},[62,134936,146],{"class":72},[62,134938,68406],{"class":1675},[62,134940,16107],{"class":122},[62,134942,146],{"class":72},[62,134944,134167],{"class":1675},[62,134946,20727],{"class":122},[62,134948,146],{"class":72},[62,134950,134898],{"class":1675},[62,134952,67133],{"class":72},[62,134954,134955,134957,134959,134961,134963,134965,134967,134969,134971,134973,134975,134978],{"class":64,"line":156},[62,134956,1789],{"class":72},[62,134958,20701],{"class":1780},[62,134960,134713],{"class":122},[62,134962,146],{"class":72},[62,134964,68406],{"class":1675},[62,134966,16107],{"class":122},[62,134968,146],{"class":72},[62,134970,134189],{"class":1675},[62,134972,20727],{"class":122},[62,134974,146],{"class":72},[62,134976,134977],{"class":1675},"\"How to add Twitter Card Meta Tags to your Blog\"",[62,134979,67133],{"class":72},[62,134981,134982,134984,134986,134988,134990,134992,134994,134996,134998,135000,135002,135004],{"class":64,"line":161},[62,134983,1789],{"class":72},[62,134985,20701],{"class":1780},[62,134987,134713],{"class":122},[62,134989,146],{"class":72},[62,134991,68406],{"class":1675},[62,134993,16107],{"class":122},[62,134995,146],{"class":72},[62,134997,134211],{"class":1675},[62,134999,20727],{"class":122},[62,135001,146],{"class":72},[62,135003,134218],{"class":1675},[62,135005,67133],{"class":72},[62,135007,135008,135010,135012,135014,135016,135018,135020,135022,135024,135026,135028,135031],{"class":64,"line":167},[62,135009,1789],{"class":72},[62,135011,20701],{"class":1780},[62,135013,134713],{"class":122},[62,135015,146],{"class":72},[62,135017,68406],{"class":1675},[62,135019,16107],{"class":122},[62,135021,146],{"class":72},[62,135023,134233],{"class":1675},[62,135025,20727],{"class":122},[62,135027,146],{"class":72},[62,135029,135030],{"class":1675},"\"https://www.danvega.dev/assets/static/twitter-cards.bd64798.89214ec.png\"",[62,135032,67133],{"class":72},[62,135034,135035,135037,135039,135041,135043,135045,135047,135049,135051,135053,135055,135057],{"class":64,"line":173},[62,135036,1789],{"class":72},[62,135038,20701],{"class":1780},[62,135040,134713],{"class":122},[62,135042,146],{"class":72},[62,135044,68406],{"class":1675},[62,135046,16107],{"class":122},[62,135048,146],{"class":72},[62,135050,134255],{"class":1675},[62,135052,20727],{"class":122},[62,135054,146],{"class":72},[62,135056,134218],{"class":1675},[62,135058,67133],{"class":72},[62,135060,135061,135063,135065,135067,135069,135071,135073,135075,135077,135080,135082,135084,135086,135088],{"class":64,"line":179},[62,135062,1789],{"class":72},[62,135064,15846],{"class":1780},[62,135066,134713],{"class":122},[62,135068,146],{"class":72},[62,135070,68406],{"class":1675},[62,135072,15849],{"class":122},[62,135074,146],{"class":72},[62,135076,134667],{"class":1675},[62,135078,135079],{"class":122}," async",[62,135081,146],{"class":72},[62,135083,68406],{"class":1675},[62,135085,15857],{"class":72},[62,135087,15846],{"class":1780},[62,135089,1784],{"class":72},[62,135091,135092,135094,135096],{"class":64,"line":185},[62,135093,33187],{"class":72},[62,135095,15824],{"class":1780},[62,135097,1784],{"class":72},[22,135099,135100],{},"And this is an example of what a card might look like",[22,135102,135103],{},[653,135104],{"alt":57,"src":135105},"/images/blog/2019/04/09/2019-04-09_14-22-55-7de0bfb4-6ad0-4bd0-bf31-d2e9092d37ca.png",[26,135107,1499],{"id":1498},[22,135109,135110,135111,2755],{},"Twitter cards add a really nice touch to sharing your articles on Twitter. Tweets with an image will get more impressions and clicks than tweets without one so what are you waiting for? If you're interested in seeing the code for my entire Gridsome blog you can ",[677,135112,36806],{"href":113931,"rel":135113},[681],[1527,135115,135116],{},"html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .shOWo, html code.shiki .shOWo{--shiki-default:#B31D28;--shiki-default-font-style:italic;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic;--shiki-sepia:#B31D28;--shiki-sepia-font-style:italic}html pre.shiki code .suaqH, html code.shiki .suaqH{--shiki-default:#22863A;--shiki-dark:#85E89D;--shiki-sepia:#22863A}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}html pre.shiki code .s4-Ip, html code.shiki .s4-Ip{--shiki-default:#6A737D;--shiki-dark:#6A737D;--shiki-sepia:#6A737D}html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}",{"title":57,"searchDepth":76,"depth":76,"links":135118},[135119,135120,135121,135122],{"id":133756,"depth":76,"text":133757},{"id":134110,"depth":76,"text":134111},{"id":134463,"depth":76,"text":134111},{"id":1498,"depth":76,"text":1499},{"_id":135124,"path":135125,"title":135126,"description":135127,"meta":135128,"body":135133},"content/blog/2019/03/14/find-max-array-objects-javascript.md","/blog/2019/03/14/find-max-array-objects-javascript","How to find the max id in an array of objects in JavaScript","A quick tutorial on the thought process on how you would go about finding the max id in an array of objects in JavaScript",{"slug":135129,"date":135130,"published":13,"tags":135131,"author":17,"cover":135132,"excerpt":-1},"find-max-array-objects-javascript","2019-03-14T10:59:53.416Z",[32793],"joshua-aragon-EaB4Ml7C7fE-unsplash.jpg",{"type":19,"value":135134,"toc":135876},[135135,135138,135141,135155,135158,135167,135190,135199,135408,135411,135414,135417,135496,135499,135502,135514,135595,135598,135671,135674,135684,135763,135770,135774,135782,135792,135828,135833,135836,135840,135850,135864,135866,135869,135873],[22,135136,135137],{},"I was recently putting together a tutorial for some students and I came across an issue that I am sure we have all come across at one time or another. I had this array of objects and in each object I had an id. I wanted a quick and efficient way to look at all the ids and find the max id.",[22,135139,135140],{},"In this article I will walk you through a 4 different solutions.",[915,135142,135143,135146,135149,135152],{},[37,135144,135145],{},"Array.forEach",[37,135147,135148],{},"Array.map",[37,135150,135151],{},"Array.reduce",[37,135153,135154],{},"Math.max",[26,135156,135157],{"id":2894},"Setting up your project",[22,135159,135160,135161,135166],{},"I am using Node to run this example but you can just as easily use JavaScript and drop this into a web page. The first thing that I am going to do is to require the ",[677,135162,135165],{"href":135163,"rel":135164},"https://nodejs.org/api/assert.html",[681],"assert module"," from Node which gives us the ability to provide a simple set of assertion tests.",[52,135168,135170],{"className":32791,"code":135169,"language":32793,"meta":57,"style":57},"const assert = require(\"assert\");\n",[59,135171,135172],{"__ignoreMap":57},[62,135173,135174,135176,135179,135181,135183,135185,135188],{"class":64,"line":65},[62,135175,110541],{"class":68},[62,135177,135178],{"class":149}," assert",[62,135180,2556],{"class":68},[62,135182,120735],{"class":122},[62,135184,2109],{"class":72},[62,135186,135187],{"class":1675},"\"assert\"",[62,135189,1133],{"class":72},[22,135191,135192,135193,135198],{},"Next you are going to create an array of characters. I recently read the book ",[677,135194,135197],{"href":135195,"rel":135196},"https://amzn.to/2TFwvIC",[681],"Bad Blood"," so I decided to use some of the characters from that book. Side note: If you haven't read that book yet, I highly recommend it. Each character in the array is going to be an object that contains an id, first name and last name.",[52,135200,135202],{"className":32791,"code":135201,"language":32793,"meta":57,"style":57},"const characters = [\n { id: 1, first: \"Tim\", last: \"Draper\" },\n { id: 17, first: \"David\", last: \"Boies\" },\n { id: 199, first: \"Tim\", last: \"Kemp\" },\n { id: 75, first: \"Henry\", last: \"Mosley\" },\n { id: 444, first: \"Elizabeth\", last: \"Holmes\" },\n { id: 95, first: \"Donald\", last: \"Lucas\" },\n { id: 186, first: \"Larry\", last: \"Ellison\" },\n { id: 364, first: \"Channing\", last: \"Robertson\" },\n { id: 285, first: \"Charles\", last: \"Fleischmann\" },\n { id: 33, first: \"John\", last: \"Howard\" }\n];\n",[59,135203,135204,135215,135236,135254,135271,135290,135309,135328,135347,135366,135385,135404],{"__ignoreMap":57},[62,135205,135206,135208,135211,135213],{"class":64,"line":65},[62,135207,110541],{"class":68},[62,135209,135210],{"class":149}," characters",[62,135212,2556],{"class":68},[62,135214,103023],{"class":72},[62,135216,135217,135220,135222,135225,135228,135231,135234],{"class":64,"line":76},[62,135218,135219],{"class":72}," { id: ",[62,135221,6689],{"class":149},[62,135223,135224],{"class":72},", first: ",[62,135226,135227],{"class":1675},"\"Tim\"",[62,135229,135230],{"class":72},", last: ",[62,135232,135233],{"class":1675},"\"Draper\"",[62,135235,122633],{"class":72},[62,135237,135238,135240,135243,135245,135247,135249,135252],{"class":64,"line":82},[62,135239,135219],{"class":72},[62,135241,135242],{"class":149},"17",[62,135244,135224],{"class":72},[62,135246,25178],{"class":1675},[62,135248,135230],{"class":72},[62,135250,135251],{"class":1675},"\"Boies\"",[62,135253,122633],{"class":72},[62,135255,135256,135258,135260,135262,135264,135266,135269],{"class":64,"line":89},[62,135257,135219],{"class":72},[62,135259,119514],{"class":149},[62,135261,135224],{"class":72},[62,135263,135227],{"class":1675},[62,135265,135230],{"class":72},[62,135267,135268],{"class":1675},"\"Kemp\"",[62,135270,122633],{"class":72},[62,135272,135273,135275,135278,135280,135283,135285,135288],{"class":64,"line":95},[62,135274,135219],{"class":72},[62,135276,135277],{"class":149},"75",[62,135279,135224],{"class":72},[62,135281,135282],{"class":1675},"\"Henry\"",[62,135284,135230],{"class":72},[62,135286,135287],{"class":1675},"\"Mosley\"",[62,135289,122633],{"class":72},[62,135291,135292,135294,135297,135299,135302,135304,135307],{"class":64,"line":101},[62,135293,135219],{"class":72},[62,135295,135296],{"class":149},"444",[62,135298,135224],{"class":72},[62,135300,135301],{"class":1675},"\"Elizabeth\"",[62,135303,135230],{"class":72},[62,135305,135306],{"class":1675},"\"Holmes\"",[62,135308,122633],{"class":72},[62,135310,135311,135313,135316,135318,135321,135323,135326],{"class":64,"line":107},[62,135312,135219],{"class":72},[62,135314,135315],{"class":149},"95",[62,135317,135224],{"class":72},[62,135319,135320],{"class":1675},"\"Donald\"",[62,135322,135230],{"class":72},[62,135324,135325],{"class":1675},"\"Lucas\"",[62,135327,122633],{"class":72},[62,135329,135330,135332,135335,135337,135340,135342,135345],{"class":64,"line":113},[62,135331,135219],{"class":72},[62,135333,135334],{"class":149},"186",[62,135336,135224],{"class":72},[62,135338,135339],{"class":1675},"\"Larry\"",[62,135341,135230],{"class":72},[62,135343,135344],{"class":1675},"\"Ellison\"",[62,135346,122633],{"class":72},[62,135348,135349,135351,135354,135356,135359,135361,135364],{"class":64,"line":129},[62,135350,135219],{"class":72},[62,135352,135353],{"class":149},"364",[62,135355,135224],{"class":72},[62,135357,135358],{"class":1675},"\"Channing\"",[62,135360,135230],{"class":72},[62,135362,135363],{"class":1675},"\"Robertson\"",[62,135365,122633],{"class":72},[62,135367,135368,135370,135373,135375,135378,135380,135383],{"class":64,"line":134},[62,135369,135219],{"class":72},[62,135371,135372],{"class":149},"285",[62,135374,135224],{"class":72},[62,135376,135377],{"class":1675},"\"Charles\"",[62,135379,135230],{"class":72},[62,135381,135382],{"class":1675},"\"Fleischmann\"",[62,135384,122633],{"class":72},[62,135386,135387,135389,135392,135394,135397,135399,135402],{"class":64,"line":156},[62,135388,135219],{"class":72},[62,135390,135391],{"class":149},"33",[62,135393,135224],{"class":72},[62,135395,135396],{"class":1675},"\"John\"",[62,135398,135230],{"class":72},[62,135400,135401],{"class":1675},"\"Howard\"",[62,135403,122649],{"class":72},[62,135405,135406],{"class":64,"line":161},[62,135407,109215],{"class":72},[22,135409,135410],{},"In each possible solution you want to write some logic that will return the largest id in the array.",[26,135412,135145],{"id":135413},"arrayforeach",[22,135415,135416],{},"Your first thought might be to iterate over the array using some type of loop and why not, its what you have been taught to do since you started writing code. You can start off by declaring a max of zero, iterate over each character and if its id is larger than the max, update the max.",[52,135418,135420],{"className":32791,"code":135419,"language":32793,"meta":57,"style":57},"let max = 0;\ncharacters.forEach(character => {\n if (character.id > max) {\n max = character.id;\n }\n});\nassert(max === 444);\n",[59,135421,135422,135435,135451,135463,135473,135477,135481],{"__ignoreMap":57},[62,135423,135424,135426,135429,135431,135433],{"class":64,"line":65},[62,135425,21958],{"class":68},[62,135427,135428],{"class":72}," max ",[62,135430,146],{"class":68},[62,135432,150],{"class":149},[62,135434,153],{"class":72},[62,135436,135437,135440,135442,135444,135447,135449],{"class":64,"line":76},[62,135438,135439],{"class":72},"characters.",[62,135441,4215],{"class":122},[62,135443,2109],{"class":72},[62,135445,135446],{"class":889},"character",[62,135448,85402],{"class":68},[62,135450,126],{"class":72},[62,135452,135453,135455,135458,135460],{"class":64,"line":82},[62,135454,107558],{"class":68},[62,135456,135457],{"class":72}," (character.id ",[62,135459,2583],{"class":68},[62,135461,135462],{"class":72}," max) {\n",[62,135464,135465,135468,135470],{"class":64,"line":89},[62,135466,135467],{"class":72}," max ",[62,135469,146],{"class":68},[62,135471,135472],{"class":72}," character.id;\n",[62,135474,135475],{"class":64,"line":95},[62,135476,3731],{"class":72},[62,135478,135479],{"class":64,"line":101},[62,135480,85531],{"class":72},[62,135482,135483,135486,135489,135491,135494],{"class":64,"line":107},[62,135484,135485],{"class":122},"assert",[62,135487,135488],{"class":72},"(max ",[62,135490,21072],{"class":68},[62,135492,135493],{"class":149}," 444",[62,135495,1133],{"class":72},[22,135497,135498],{},"My main objective every time I write code is to get something to work first and then improve upon it later. If you were to run this code it certainly works but it just doesn't seem right to me. What if you have one thousand or a million objects in the array? Any time that I start iterating over an array using some type of loop to perform som calculation that is a huge red flag for me.",[26,135500,135148],{"id":135501},"arraymap",[22,135503,135504,135505,87561,135510,135513],{},"Whenever that red flag of iterating over an array comes up I immediately ask myself is this something that map/filter/reduce can solve. So if you were going to take that approach here you can start with the ",[677,135506,135509],{"href":135507,"rel":135508},"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map",[681],"map method",[59,135511,135512],{},"map()"," method creates a new array with the results of calling a provided function on every element in the calling array. You will use the map method to create a new array that contains just the id's so you're no longer working with objects. At this point you can just use the normal sort method on the array, grab the last element in the list and that is your max id.",[52,135515,135517],{"className":32791,"code":135516,"language":32793,"meta":57,"style":57},"const ids = characters.map(user => user.id);\nconst sorted = ids.sort((a, b) => a - b);\nassert(sorted[sorted.length - 1] === 444);\n",[59,135518,135519,135542,135574],{"__ignoreMap":57},[62,135520,135521,135523,135526,135528,135531,135533,135535,135537,135539],{"class":64,"line":65},[62,135522,110541],{"class":68},[62,135524,135525],{"class":149}," ids",[62,135527,2556],{"class":68},[62,135529,135530],{"class":72}," characters.",[62,135532,4200],{"class":122},[62,135534,2109],{"class":72},[62,135536,2502],{"class":889},[62,135538,85402],{"class":68},[62,135540,135541],{"class":72}," user.id);\n",[62,135543,135544,135546,135549,135551,135554,135556,135558,135560,135562,135564,135566,135568,135570,135572],{"class":64,"line":76},[62,135545,110541],{"class":68},[62,135547,135548],{"class":149}," sorted",[62,135550,2556],{"class":68},[62,135552,135553],{"class":72}," ids.",[62,135555,7228],{"class":122},[62,135557,85590],{"class":72},[62,135559,677],{"class":889},[62,135561,976],{"class":72},[62,135563,64495],{"class":889},[62,135565,5024],{"class":72},[62,135567,21525],{"class":68},[62,135569,28887],{"class":72},[62,135571,11635],{"class":68},[62,135573,26485],{"class":72},[62,135575,135576,135578,135581,135583,135585,135587,135589,135591,135593],{"class":64,"line":82},[62,135577,135485],{"class":122},[62,135579,135580],{"class":72},"(sorted[sorted.",[62,135582,14193],{"class":149},[62,135584,128533],{"class":68},[62,135586,22038],{"class":149},[62,135588,36871],{"class":72},[62,135590,21072],{"class":68},[62,135592,135493],{"class":149},[62,135594,1133],{"class":72},[22,135596,135597],{},"If you wanted to get really fancy you can do all of that in a single statement.",[52,135599,135601],{"className":32791,"code":135600,"language":32793,"meta":57,"style":57},"assert(\n characters.map(user => user.id).sort((a, b) => a - b)[\n characters.length - 1\n ] === 444\n);\n",[59,135602,135603,135609,135646,135657,135667],{"__ignoreMap":57},[62,135604,135605,135607],{"class":64,"line":65},[62,135606,135485],{"class":122},[62,135608,3301],{"class":72},[62,135610,135611,135614,135616,135618,135620,135622,135625,135627,135629,135631,135633,135635,135637,135639,135641,135643],{"class":64,"line":76},[62,135612,135613],{"class":72}," characters.",[62,135615,4200],{"class":122},[62,135617,2109],{"class":72},[62,135619,2502],{"class":889},[62,135621,85402],{"class":68},[62,135623,135624],{"class":72}," user.id).",[62,135626,7228],{"class":122},[62,135628,85590],{"class":72},[62,135630,677],{"class":889},[62,135632,976],{"class":72},[62,135634,64495],{"class":889},[62,135636,5024],{"class":72},[62,135638,21525],{"class":68},[62,135640,28887],{"class":72},[62,135642,11635],{"class":68},[62,135644,135645],{"class":72}," b)[\n",[62,135647,135648,135651,135653,135655],{"class":64,"line":82},[62,135649,135650],{"class":72}," characters.",[62,135652,14193],{"class":149},[62,135654,128533],{"class":68},[62,135656,125103],{"class":149},[62,135658,135659,135662,135664],{"class":64,"line":89},[62,135660,135661],{"class":72}," ] ",[62,135663,21072],{"class":68},[62,135665,135666],{"class":149}," 444\n",[62,135668,135669],{"class":64,"line":95},[62,135670,1133],{"class":72},[26,135672,135151],{"id":135673},"arrayreduce",[22,135675,3521,135676,135679,135680,135683],{},[59,135677,135678],{},"reduce()"," method executes a reducer function (that you provide) on each member of the array resulting in a single output value. Here you are going to call reduce on the characters array. You will define a accumulator which will accumulate the callback's return value. Every time the callback function is called it will return a value and store it in max. The callback method is looking at the character id and if it's greater than max it returns that, if not it returns max. Finally it needs to set a default value for max and that is what ",[59,135681,135682],{},"characters[0].id"," is doing.",[52,135685,135687],{"className":32791,"code":135686,"language":32793,"meta":57,"style":57},"const maxId = characters.reduce(\n (max, character) => (character.id > max ? character.id : max),\n characters[0].id\n);\nassert(maxId === 444);\n",[59,135688,135689,135704,135736,135746,135750],{"__ignoreMap":57},[62,135690,135691,135693,135696,135698,135700,135702],{"class":64,"line":65},[62,135692,110541],{"class":68},[62,135694,135695],{"class":149}," maxId",[62,135697,2556],{"class":68},[62,135699,135530],{"class":72},[62,135701,4218],{"class":122},[62,135703,3301],{"class":72},[62,135705,135706,135709,135712,135714,135716,135718,135720,135722,135724,135726,135728,135731,135733],{"class":64,"line":76},[62,135707,135708],{"class":72}," (",[62,135710,135711],{"class":889},"max",[62,135713,976],{"class":72},[62,135715,135446],{"class":889},[62,135717,5024],{"class":72},[62,135719,21525],{"class":68},[62,135721,135457],{"class":72},[62,135723,2583],{"class":68},[62,135725,135428],{"class":72},[62,135727,5668],{"class":68},[62,135729,135730],{"class":72}," character.id ",[62,135732,1266],{"class":68},[62,135734,135735],{"class":72}," max),\n",[62,135737,135738,135741,135743],{"class":64,"line":82},[62,135739,135740],{"class":72}," characters[",[62,135742,1130],{"class":149},[62,135744,135745],{"class":72},"].id\n",[62,135747,135748],{"class":64,"line":89},[62,135749,1133],{"class":72},[62,135751,135752,135754,135757,135759,135761],{"class":64,"line":95},[62,135753,135485],{"class":122},[62,135755,135756],{"class":72},"(maxId ",[62,135758,21072],{"class":68},[62,135760,135493],{"class":149},[62,135762,1133],{"class":72},[22,135764,135765,135766,135769],{},"If you notice this is a similar approach to us using the forEach method, iterating over each element and comparing it to max. The difference here is using reduce is much faster. You can also use a combination of reduce and the next solution ",[59,135767,135768],{},"Math.max()"," if you want to.",[26,135771,135773],{"id":135772},"the-spread-operator","The Spread Operator",[22,135775,135776,135777,135781],{},"While I like the map and reduce approach much better than iterating over the array it still doesn't feel great to me. If you dig into ",[677,135778,135154],{"href":135779,"rel":135780},"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/max",[681]," you will find out that it can take a list of numbers or an array of numbers.",[22,135783,135784,135785,135788,135789,135791],{},"This means that all you have to do is get an array of id's and you can pass that into the ",[59,135786,135787],{},"max()"," function. If you were paying attention you already did that earlier using map. This means that you can use the map function to get an array of id's and then pass it into the ",[59,135790,135787],{}," function using the spread operator.",[52,135793,135795],{"className":32791,"code":135794,"language":32793,"meta":57,"style":57},"assert(Math.max(...characters.map(user => user.id)) === 444);\n",[59,135796,135797],{"__ignoreMap":57},[62,135798,135799,135801,135803,135805,135807,135809,135811,135813,135815,135817,135819,135822,135824,135826],{"class":64,"line":65},[62,135800,135485],{"class":122},[62,135802,6964],{"class":72},[62,135804,135711],{"class":122},[62,135806,2109],{"class":72},[62,135808,115466],{"class":68},[62,135810,135439],{"class":72},[62,135812,4200],{"class":122},[62,135814,2109],{"class":72},[62,135816,2502],{"class":889},[62,135818,85402],{"class":68},[62,135820,135821],{"class":72}," user.id)) ",[62,135823,21072],{"class":68},[62,135825,135493],{"class":149},[62,135827,1133],{"class":72},[29685,135829,135830],{},[22,135831,135832],{},"Spread syntax allows an iterable such as an array expression or string to be expanded in places where zero or more arguments (for function calls) or elements (for array literals) are expected, or an object expression to be expanded in places where zero or more key-value pairs (for object literals) are expected.",[22,135834,135835],{},"This to me is the cleanest looking solution and I happen to really like it.",[26,135837,135839],{"id":135838},"performance-results","Performance Results",[22,135841,135842,135843,51560,135846,135849],{},"I didn't mean to turn this into an article about functional programming in JavaScript but this is where we landed. I did some basic timing of each of the functions (using ",[59,135844,135845],{},"console.time()",[59,135847,135848],{},"console.timeEnd()",") and found the following results.",[915,135851,135852,135855,135858,135861],{},[37,135853,135854],{},"iterate test: 0.217ms",[37,135856,135857],{},"map test: 0.191ms",[37,135859,135860],{},"reduce test: 0.144ms",[37,135862,135863],{},"spread test: 0.148ms",[26,135865,1499],{"id":1498},[22,135867,135868],{},"While using reduce gave us an ever so slight performance advantage I prefer using Math.max and the spread operator here. To me it just looks cleaner and it is something that I enjoy writing. If you were given this problem what solution do you reach for? Am I missing any? I hope you enjoyed walking through how I would solve this problem and until next time...",[22,135870,36004,135871,82545],{},[36006,135872],{},[1527,135874,135875],{},"html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .spoOn, html code.shiki .spoOn{--shiki-default:#E36209;--shiki-dark:#FFAB70;--shiki-sepia:#E36209}",{"title":57,"searchDepth":76,"depth":76,"links":135877},[135878,135879,135880,135881,135882,135883,135884],{"id":2894,"depth":76,"text":135157},{"id":135413,"depth":76,"text":135145},{"id":135501,"depth":76,"text":135148},{"id":135673,"depth":76,"text":135151},{"id":135772,"depth":76,"text":135773},{"id":135838,"depth":76,"text":135839},{"id":1498,"depth":76,"text":1499},{"_id":135886,"path":135887,"title":135888,"description":135889,"meta":135890,"body":135895},"content/blog/2019/03/07/spring-initializr-fresh-look.md","/blog/2019/03/07/spring-initializr-fresh-look","Spring Initializr's Fresh New Look","In this article I will tell you what the Spring Initializr is and give you my thoughts on the new redesign.",{"slug":135891,"date":135892,"published":13,"tags":135893,"author":17,"cover":135894,"excerpt":-1},"spring-initializr-fresh-look","2019-03-07 17:00:00",[11002],"./spring-init-cover.png",{"type":19,"value":135896,"toc":136042},[135897,135910,135916,135920,135927,135930,135933,135939,135943,135961,135964,135995,135999,136002,136005,136011,136014,136020,136023,136026,136028,136031,136038],[22,135898,135899,135900,135905,135906,135909],{},"The Spring Team ",[677,135901,135904],{"href":135902,"rel":135903},"https://spring.io/blog/2019/03/05/spring-initializr-new-ui",[681],"just announced"," that the Spring Initializr has been updated with a brand new UI and it is now available at ",[677,135907,40207],{"href":40207,"rel":135908},[681],". You can see a screen shot of it below but I have say my first impressions were all positive.",[22,135911,135912],{},[653,135913],{"alt":135914,"src":135915},"Spring Initializr New Look","./spring-init-new-look.png",[26,135917,135919],{"id":135918},"what-is-the-spring-initializr","What is the Spring Initializr",[22,135921,135922,135923,135926],{},"Before I get into my thoughts on the redesign it might be a good idea to discuss what the Spring Initializr is for those of you who might be new to it. The ",[677,135924,24606],{"href":1744,"rel":135925},[681]," provides a web UI that will allow you the ability to quickly create a new Spring Boot project customized just the way you want it.",[22,135928,135929],{},"When I am describing this to students I often make the analogy of going to the grocery store when you're planning on preparing a big meal. You know what you need to make and you know what the ingredients are and now you need to gather them all up.",[22,135931,135932],{},"When you're building a new application you have a similar story. You know what you're building, even if you don't know all the details. If you find out later on you need to add new features you can certainly do so, the same way you inevitably will need to run out on Thanksgiving for those cranberries.",[22,135934,135935],{},[653,135936],{"alt":135937,"src":135938},"Shopping List","./shopping-list.png",[636,135940,135942],{"id":135941},"spring-initializr-features","Spring Initializr Features",[22,135944,135945,135946,135949,135950,135955,135956,34867,135959,2755],{},"It is worth mentioning that the Spring Intitializr at ",[677,135947,1744],{"href":1744,"rel":135948},[681]," isn't the only way to create a new project. IDE's like Eclips, IntelliJ IDEA Ultimate and NetBeans have this built in and make it really easy to select your dependencies. You can also kick start a project running a similar wizard from Visual Studio Code which is really handy. Finally if the command line is your thing you can create a new project using the ",[677,135951,135954],{"href":135952,"rel":135953},"https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#cli-init",[681],"Spring Boot CLI"," or by using ",[59,135957,135958],{},"cURL",[59,135960,35200],{},[22,135962,135963],{},"The Spring Initalizr let's you customize the following options in your prjoject.",[915,135965,135966,135975,135987,135990,135993],{},[37,135967,135968,135969],{},"Build System\n",[915,135970,135971,135973],{},[37,135972,94504],{},[37,135974,54946],{},[37,135976,135977,135978],{},"Language\n",[915,135979,135980,135982,135985],{},[37,135981,15],{},[37,135983,135984],{},"Kotlin",[37,135986,53590],{},[37,135988,135989],{},"Spring Boot Version",[37,135991,135992],{},"Project Meta Data",[37,135994,2931],{},[26,135996,135998],{"id":135997},"the-new-look-feel","The new look & feel",[22,136000,136001],{},"Now that you know a little bit about what the Spring Boot Initilaizr is let's talk about the new look and feel. As I said earlier in the article my first impressions were all great. I am someone who has always enjoyed a nice clean interface so this appealed to me. At a quick glance I was able to fill out everything I needed for my project but there is one small problem and to some folks its a big issue.",[22,136003,136004],{},"I didn't even notice this at first but I was scrolling through the comments on Damien's announcement I noticed a theme.",[22,136006,136007],{},[677,136008,136009],{"href":136009,"rel":136010},"https://twitter.com/oodamien/status/1102875897290473472",[681],[22,136012,136013],{},"The problem is that in the previous version of the Spring Initializr you could switch to a full version and see all of the available starters.",[22,136015,136016],{},[653,136017],{"alt":136018,"src":136019},"Spring Initializr Full Version","./spring-init-full-version.jpg",[22,136021,136022],{},"This was very useful for beginners and but I don't think it was just geared towards them. There are a ton of available dependencies and it's really hard to remember what is available. The team addressed this in their announcement post and I am hoping this is something they address in the future.",[22,136024,136025],{},"The important thing to note is that you will still get the full list if you're in an IDE like IntelliJ so this is only affecting the web UI.",[26,136027,1499],{"id":1498},[22,136029,136030],{},"I really like the fresh and clean design but I think the lack of a full list of dependencies is a bit of a downer. Personally it doesn't affect me as much because I am the proud owner of IntelliJ Ultimate. It does hurt beginners though like my students who might be using the community edition and rely on the web UI to kick start their project.",[22,136032,136033,136034,136037],{},"I would love to hear your thoughts, find me on ",[677,136035,86336],{"href":86334,"rel":136036},[681]," and let me know what you think! Until then...",[22,136039,36004,136040,82545],{},[36006,136041],{},{"title":57,"searchDepth":76,"depth":76,"links":136043},[136044,136047,136048],{"id":135918,"depth":76,"text":135919,"children":136045},[136046],{"id":135941,"depth":82,"text":135942},{"id":135997,"depth":76,"text":135998},{"id":1498,"depth":76,"text":1499},{"_id":136050,"path":136051,"title":136052,"description":136053,"meta":136054,"body":136059},"content/blog/2019/03/04/vue-event-arguments.md","/blog/2019/03/04/vue-event-arguments","Vue Event Handler Arguments","In this article you are going to learn how to pass arguments to your Vue event handlers as well as how get access to the original DOM event.",{"slug":136055,"date":136056,"published":13,"tags":136057,"author":17,"cover":136058,"excerpt":-1},"vue-event-arguments","2019-03-04 10:00:00",[105335,32793],"./vue-event-arguments.png",{"type":19,"value":136060,"toc":137900},[136061,136064,136068,136071,136085,136095,136203,136206,136401,136405,136412,136421,136537,136546,136549,136553,136556,136621,136624,136752,136756,136759,136890,136893,136899,137168,137171,137175,137182,137453,137456,137460,137463,137473,137740,137744,137751,137888,137890,137893,137897],[22,136062,136063],{},"A fundamental skill in learning JavaScript and Vue is the ability to listen for events and handle them. Events are notifications to code that something interesting has taken place like a user clicking on a button or pressing the enter key. Vue makes event handling incredibly easy and gives us some great features when it comes to event modifiers.",[26,136065,136067],{"id":136066},"event-handler-project","Event Handler Project",[22,136069,136070],{},"To get started you are going to setup a new Vue project by running the following command:",[52,136072,136074],{"className":1663,"code":136073,"language":1665,"meta":57,"style":57},"vue create event-handlers\n",[59,136075,136076],{"__ignoreMap":57},[62,136077,136078,136080,136082],{"class":64,"line":65},[62,136079,105335],{"class":122},[62,136081,23301],{"class":1675},[62,136083,136084],{"class":1675}," event-handlers\n",[22,136086,136087,136088,136090,136091,136094],{},"Feel free to accept the defaults while creating this project. The first thing you're going to do is open up ",[59,136089,105973],{}," and remove the default content in between the ",[59,136092,136093],{},"\u003Ctemplate>\u003C/template>"," tags. With a empty page you're going to add 2 buttons that we will use as the base for our demo.",[52,136096,136098],{"className":15773,"code":136097,"language":15775,"meta":57,"style":57},"\u003Ctemplate>\n \u003Cdiv id=\"app\">\n \u003Ca href=\"#\" id=\"increase\" class=\"btn\">Increase\u003C/a>\n \u003Ca href=\"#\" id=\"decrease\" class=\"btn\">Decrease\u003C/a>\n \u003C/div>\n\u003C/template>\n",[59,136099,136100,136108,136122,136155,136187,136195],{"__ignoreMap":57},[62,136101,136102,136104,136106],{"class":64,"line":65},[62,136103,760],{"class":72},[62,136105,105991],{"class":1780},[62,136107,1784],{"class":72},[62,136109,136110,136112,136114,136116,136118,136120],{"class":64,"line":76},[62,136111,33056],{"class":72},[62,136113,15944],{"class":1780},[62,136115,20831],{"class":122},[62,136117,146],{"class":72},[62,136119,49987],{"class":1675},[62,136121,1784],{"class":72},[62,136123,136124,136126,136128,136130,136132,136134,136136,136138,136141,136143,136145,136148,136151,136153],{"class":64,"line":82},[62,136125,1789],{"class":72},[62,136127,677],{"class":1780},[62,136129,16494],{"class":122},[62,136131,146],{"class":72},[62,136133,30687],{"class":1675},[62,136135,20831],{"class":122},[62,136137,146],{"class":72},[62,136139,136140],{"class":1675},"\"increase\"",[62,136142,119],{"class":122},[62,136144,146],{"class":72},[62,136146,136147],{"class":1675},"\"btn\"",[62,136149,136150],{"class":72},">Increase\u003C/",[62,136152,677],{"class":1780},[62,136154,1784],{"class":72},[62,136156,136157,136159,136161,136163,136165,136167,136169,136171,136174,136176,136178,136180,136183,136185],{"class":64,"line":89},[62,136158,1789],{"class":72},[62,136160,677],{"class":1780},[62,136162,16494],{"class":122},[62,136164,146],{"class":72},[62,136166,30687],{"class":1675},[62,136168,20831],{"class":122},[62,136170,146],{"class":72},[62,136172,136173],{"class":1675},"\"decrease\"",[62,136175,119],{"class":122},[62,136177,146],{"class":72},[62,136179,136147],{"class":1675},[62,136181,136182],{"class":72},">Decrease\u003C/",[62,136184,677],{"class":1780},[62,136186,1784],{"class":72},[62,136188,136189,136191,136193],{"class":64,"line":95},[62,136190,33187],{"class":72},[62,136192,15944],{"class":1780},[62,136194,1784],{"class":72},[62,136196,136197,136199,136201],{"class":64,"line":101},[62,136198,1818],{"class":72},[62,136200,105991],{"class":1780},[62,136202,1784],{"class":72},[22,136204,136205],{},"To give our buttons some style add the following css:",[52,136207,136209],{"className":15773,"code":136208,"language":15775,"meta":57,"style":57},"\u003Cstyle>\n body {\n margin: 10px;\n }\n a.btn {\n display: inline-block;\n margin: 10px;\n padding: 12px;\n font-size: 13px;\n font-weight: 700;\n background-color: rgb(63, 63, 219);\n color: white;\n text-decoration: none;\n text-transform: uppercase;\n border-radius: 4px;\n }\n\u003C/style>\n",[59,136210,136211,136219,136226,136239,136243,136253,136265,136277,136291,136305,136317,136342,136353,136364,136376,136389,136393],{"__ignoreMap":57},[62,136212,136213,136215,136217],{"class":64,"line":65},[62,136214,760],{"class":72},[62,136216,1527],{"class":1780},[62,136218,1784],{"class":72},[62,136220,136221,136224],{"class":64,"line":76},[62,136222,136223],{"class":1780}," body",[62,136225,126],{"class":72},[62,136227,136228,136231,136233,136235,136237],{"class":64,"line":82},[62,136229,136230],{"class":149}," margin",[62,136232,3696],{"class":72},[62,136234,26752],{"class":149},[62,136236,30191],{"class":68},[62,136238,153],{"class":72},[62,136240,136241],{"class":64,"line":89},[62,136242,3731],{"class":72},[62,136244,136245,136248,136251],{"class":64,"line":95},[62,136246,136247],{"class":1780}," a",[62,136249,136250],{"class":122},".btn",[62,136252,126],{"class":72},[62,136254,136255,136258,136260,136263],{"class":64,"line":101},[62,136256,136257],{"class":149}," display",[62,136259,3696],{"class":72},[62,136261,136262],{"class":149},"inline-block",[62,136264,153],{"class":72},[62,136266,136267,136269,136271,136273,136275],{"class":64,"line":107},[62,136268,136230],{"class":149},[62,136270,3696],{"class":72},[62,136272,26752],{"class":149},[62,136274,30191],{"class":68},[62,136276,153],{"class":72},[62,136278,136279,136282,136284,136287,136289],{"class":64,"line":113},[62,136280,136281],{"class":149}," padding",[62,136283,3696],{"class":72},[62,136285,136286],{"class":149},"12",[62,136288,30191],{"class":68},[62,136290,153],{"class":72},[62,136292,136293,136296,136298,136301,136303],{"class":64,"line":129},[62,136294,136295],{"class":149}," font-size",[62,136297,3696],{"class":72},[62,136299,136300],{"class":149},"13",[62,136302,30191],{"class":68},[62,136304,153],{"class":72},[62,136306,136307,136310,136312,136315],{"class":64,"line":134},[62,136308,136309],{"class":149}," font-weight",[62,136311,3696],{"class":72},[62,136313,136314],{"class":149},"700",[62,136316,153],{"class":72},[62,136318,136319,136322,136324,136326,136328,136331,136333,136335,136337,136340],{"class":64,"line":156},[62,136320,136321],{"class":149}," background-color",[62,136323,3696],{"class":72},[62,136325,119509],{"class":149},[62,136327,2109],{"class":72},[62,136329,136330],{"class":149},"63",[62,136332,976],{"class":72},[62,136334,136330],{"class":149},[62,136336,976],{"class":72},[62,136338,136339],{"class":149},"219",[62,136341,1133],{"class":72},[62,136343,136344,136347,136349,136351],{"class":64,"line":161},[62,136345,136346],{"class":149}," color",[62,136348,3696],{"class":72},[62,136350,113657],{"class":149},[62,136352,153],{"class":72},[62,136354,136355,136358,136360,136362],{"class":64,"line":167},[62,136356,136357],{"class":149}," text-decoration",[62,136359,3696],{"class":72},[62,136361,30351],{"class":149},[62,136363,153],{"class":72},[62,136365,136366,136369,136371,136374],{"class":64,"line":173},[62,136367,136368],{"class":149}," text-transform",[62,136370,3696],{"class":72},[62,136372,136373],{"class":149},"uppercase",[62,136375,153],{"class":72},[62,136377,136378,136381,136383,136385,136387],{"class":64,"line":179},[62,136379,136380],{"class":149}," border-radius",[62,136382,3696],{"class":72},[62,136384,26724],{"class":149},[62,136386,30191],{"class":68},[62,136388,153],{"class":72},[62,136390,136391],{"class":64,"line":185},[62,136392,3731],{"class":72},[62,136394,136395,136397,136399],{"class":64,"line":191},[62,136396,1818],{"class":72},[62,136398,1527],{"class":1780},[62,136400,1784],{"class":72},[26,136402,136404],{"id":136403},"listening-to-events","Listening to Events",[22,136406,136407,136408,136411],{},"With your buttons in place we can add an event listener to each. In vanilla JavaScript you would have to get a reference to the element and then add an event listener. In Vue you can use the ",[59,136409,136410],{},"v-on"," directive to listen to DOM events and run some JavaScript when they're triggered.",[22,136413,3521,136414,136416,136417,136420],{},[59,136415,136410],{}," directive is followed by a colon and then the DOM event that you want to listen for. In the case of the example you want to listen for the ",[59,136418,136419],{},"click"," event so your code would now look like this.",[52,136422,136424],{"className":15773,"code":136423,"language":15775,"meta":57,"style":57},"\u003Ctemplate>\n \u003Cdiv id=\"app\">\n \u003Ca href=\"#\" id=\"increase\" class=\"btn\" v-on:click=\"\">Increase\u003C/a>\n \u003Ca href=\"#\" id=\"decrease\" class=\"btn\" v-on:click=\"\">Decrease\u003C/a>\n \u003C/div>\n\u003C/template>\n",[59,136425,136426,136434,136448,136485,136521,136529],{"__ignoreMap":57},[62,136427,136428,136430,136432],{"class":64,"line":65},[62,136429,760],{"class":72},[62,136431,105991],{"class":1780},[62,136433,1784],{"class":72},[62,136435,136436,136438,136440,136442,136444,136446],{"class":64,"line":76},[62,136437,33056],{"class":72},[62,136439,15944],{"class":1780},[62,136441,20831],{"class":122},[62,136443,146],{"class":72},[62,136445,49987],{"class":1675},[62,136447,1784],{"class":72},[62,136449,136450,136452,136454,136456,136458,136460,136462,136464,136466,136468,136470,136472,136475,136477,136479,136481,136483],{"class":64,"line":82},[62,136451,1789],{"class":72},[62,136453,677],{"class":1780},[62,136455,16494],{"class":122},[62,136457,146],{"class":72},[62,136459,30687],{"class":1675},[62,136461,20831],{"class":122},[62,136463,146],{"class":72},[62,136465,136140],{"class":1675},[62,136467,119],{"class":122},[62,136469,146],{"class":72},[62,136471,136147],{"class":1675},[62,136473,136474],{"class":122}," v-on:click",[62,136476,146],{"class":72},[62,136478,25895],{"class":1675},[62,136480,136150],{"class":72},[62,136482,677],{"class":1780},[62,136484,1784],{"class":72},[62,136486,136487,136489,136491,136493,136495,136497,136499,136501,136503,136505,136507,136509,136511,136513,136515,136517,136519],{"class":64,"line":89},[62,136488,1789],{"class":72},[62,136490,677],{"class":1780},[62,136492,16494],{"class":122},[62,136494,146],{"class":72},[62,136496,30687],{"class":1675},[62,136498,20831],{"class":122},[62,136500,146],{"class":72},[62,136502,136173],{"class":1675},[62,136504,119],{"class":122},[62,136506,146],{"class":72},[62,136508,136147],{"class":1675},[62,136510,136474],{"class":122},[62,136512,146],{"class":72},[62,136514,25895],{"class":1675},[62,136516,136182],{"class":72},[62,136518,677],{"class":1780},[62,136520,1784],{"class":72},[62,136522,136523,136525,136527],{"class":64,"line":95},[62,136524,33187],{"class":72},[62,136526,15944],{"class":1780},[62,136528,1784],{"class":72},[62,136530,136531,136533,136535],{"class":64,"line":101},[62,136532,1818],{"class":72},[62,136534,105991],{"class":1780},[62,136536,1784],{"class":72},[29685,136538,136539],{},[22,136540,136541,136542,136545],{},"There is a shorthand for the v-on: directive which is to just use the @ symbol followed by the name of the event. In this case ",[59,136543,136544],{},"@click=\"\""," does the exact same thing as the longer format. For the rest of this demo I will be using the long version.",[22,136547,136548],{},"The code that you place inside of the parenthesis is the code that you will run when that event is fired.",[26,136550,136552],{"id":136551},"method-event-handlers","Method Event Handlers",[22,136554,136555],{},"The first thing you need to do is setup some initial data. In the script block create an instance variable called counter and set it to zero.",[52,136557,136559],{"className":32791,"code":136558,"language":32793,"meta":57,"style":57},"\u003Cscript>\nexport default {\n name: \"app\",\n data() {\n return {\n counter: 0\n };\n }\n};\n\u003C/script>\n",[59,136560,136561,136569,136573,136581,136587,136592,136601,136605,136609,136613],{"__ignoreMap":57},[62,136562,136563,136565,136567],{"class":64,"line":65},[62,136564,760],{"class":72},[62,136566,15846],{"class":1780},[62,136568,1784],{"class":72},[62,136570,136571],{"class":64,"line":76},[62,136572,122082],{"class":72},[62,136574,136575,136577,136579],{"class":64,"line":82},[62,136576,106052],{"class":72},[62,136578,49987],{"class":1675},[62,136580,3338],{"class":72},[62,136582,136583,136585],{"class":64,"line":89},[62,136584,106190],{"class":122},[62,136586,206],{"class":72},[62,136588,136589],{"class":64,"line":95},[62,136590,136591],{"class":72}," return {\n",[62,136593,136594,136597,136599],{"class":64,"line":101},[62,136595,136596],{"class":122}," counter",[62,136598,3696],{"class":72},[62,136600,72365],{"class":149},[62,136602,136603],{"class":64,"line":107},[62,136604,40087],{"class":72},[62,136606,136607],{"class":64,"line":113},[62,136608,3731],{"class":72},[62,136610,136611],{"class":64,"line":129},[62,136612,107354],{"class":72},[62,136614,136615,136617,136619],{"class":64,"line":134},[62,136616,1818],{"class":72},[62,136618,15846],{"class":1780},[62,136620,1784],{"class":72},[22,136622,136623],{},"In the template you are going to add some text and using data binding you are going to display the value of counter.",[52,136625,136627],{"className":15773,"code":136626,"language":15775,"meta":57,"style":57},"\u003Ctemplate>\n \u003Cdiv id=\"app\">\n \u003Ca href=\"#\" id=\"increase\" class=\"btn\" v-on:click=\"\">Increase\u003C/a>\n \u003Ca href=\"#\" id=\"decrease\" class=\"btn\" v-on:click=\"\">Decrease\u003C/a>\n \u003Cp>The button was clicked {{ counter }} times\u003C/p>\n \u003C/div>\n\u003C/template>\n",[59,136628,136629,136637,136651,136687,136723,136736,136744],{"__ignoreMap":57},[62,136630,136631,136633,136635],{"class":64,"line":65},[62,136632,760],{"class":72},[62,136634,105991],{"class":1780},[62,136636,1784],{"class":72},[62,136638,136639,136641,136643,136645,136647,136649],{"class":64,"line":76},[62,136640,33056],{"class":72},[62,136642,15944],{"class":1780},[62,136644,20831],{"class":122},[62,136646,146],{"class":72},[62,136648,49987],{"class":1675},[62,136650,1784],{"class":72},[62,136652,136653,136655,136657,136659,136661,136663,136665,136667,136669,136671,136673,136675,136677,136679,136681,136683,136685],{"class":64,"line":82},[62,136654,1789],{"class":72},[62,136656,677],{"class":1780},[62,136658,16494],{"class":122},[62,136660,146],{"class":72},[62,136662,30687],{"class":1675},[62,136664,20831],{"class":122},[62,136666,146],{"class":72},[62,136668,136140],{"class":1675},[62,136670,119],{"class":122},[62,136672,146],{"class":72},[62,136674,136147],{"class":1675},[62,136676,136474],{"class":122},[62,136678,146],{"class":72},[62,136680,25895],{"class":1675},[62,136682,136150],{"class":72},[62,136684,677],{"class":1780},[62,136686,1784],{"class":72},[62,136688,136689,136691,136693,136695,136697,136699,136701,136703,136705,136707,136709,136711,136713,136715,136717,136719,136721],{"class":64,"line":89},[62,136690,1789],{"class":72},[62,136692,677],{"class":1780},[62,136694,16494],{"class":122},[62,136696,146],{"class":72},[62,136698,30687],{"class":1675},[62,136700,20831],{"class":122},[62,136702,146],{"class":72},[62,136704,136173],{"class":1675},[62,136706,119],{"class":122},[62,136708,146],{"class":72},[62,136710,136147],{"class":1675},[62,136712,136474],{"class":122},[62,136714,146],{"class":72},[62,136716,25895],{"class":1675},[62,136718,136182],{"class":72},[62,136720,677],{"class":1780},[62,136722,1784],{"class":72},[62,136724,136725,136727,136729,136732,136734],{"class":64,"line":95},[62,136726,1789],{"class":72},[62,136728,22],{"class":1780},[62,136730,136731],{"class":72},">The button was clicked {{ counter }} times\u003C/",[62,136733,22],{"class":1780},[62,136735,1784],{"class":72},[62,136737,136738,136740,136742],{"class":64,"line":101},[62,136739,33187],{"class":72},[62,136741,15944],{"class":1780},[62,136743,1784],{"class":72},[62,136745,136746,136748,136750],{"class":64,"line":107},[62,136747,1818],{"class":72},[62,136749,105991],{"class":1780},[62,136751,1784],{"class":72},[636,136753,136755],{"id":136754},"inline-event-handlers","Inline Event Handlers",[22,136757,136758],{},"Now that you know how to declare an event handler you need to write some code that will execute when that event is triggered. You can write this code inside of the parenthesis (inline) or you can declare a function to handle it. For basic operations writing inline code will work and here all you want to do is increase or decrease the value of the variable counter.",[52,136760,136762],{"className":15773,"code":136761,"language":15775,"meta":57,"style":57},"\u003Cdiv id=\"app\">\n \u003Ca href=\"#\" id=\"increase\" class=\"btn\" v-on:click=\"counter += 1\">\n Increase\n \u003C/a>\n \u003Ca href=\"#\" id=\"decrease\" class=\"btn\" v-on:click=\"counter -= 1\">\n Decrease\n \u003C/a>\n \u003Cp>The button was clicked {{ counter }} times\u003C/p>\n\u003C/div>\n",[59,136763,136764,136778,136811,136816,136824,136857,136862,136870,136882],{"__ignoreMap":57},[62,136765,136766,136768,136770,136772,136774,136776],{"class":64,"line":65},[62,136767,760],{"class":72},[62,136769,15944],{"class":1780},[62,136771,20831],{"class":122},[62,136773,146],{"class":72},[62,136775,49987],{"class":1675},[62,136777,1784],{"class":72},[62,136779,136780,136782,136784,136786,136788,136790,136792,136794,136796,136798,136800,136802,136804,136806,136809],{"class":64,"line":76},[62,136781,33056],{"class":72},[62,136783,677],{"class":1780},[62,136785,16494],{"class":122},[62,136787,146],{"class":72},[62,136789,30687],{"class":1675},[62,136791,20831],{"class":122},[62,136793,146],{"class":72},[62,136795,136140],{"class":1675},[62,136797,119],{"class":122},[62,136799,146],{"class":72},[62,136801,136147],{"class":1675},[62,136803,136474],{"class":122},[62,136805,146],{"class":72},[62,136807,136808],{"class":1675},"\"counter += 1\"",[62,136810,1784],{"class":72},[62,136812,136813],{"class":64,"line":82},[62,136814,136815],{"class":72}," Increase\n",[62,136817,136818,136820,136822],{"class":64,"line":89},[62,136819,33187],{"class":72},[62,136821,677],{"class":1780},[62,136823,1784],{"class":72},[62,136825,136826,136828,136830,136832,136834,136836,136838,136840,136842,136844,136846,136848,136850,136852,136855],{"class":64,"line":95},[62,136827,33056],{"class":72},[62,136829,677],{"class":1780},[62,136831,16494],{"class":122},[62,136833,146],{"class":72},[62,136835,30687],{"class":1675},[62,136837,20831],{"class":122},[62,136839,146],{"class":72},[62,136841,136173],{"class":1675},[62,136843,119],{"class":122},[62,136845,146],{"class":72},[62,136847,136147],{"class":1675},[62,136849,136474],{"class":122},[62,136851,146],{"class":72},[62,136853,136854],{"class":1675},"\"counter -= 1\"",[62,136856,1784],{"class":72},[62,136858,136859],{"class":64,"line":101},[62,136860,136861],{"class":72}," Decrease\n",[62,136863,136864,136866,136868],{"class":64,"line":107},[62,136865,33187],{"class":72},[62,136867,677],{"class":1780},[62,136869,1784],{"class":72},[62,136871,136872,136874,136876,136878,136880],{"class":64,"line":113},[62,136873,33056],{"class":72},[62,136875,22],{"class":1780},[62,136877,136731],{"class":72},[62,136879,22],{"class":1780},[62,136881,1784],{"class":72},[62,136883,136884,136886,136888],{"class":64,"line":129},[62,136885,1818],{"class":72},[62,136887,15944],{"class":1780},[62,136889,1784],{"class":72},[636,136891,136552],{"id":136892},"method-event-handlers-1",[22,136894,136895,136896,136898],{},"You will quickly find out that logic for most of your event handlers is more complex and for those cases we can call a method. The ",[59,136897,136410],{}," directive will take the name of a method that will be called when the event is fired. In the followed code you are moving the logic from inline to a method.",[52,136900,136902],{"className":105981,"code":136901,"language":105335,"meta":57,"style":57},"\u003Ctemplate>\n \u003Cdiv id=\"app\">\n \u003Ca href=\"#\" id=\"increase\" class=\"btn\" v-on:click=\"increase\">Increase\u003C/a>\n \u003Ca href=\"#\" id=\"decrease\" class=\"btn\" v-on:click=\"decrease\">Decrease\u003C/a>\n \u003Cp>The button was clicked {{ counter }} times\u003C/p>\n \u003C/div>\n\u003C/template>\n\n\u003Cscript>\nexport default {\n name: \"app\",\n data() {\n return {\n counter: 0\n };\n },\n methods: {\n increase() {\n this.counter += 1;\n },\n decrease() {\n this.counter -= 1;\n }\n }\n};\n\u003C/script>\n",[59,136903,136904,136912,136926,136972,137017,137029,137037,137045,137049,137057,137065,137073,137079,137085,137092,137096,137100,137104,137111,137124,137128,137135,137148,137152,137156,137160],{"__ignoreMap":57},[62,136905,136906,136908,136910],{"class":64,"line":65},[62,136907,760],{"class":72},[62,136909,105991],{"class":1780},[62,136911,1784],{"class":72},[62,136913,136914,136916,136918,136920,136922,136924],{"class":64,"line":76},[62,136915,33056],{"class":72},[62,136917,15944],{"class":1780},[62,136919,20831],{"class":122},[62,136921,146],{"class":72},[62,136923,49987],{"class":1675},[62,136925,1784],{"class":72},[62,136927,136928,136930,136932,136934,136936,136938,136940,136942,136944,136946,136948,136950,136953,136955,136957,136959,136961,136964,136966,136968,136970],{"class":64,"line":82},[62,136929,1789],{"class":72},[62,136931,677],{"class":1780},[62,136933,16494],{"class":122},[62,136935,146],{"class":72},[62,136937,30687],{"class":1675},[62,136939,20831],{"class":122},[62,136941,146],{"class":72},[62,136943,136140],{"class":1675},[62,136945,119],{"class":122},[62,136947,146],{"class":72},[62,136949,136147],{"class":1675},[62,136951,136952],{"class":122}," v-on",[62,136954,1266],{"class":72},[62,136956,136419],{"class":122},[62,136958,146],{"class":72},[62,136960,4498],{"class":1675},[62,136962,136963],{"class":72},"increase",[62,136965,4498],{"class":1675},[62,136967,136150],{"class":72},[62,136969,677],{"class":1780},[62,136971,1784],{"class":72},[62,136973,136974,136976,136978,136980,136982,136984,136986,136988,136990,136992,136994,136996,136998,137000,137002,137004,137006,137009,137011,137013,137015],{"class":64,"line":89},[62,136975,1789],{"class":72},[62,136977,677],{"class":1780},[62,136979,16494],{"class":122},[62,136981,146],{"class":72},[62,136983,30687],{"class":1675},[62,136985,20831],{"class":122},[62,136987,146],{"class":72},[62,136989,136173],{"class":1675},[62,136991,119],{"class":122},[62,136993,146],{"class":72},[62,136995,136147],{"class":1675},[62,136997,136952],{"class":122},[62,136999,1266],{"class":72},[62,137001,136419],{"class":122},[62,137003,146],{"class":72},[62,137005,4498],{"class":1675},[62,137007,137008],{"class":72},"decrease",[62,137010,4498],{"class":1675},[62,137012,136182],{"class":72},[62,137014,677],{"class":1780},[62,137016,1784],{"class":72},[62,137018,137019,137021,137023,137025,137027],{"class":64,"line":95},[62,137020,1789],{"class":72},[62,137022,22],{"class":1780},[62,137024,136731],{"class":72},[62,137026,22],{"class":1780},[62,137028,1784],{"class":72},[62,137030,137031,137033,137035],{"class":64,"line":101},[62,137032,33187],{"class":72},[62,137034,15944],{"class":1780},[62,137036,1784],{"class":72},[62,137038,137039,137041,137043],{"class":64,"line":107},[62,137040,1818],{"class":72},[62,137042,105991],{"class":1780},[62,137044,1784],{"class":72},[62,137046,137047],{"class":64,"line":113},[62,137048,79],{"emptyLinePlaceholder":13},[62,137050,137051,137053,137055],{"class":64,"line":129},[62,137052,760],{"class":72},[62,137054,15846],{"class":1780},[62,137056,1784],{"class":72},[62,137058,137059,137061,137063],{"class":64,"line":134},[62,137060,14767],{"class":68},[62,137062,106045],{"class":68},[62,137064,126],{"class":72},[62,137066,137067,137069,137071],{"class":64,"line":156},[62,137068,106052],{"class":72},[62,137070,49987],{"class":1675},[62,137072,3338],{"class":72},[62,137074,137075,137077],{"class":64,"line":161},[62,137076,106190],{"class":122},[62,137078,206],{"class":72},[62,137080,137081,137083],{"class":64,"line":167},[62,137082,2599],{"class":68},[62,137084,126],{"class":72},[62,137086,137087,137090],{"class":64,"line":173},[62,137088,137089],{"class":72}," counter: ",[62,137091,72365],{"class":149},[62,137093,137094],{"class":64,"line":179},[62,137095,40087],{"class":72},[62,137097,137098],{"class":64,"line":185},[62,137099,32848],{"class":72},[62,137101,137102],{"class":64,"line":191},[62,137103,107160],{"class":72},[62,137105,137106,137109],{"class":64,"line":209},[62,137107,137108],{"class":122}," increase",[62,137110,206],{"class":72},[62,137112,137113,137115,137118,137120,137122],{"class":64,"line":220},[62,137114,122700],{"class":149},[62,137116,137117],{"class":72},".counter ",[62,137119,21578],{"class":68},[62,137121,22038],{"class":149},[62,137123,153],{"class":72},[62,137125,137126],{"class":64,"line":226},[62,137127,50124],{"class":72},[62,137129,137130,137133],{"class":64,"line":231},[62,137131,137132],{"class":122}," decrease",[62,137134,206],{"class":72},[62,137136,137137,137139,137141,137144,137146],{"class":64,"line":236},[62,137138,122700],{"class":149},[62,137140,137117],{"class":72},[62,137142,137143],{"class":68},"-=",[62,137145,22038],{"class":149},[62,137147,153],{"class":72},[62,137149,137150],{"class":64,"line":242},[62,137151,223],{"class":72},[62,137153,137154],{"class":64,"line":247},[62,137155,3731],{"class":72},[62,137157,137158],{"class":64,"line":252},[62,137159,107354],{"class":72},[62,137161,137162,137164,137166],{"class":64,"line":257},[62,137163,1818],{"class":72},[62,137165,15846],{"class":1780},[62,137167,1784],{"class":72},[22,137169,137170],{},"The program will function the same but now you have extracted it into a program where it could contain more complex operations if needed.",[26,137172,137174],{"id":137173},"event-handler-arguments","Event Handler Arguments",[22,137176,137177,137178,137181],{},"While this program works just fine it seems like we can simplify it. All the increase or decrease methods do is change the variable counter. You could write a single method to handle the logic for this method. Just like any other method in JavaScript you can pass arguments to it. Here you are calling ",[59,137179,137180],{},"updateCounter()"," for both buttons but passing the value of 1 for increase and -1 for decrease.",[52,137183,137185],{"className":105981,"code":137184,"language":105335,"meta":57,"style":57},"\u003Ctemplate>\n \u003Cdiv id=\"app\">\n \u003Ca href=\"#\" id=\"increase\" class=\"btn\" v-on:click=\"updateCounter(1)\"\n >Increase\u003C/a\n >\n \u003Ca href=\"#\" id=\"decrease\" class=\"btn\" v-on:click=\"updateCounter(-1)\"\n >Decrease\u003C/a\n >\n \u003Cp>The button was clicked {{ counter }} times\u003C/p>\n \u003C/div>\n\u003C/template>\n\n\u003Cscript>\nexport default {\n name: \"app\",\n data() {\n return {\n counter: 0\n };\n },\n methods: {\n updateCounter(operand) {\n this.counter += operand;\n }\n }\n};\n\u003C/script>\n",[59,137186,137187,137195,137209,137254,137262,137267,137313,137320,137324,137336,137344,137352,137356,137364,137372,137380,137386,137392,137398,137402,137406,137410,137422,137433,137437,137441,137445],{"__ignoreMap":57},[62,137188,137189,137191,137193],{"class":64,"line":65},[62,137190,760],{"class":72},[62,137192,105991],{"class":1780},[62,137194,1784],{"class":72},[62,137196,137197,137199,137201,137203,137205,137207],{"class":64,"line":76},[62,137198,33056],{"class":72},[62,137200,15944],{"class":1780},[62,137202,20831],{"class":122},[62,137204,146],{"class":72},[62,137206,49987],{"class":1675},[62,137208,1784],{"class":72},[62,137210,137211,137213,137215,137217,137219,137221,137223,137225,137227,137229,137231,137233,137235,137237,137239,137241,137243,137246,137248,137250,137252],{"class":64,"line":82},[62,137212,1789],{"class":72},[62,137214,677],{"class":1780},[62,137216,16494],{"class":122},[62,137218,146],{"class":72},[62,137220,30687],{"class":1675},[62,137222,20831],{"class":122},[62,137224,146],{"class":72},[62,137226,136140],{"class":1675},[62,137228,119],{"class":122},[62,137230,146],{"class":72},[62,137232,136147],{"class":1675},[62,137234,136952],{"class":122},[62,137236,1266],{"class":72},[62,137238,136419],{"class":122},[62,137240,146],{"class":72},[62,137242,4498],{"class":1675},[62,137244,137245],{"class":122},"updateCounter",[62,137247,2109],{"class":72},[62,137249,6689],{"class":149},[62,137251,2712],{"class":72},[62,137253,75083],{"class":1675},[62,137255,137256,137259],{"class":64,"line":89},[62,137257,137258],{"class":72}," >Increase\u003C/",[62,137260,137261],{"class":1780},"a\n",[62,137263,137264],{"class":64,"line":95},[62,137265,137266],{"class":72}," >\n",[62,137268,137269,137271,137273,137275,137277,137279,137281,137283,137285,137287,137289,137291,137293,137295,137297,137299,137301,137303,137305,137307,137309,137311],{"class":64,"line":101},[62,137270,1789],{"class":72},[62,137272,677],{"class":1780},[62,137274,16494],{"class":122},[62,137276,146],{"class":72},[62,137278,30687],{"class":1675},[62,137280,20831],{"class":122},[62,137282,146],{"class":72},[62,137284,136173],{"class":1675},[62,137286,119],{"class":122},[62,137288,146],{"class":72},[62,137290,136147],{"class":1675},[62,137292,136952],{"class":122},[62,137294,1266],{"class":72},[62,137296,136419],{"class":122},[62,137298,146],{"class":72},[62,137300,4498],{"class":1675},[62,137302,137245],{"class":122},[62,137304,2109],{"class":72},[62,137306,11635],{"class":68},[62,137308,6689],{"class":149},[62,137310,2712],{"class":72},[62,137312,75083],{"class":1675},[62,137314,137315,137318],{"class":64,"line":107},[62,137316,137317],{"class":72}," >Decrease\u003C/",[62,137319,137261],{"class":1780},[62,137321,137322],{"class":64,"line":113},[62,137323,137266],{"class":72},[62,137325,137326,137328,137330,137332,137334],{"class":64,"line":129},[62,137327,1789],{"class":72},[62,137329,22],{"class":1780},[62,137331,136731],{"class":72},[62,137333,22],{"class":1780},[62,137335,1784],{"class":72},[62,137337,137338,137340,137342],{"class":64,"line":134},[62,137339,33187],{"class":72},[62,137341,15944],{"class":1780},[62,137343,1784],{"class":72},[62,137345,137346,137348,137350],{"class":64,"line":156},[62,137347,1818],{"class":72},[62,137349,105991],{"class":1780},[62,137351,1784],{"class":72},[62,137353,137354],{"class":64,"line":161},[62,137355,79],{"emptyLinePlaceholder":13},[62,137357,137358,137360,137362],{"class":64,"line":167},[62,137359,760],{"class":72},[62,137361,15846],{"class":1780},[62,137363,1784],{"class":72},[62,137365,137366,137368,137370],{"class":64,"line":173},[62,137367,14767],{"class":68},[62,137369,106045],{"class":68},[62,137371,126],{"class":72},[62,137373,137374,137376,137378],{"class":64,"line":179},[62,137375,106052],{"class":72},[62,137377,49987],{"class":1675},[62,137379,3338],{"class":72},[62,137381,137382,137384],{"class":64,"line":185},[62,137383,106190],{"class":122},[62,137385,206],{"class":72},[62,137387,137388,137390],{"class":64,"line":191},[62,137389,2599],{"class":68},[62,137391,126],{"class":72},[62,137393,137394,137396],{"class":64,"line":209},[62,137395,137089],{"class":72},[62,137397,72365],{"class":149},[62,137399,137400],{"class":64,"line":220},[62,137401,40087],{"class":72},[62,137403,137404],{"class":64,"line":226},[62,137405,32848],{"class":72},[62,137407,137408],{"class":64,"line":231},[62,137409,107160],{"class":72},[62,137411,137412,137415,137417,137420],{"class":64,"line":236},[62,137413,137414],{"class":122}," updateCounter",[62,137416,2109],{"class":72},[62,137418,137419],{"class":889},"operand",[62,137421,768],{"class":72},[62,137423,137424,137426,137428,137430],{"class":64,"line":242},[62,137425,122700],{"class":149},[62,137427,137117],{"class":72},[62,137429,21578],{"class":68},[62,137431,137432],{"class":72}," operand;\n",[62,137434,137435],{"class":64,"line":247},[62,137436,223],{"class":72},[62,137438,137439],{"class":64,"line":252},[62,137440,3731],{"class":72},[62,137442,137443],{"class":64,"line":257},[62,137444,107354],{"class":72},[62,137446,137447,137449,137451],{"class":64,"line":271},[62,137448,1818],{"class":72},[62,137450,15846],{"class":1780},[62,137452,1784],{"class":72},[22,137454,137455],{},"Just like any other method you can pass whatever arguments are needed to this method.",[636,137457,137459],{"id":137458},"implicit-event-argument","Implicit Event Argument",[22,137461,137462],{},"Looking at this method I can't help but think we are be a little explicit here. You know that the increase button will increase the counter by 1 and the decrease button will decrease the counter by 1. If that is the case why do you need to pass that variable to the method.",[22,137464,137465,137466,137468,137469,137472],{},"In vanilla JavaScript you have access to the original DOM event. From there you can determine where the event was originated from. In Vue the original DOM event is implicitly passed if there are no arguments to the method. This means that in our ",[59,137467,137180],{}," method you can declare an argument called event (or whatever you want for that matter) and the event will be passed in. With the original event we can get the id of the button using ",[59,137470,137471],{},"event.target.id"," and determine if we are increasing or decreasing the value of counter.",[52,137474,137476],{"className":105981,"code":137475,"language":105335,"meta":57,"style":57},"\u003Ctemplate>\n \u003Cdiv id=\"app\">\n \u003Ca href=\"#\" id=\"increase\" class=\"btn\" v-on:click=\"updateCounter\"\n >Increase\u003C/a\n >\n \u003Ca href=\"#\" id=\"decrease\" class=\"btn\" v-on:click=\"updateCounter\"\n >Decrease\u003C/a\n >\n \u003Cp>The button was clicked {{ counter }} times\u003C/p>\n \u003C/div>\n\u003C/template>\n\n\u003Cscript>\nexport default {\n name: \"app\",\n data() {\n return {\n counter: 0\n };\n },\n methods: {\n updateCounter(event) {\n this.counter += event.target.id === \"increase\" ? 1 : -1;\n }\n }\n};\n\u003C/script>\n",[59,137477,137478,137486,137500,137538,137544,137548,137586,137592,137596,137608,137616,137624,137628,137636,137644,137652,137658,137664,137670,137674,137678,137682,137692,137720,137724,137728,137732],{"__ignoreMap":57},[62,137479,137480,137482,137484],{"class":64,"line":65},[62,137481,760],{"class":72},[62,137483,105991],{"class":1780},[62,137485,1784],{"class":72},[62,137487,137488,137490,137492,137494,137496,137498],{"class":64,"line":76},[62,137489,33056],{"class":72},[62,137491,15944],{"class":1780},[62,137493,20831],{"class":122},[62,137495,146],{"class":72},[62,137497,49987],{"class":1675},[62,137499,1784],{"class":72},[62,137501,137502,137504,137506,137508,137510,137512,137514,137516,137518,137520,137522,137524,137526,137528,137530,137532,137534,137536],{"class":64,"line":82},[62,137503,1789],{"class":72},[62,137505,677],{"class":1780},[62,137507,16494],{"class":122},[62,137509,146],{"class":72},[62,137511,30687],{"class":1675},[62,137513,20831],{"class":122},[62,137515,146],{"class":72},[62,137517,136140],{"class":1675},[62,137519,119],{"class":122},[62,137521,146],{"class":72},[62,137523,136147],{"class":1675},[62,137525,136952],{"class":122},[62,137527,1266],{"class":72},[62,137529,136419],{"class":122},[62,137531,146],{"class":72},[62,137533,4498],{"class":1675},[62,137535,137245],{"class":72},[62,137537,75083],{"class":1675},[62,137539,137540,137542],{"class":64,"line":89},[62,137541,137258],{"class":72},[62,137543,137261],{"class":1780},[62,137545,137546],{"class":64,"line":95},[62,137547,137266],{"class":72},[62,137549,137550,137552,137554,137556,137558,137560,137562,137564,137566,137568,137570,137572,137574,137576,137578,137580,137582,137584],{"class":64,"line":101},[62,137551,1789],{"class":72},[62,137553,677],{"class":1780},[62,137555,16494],{"class":122},[62,137557,146],{"class":72},[62,137559,30687],{"class":1675},[62,137561,20831],{"class":122},[62,137563,146],{"class":72},[62,137565,136173],{"class":1675},[62,137567,119],{"class":122},[62,137569,146],{"class":72},[62,137571,136147],{"class":1675},[62,137573,136952],{"class":122},[62,137575,1266],{"class":72},[62,137577,136419],{"class":122},[62,137579,146],{"class":72},[62,137581,4498],{"class":1675},[62,137583,137245],{"class":72},[62,137585,75083],{"class":1675},[62,137587,137588,137590],{"class":64,"line":107},[62,137589,137317],{"class":72},[62,137591,137261],{"class":1780},[62,137593,137594],{"class":64,"line":113},[62,137595,137266],{"class":72},[62,137597,137598,137600,137602,137604,137606],{"class":64,"line":129},[62,137599,1789],{"class":72},[62,137601,22],{"class":1780},[62,137603,136731],{"class":72},[62,137605,22],{"class":1780},[62,137607,1784],{"class":72},[62,137609,137610,137612,137614],{"class":64,"line":134},[62,137611,33187],{"class":72},[62,137613,15944],{"class":1780},[62,137615,1784],{"class":72},[62,137617,137618,137620,137622],{"class":64,"line":156},[62,137619,1818],{"class":72},[62,137621,105991],{"class":1780},[62,137623,1784],{"class":72},[62,137625,137626],{"class":64,"line":161},[62,137627,79],{"emptyLinePlaceholder":13},[62,137629,137630,137632,137634],{"class":64,"line":167},[62,137631,760],{"class":72},[62,137633,15846],{"class":1780},[62,137635,1784],{"class":72},[62,137637,137638,137640,137642],{"class":64,"line":173},[62,137639,14767],{"class":68},[62,137641,106045],{"class":68},[62,137643,126],{"class":72},[62,137645,137646,137648,137650],{"class":64,"line":179},[62,137647,106052],{"class":72},[62,137649,49987],{"class":1675},[62,137651,3338],{"class":72},[62,137653,137654,137656],{"class":64,"line":185},[62,137655,106190],{"class":122},[62,137657,206],{"class":72},[62,137659,137660,137662],{"class":64,"line":191},[62,137661,2599],{"class":68},[62,137663,126],{"class":72},[62,137665,137666,137668],{"class":64,"line":209},[62,137667,137089],{"class":72},[62,137669,72365],{"class":149},[62,137671,137672],{"class":64,"line":220},[62,137673,40087],{"class":72},[62,137675,137676],{"class":64,"line":226},[62,137677,32848],{"class":72},[62,137679,137680],{"class":64,"line":231},[62,137681,107160],{"class":72},[62,137683,137684,137686,137688,137690],{"class":64,"line":236},[62,137685,137414],{"class":122},[62,137687,2109],{"class":72},[62,137689,21051],{"class":889},[62,137691,768],{"class":72},[62,137693,137694,137696,137698,137700,137703,137705,137708,137710,137712,137714,137716,137718],{"class":64,"line":242},[62,137695,122700],{"class":149},[62,137697,137117],{"class":72},[62,137699,21578],{"class":68},[62,137701,137702],{"class":72}," event.target.id ",[62,137704,21072],{"class":68},[62,137706,137707],{"class":1675}," \"increase\"",[62,137709,64807],{"class":68},[62,137711,22038],{"class":149},[62,137713,57090],{"class":68},[62,137715,128533],{"class":68},[62,137717,6689],{"class":149},[62,137719,153],{"class":72},[62,137721,137722],{"class":64,"line":247},[62,137723,223],{"class":72},[62,137725,137726],{"class":64,"line":252},[62,137727,3731],{"class":72},[62,137729,137730],{"class":64,"line":257},[62,137731,107354],{"class":72},[62,137733,137734,137736,137738],{"class":64,"line":271},[62,137735,1818],{"class":72},[62,137737,15846],{"class":1780},[62,137739,1784],{"class":72},[636,137741,137743],{"id":137742},"explicit-event-argument","Explicit Event Argument",[22,137745,137746,137747,137750],{},"What happens when you have arguments that you need to pass to your method but you also need access to the original DOM event? In that case there is a special variable ",[59,137748,137749],{},"$event"," that you can pass.",[52,137752,137754],{"className":15773,"code":137753,"language":15775,"meta":57,"style":57},"\u003Ctemplate>\n \u003Cdiv id=\"app\">\n \u003Ca href=\"#\" id=\"increase\" class=\"btn\" v-on:click=\"updateCounter(1,$event)\"\n >Increase\u003C/a\n >\n \u003Ca href=\"#\" id=\"decrease\" class=\"btn\" v-on:click=\"updateCounter(-1,$event)\"\n >Decrease\u003C/a\n >\n \u003Cp>The button was clicked {{ counter }} times\u003C/p>\n \u003C/div>\n\u003C/template>\n",[59,137755,137756,137764,137778,137809,137815,137819,137850,137856,137860,137872,137880],{"__ignoreMap":57},[62,137757,137758,137760,137762],{"class":64,"line":65},[62,137759,760],{"class":72},[62,137761,105991],{"class":1780},[62,137763,1784],{"class":72},[62,137765,137766,137768,137770,137772,137774,137776],{"class":64,"line":76},[62,137767,33056],{"class":72},[62,137769,15944],{"class":1780},[62,137771,20831],{"class":122},[62,137773,146],{"class":72},[62,137775,49987],{"class":1675},[62,137777,1784],{"class":72},[62,137779,137780,137782,137784,137786,137788,137790,137792,137794,137796,137798,137800,137802,137804,137806],{"class":64,"line":82},[62,137781,1789],{"class":72},[62,137783,677],{"class":1780},[62,137785,16494],{"class":122},[62,137787,146],{"class":72},[62,137789,30687],{"class":1675},[62,137791,20831],{"class":122},[62,137793,146],{"class":72},[62,137795,136140],{"class":1675},[62,137797,119],{"class":122},[62,137799,146],{"class":72},[62,137801,136147],{"class":1675},[62,137803,136474],{"class":122},[62,137805,146],{"class":72},[62,137807,137808],{"class":1675},"\"updateCounter(1,$event)\"\n",[62,137810,137811,137813],{"class":64,"line":89},[62,137812,137258],{"class":72},[62,137814,137261],{"class":1780},[62,137816,137817],{"class":64,"line":95},[62,137818,137266],{"class":72},[62,137820,137821,137823,137825,137827,137829,137831,137833,137835,137837,137839,137841,137843,137845,137847],{"class":64,"line":101},[62,137822,1789],{"class":72},[62,137824,677],{"class":1780},[62,137826,16494],{"class":122},[62,137828,146],{"class":72},[62,137830,30687],{"class":1675},[62,137832,20831],{"class":122},[62,137834,146],{"class":72},[62,137836,136173],{"class":1675},[62,137838,119],{"class":122},[62,137840,146],{"class":72},[62,137842,136147],{"class":1675},[62,137844,136474],{"class":122},[62,137846,146],{"class":72},[62,137848,137849],{"class":1675},"\"updateCounter(-1,$event)\"\n",[62,137851,137852,137854],{"class":64,"line":107},[62,137853,137317],{"class":72},[62,137855,137261],{"class":1780},[62,137857,137858],{"class":64,"line":113},[62,137859,137266],{"class":72},[62,137861,137862,137864,137866,137868,137870],{"class":64,"line":129},[62,137863,1789],{"class":72},[62,137865,22],{"class":1780},[62,137867,136731],{"class":72},[62,137869,22],{"class":1780},[62,137871,1784],{"class":72},[62,137873,137874,137876,137878],{"class":64,"line":134},[62,137875,33187],{"class":72},[62,137877,15944],{"class":1780},[62,137879,1784],{"class":72},[62,137881,137882,137884,137886],{"class":64,"line":156},[62,137883,1818],{"class":72},[62,137885,105991],{"class":1780},[62,137887,1784],{"class":72},[26,137889,1499],{"id":1498},[22,137891,137892],{},"I realize that most or all of this was pretty basic but I have found that a lot of people didn't know about the implicit event argument. This comes in really handy in many situations so its good to know how to access the original DOM event. If you have any questions about this article and or anything related to Vue please let me know and I will try and answer them, until then...",[22,137894,36004,137895,82545],{},[36006,137896],{},[1527,137898,137899],{},"html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .suaqH, html code.shiki .suaqH{--shiki-default:#22863A;--shiki-dark:#85E89D;--shiki-sepia:#22863A}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .spoOn, html code.shiki .spoOn{--shiki-default:#E36209;--shiki-dark:#FFAB70;--shiki-sepia:#E36209}",{"title":57,"searchDepth":76,"depth":76,"links":137901},[137902,137903,137904,137908,137912],{"id":136066,"depth":76,"text":136067},{"id":136403,"depth":76,"text":136404},{"id":136551,"depth":76,"text":136552,"children":137905},[137906,137907],{"id":136754,"depth":82,"text":136755},{"id":136892,"depth":82,"text":136552},{"id":137173,"depth":76,"text":137174,"children":137909},[137910,137911],{"id":137458,"depth":82,"text":137459},{"id":137742,"depth":82,"text":137743},{"id":1498,"depth":76,"text":1499},{"_id":137914,"path":137915,"title":137916,"description":137917,"meta":137918,"body":137923},"content/blog/2019/02/21/node-recursive-directories.md","/blog/2019/02/21/node-recursive-directories","Creating multiple directories in node","A quick tutorial on how to recursively create directories in node.",{"slug":137919,"date":137920,"published":13,"tags":137921,"author":17,"cover":137922,"excerpt":-1},"node-recursive-directories","2019-02-21 08:00:00",[32634,32793],"node-multiple-directories.png",{"type":19,"value":137924,"toc":138927},[137925,137928,137931,137948,137957,137961,137968,137979,137985,137988,138031,138039,138049,138057,138212,138215,138221,138224,138227,138383,138387,138401,138429,138437,138457,138467,138481,138484,138661,138664,138670,138688,138693,138696,138915,138917,138920,138924],[22,137926,137927],{},"This is going to be a quick tutorial but I think it's one that I want to share. I am in the middle of migrating close to 1,000 blog posts from WordPress to Gridsome, a static site generator.",[22,137929,137930],{},"As any good (lazy) developer would, I did some searching around for a migration script. With Gridsome being a fairly new project I knew that my chances of finding a script would be pretty slim. After spending about 10 minutes looking around I found that my assumptions were true.",[22,137932,137933,137934,976,137939,4201,137944,137947],{},"Not to worry because I wasn't really doing anything that was Gridsome specific. What I wanted to do was convert a bunch of WordPress posts into Markdown. With all of the great blogs written on top of static site generators like ",[677,137935,137938],{"href":137936,"rel":137937},"https://gohugo.io/",[681],"Hugo",[677,137940,137943],{"href":137941,"rel":137942},"https://jekyllrb.com/",[681],"Jekyll",[677,137945,130879],{"href":130877,"rel":137946},[681]," I was sure that I could find something close to what I was looking for.",[22,137949,137950,137951,137956],{},"Sure enough, I came across a this awesome ",[677,137952,137955],{"href":137953,"rel":137954},"https://github.com/konsalex/gatsby-wordpress-migrate",[681],"Gatsby to WordPress"," migration script by Costa Alexoglou. This script will take your WordPress posts (exported out as XML) and convert them to Markdown. This was a good start for me but one of the things I needed to do was put the markdown files into a specific folder format.",[26,137958,137960],{"id":137959},"directory-format","Directory Format",[22,137962,137963,137964,137967],{},"I needed to stick to the url format that my existing posts were in which was ",[646,137965,137966],{},"/blog/:year/:month/:day/:slug",". I also needed to make sure that parts of the date were formatted in the following format:",[915,137969,137970,137973,137976],{},[37,137971,137972],{},"Year: 4 digits",[37,137974,137975],{},"Month: 2 digits",[37,137977,137978],{},"Day: 2 digits",[22,137980,137981],{},[653,137982],{"alt":137983,"src":137984},"Calendar","./undraw_calendar_dutt.png",[22,137986,137987],{},"Before I could even begin to worry about creating a new directory (or directories) I needed to get the 3 parts I needed from the date for that blog post in the format I needed them. The first thing I did was to create a new date called createdOn from the post date.",[52,137989,137991],{"className":32791,"code":137990,"language":32793,"meta":57,"style":57},"const header = {\n date: \"2019-02-21 08:00:00\"\n};\nconst createdOn = new Date(header.date);\n",[59,137992,137993,138004,138012,138016],{"__ignoreMap":57},[62,137994,137995,137997,138000,138002],{"class":64,"line":65},[62,137996,110541],{"class":68},[62,137998,137999],{"class":149}," header",[62,138001,2556],{"class":68},[62,138003,126],{"class":72},[62,138005,138006,138009],{"class":64,"line":76},[62,138007,138008],{"class":72}," date: ",[62,138010,138011],{"class":1675},"\"2019-02-21 08:00:00\"\n",[62,138013,138014],{"class":64,"line":82},[62,138015,107354],{"class":72},[62,138017,138018,138020,138022,138024,138026,138028],{"class":64,"line":89},[62,138019,110541],{"class":68},[62,138021,131573],{"class":149},[62,138023,2556],{"class":68},[62,138025,466],{"class":68},[62,138027,61498],{"class":122},[62,138029,138030],{"class":72},"(header.date);\n",[22,138032,138033,138034,138038],{},"Now that I had a ",[677,138035,46398],{"href":138036,"rel":138037},"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date",[681]," object I could use the API to get the different parts that I wanted. The year was the easiest while I had to do some work to get the month and day in the format I wanted them in.",[22,138040,138041,138042,51560,138045,138048],{},"The month and day were not as easy. First off both ",[59,138043,138044],{},"getMonth()",[59,138046,138047],{},"getDate()"," of them return to you 1,2,3... and I needed them in 2 digit format 01,02,03. For both of those I used a ternary operator to pad it with a 0 if the number was less than 10.",[22,138050,138051,138052,138054,138055,2755],{},"The other gotcha here is that ",[59,138053,138044],{}," returns the month as a zero based value so January is 0. Knowing that I will need to add 1 to each value returned from ",[59,138056,138044],{},[52,138058,138060],{"className":32791,"code":138059,"language":32793,"meta":57,"style":57},"const header = {\n date: \"2019-02-21 08:00:00\"\n};\nconst createdOn = new Date(header.date);\nconst year = createdOn.getFullYear();\nconst month = `${\n createdOn.getMonth() + 1 \u003C 10 ? \"0\" : \"\"\n}${createdOn.getMonth() + 1}`;\nconst day = `${createdOn.getDate() \u003C 10 ? \"0\" : \"\"}${createdOn.getDate()}`;\n",[59,138061,138062,138072,138078,138082,138096,138110,138121,138149,138170],{"__ignoreMap":57},[62,138063,138064,138066,138068,138070],{"class":64,"line":65},[62,138065,110541],{"class":68},[62,138067,137999],{"class":149},[62,138069,2556],{"class":68},[62,138071,126],{"class":72},[62,138073,138074,138076],{"class":64,"line":76},[62,138075,138008],{"class":72},[62,138077,138011],{"class":1675},[62,138079,138080],{"class":64,"line":82},[62,138081,107354],{"class":72},[62,138083,138084,138086,138088,138090,138092,138094],{"class":64,"line":89},[62,138085,110541],{"class":68},[62,138087,131573],{"class":149},[62,138089,2556],{"class":68},[62,138091,466],{"class":68},[62,138093,61498],{"class":122},[62,138095,138030],{"class":72},[62,138097,138098,138100,138102,138104,138106,138108],{"class":64,"line":95},[62,138099,110541],{"class":68},[62,138101,131588],{"class":149},[62,138103,2556],{"class":68},[62,138105,131593],{"class":72},[62,138107,131596],{"class":122},[62,138109,822],{"class":72},[62,138111,138112,138114,138116,138118],{"class":64,"line":101},[62,138113,110541],{"class":68},[62,138115,131605],{"class":149},[62,138117,2556],{"class":68},[62,138119,138120],{"class":1675}," `${\n",[62,138122,138123,138126,138128,138130,138132,138134,138136,138138,138140,138142,138144,138146],{"class":64,"line":107},[62,138124,138125],{"class":72}," createdOn",[62,138127,2755],{"class":1675},[62,138129,131617],{"class":122},[62,138131,5398],{"class":1675},[62,138133,1148],{"class":68},[62,138135,22038],{"class":149},[62,138137,5607],{"class":68},[62,138139,30327],{"class":149},[62,138141,64807],{"class":68},[62,138143,131632],{"class":1675},[62,138145,57090],{"class":68},[62,138147,138148],{"class":1675}," \"\"\n",[62,138150,138151,138154,138156,138158,138160,138162,138164,138166,138168],{"class":64,"line":113},[62,138152,138153],{"class":1675},"}${",[62,138155,131612],{"class":72},[62,138157,2755],{"class":1675},[62,138159,131617],{"class":122},[62,138161,5398],{"class":1675},[62,138163,1148],{"class":68},[62,138165,22038],{"class":149},[62,138167,21727],{"class":1675},[62,138169,153],{"class":72},[62,138171,138172,138174,138176,138178,138180,138182,138184,138186,138188,138190,138192,138194,138196,138198,138200,138202,138204,138206,138208,138210],{"class":64,"line":129},[62,138173,110541],{"class":68},[62,138175,131660],{"class":149},[62,138177,2556],{"class":68},[62,138179,121072],{"class":1675},[62,138181,131612],{"class":72},[62,138183,2755],{"class":1675},[62,138185,42183],{"class":122},[62,138187,5398],{"class":1675},[62,138189,760],{"class":68},[62,138191,30327],{"class":149},[62,138193,64807],{"class":68},[62,138195,131632],{"class":1675},[62,138197,57090],{"class":68},[62,138199,131637],{"class":1675},[62,138201,131612],{"class":72},[62,138203,2755],{"class":1675},[62,138205,42183],{"class":122},[62,138207,131693],{"class":1675},[62,138209,21727],{"class":1675},[62,138211,153],{"class":72},[22,138213,138214],{},"While this wasn't super hard it isn't the easiest or most elegant solution. Dates just always seem to be a pain point in every language and they all have their quirks. I thought I would take the Twitter and see if anyone could help me out.",[22,138216,138217],{},[677,138218,138219],{"href":138219,"rel":138220},"https://twitter.com/therealdanvega/status/1098667133112934401",[681],[22,138222,138223],{},"I didn't get any real good answers so for now that is what I am going to stick with. It was also pointed out to me that IE11 won't support creating a Date from a string. You should look into browser compatibility when you have to worry about it but in my case this is just a local script that I am running so I'm not worried.",[22,138225,138226],{},"So if you're following along so far you should have something that looks like this.",[52,138228,138230],{"className":32791,"code":138229,"language":32793,"meta":57,"style":57},"const header = {\n date: \"2019-02-20 08:00:00\"\n};\n\nconst createdOn = new Date(header.date);\nconst year = createdOn.getFullYear();\nconst month = `${\n createdOn.getMonth() + 1 \u003C 10 ? \"0\" : \"\"\n}${createdOn.getMonth() + 1}`;\nconst day = `${createdOn.getDate() \u003C 10 ? \"0\" : \"\"}${createdOn.getDate()}`;\n",[59,138231,138232,138242,138249,138253,138257,138271,138285,138295,138321,138341],{"__ignoreMap":57},[62,138233,138234,138236,138238,138240],{"class":64,"line":65},[62,138235,110541],{"class":68},[62,138237,137999],{"class":149},[62,138239,2556],{"class":68},[62,138241,126],{"class":72},[62,138243,138244,138246],{"class":64,"line":76},[62,138245,138008],{"class":72},[62,138247,138248],{"class":1675},"\"2019-02-20 08:00:00\"\n",[62,138250,138251],{"class":64,"line":82},[62,138252,107354],{"class":72},[62,138254,138255],{"class":64,"line":89},[62,138256,79],{"emptyLinePlaceholder":13},[62,138258,138259,138261,138263,138265,138267,138269],{"class":64,"line":95},[62,138260,110541],{"class":68},[62,138262,131573],{"class":149},[62,138264,2556],{"class":68},[62,138266,466],{"class":68},[62,138268,61498],{"class":122},[62,138270,138030],{"class":72},[62,138272,138273,138275,138277,138279,138281,138283],{"class":64,"line":101},[62,138274,110541],{"class":68},[62,138276,131588],{"class":149},[62,138278,2556],{"class":68},[62,138280,131593],{"class":72},[62,138282,131596],{"class":122},[62,138284,822],{"class":72},[62,138286,138287,138289,138291,138293],{"class":64,"line":107},[62,138288,110541],{"class":68},[62,138290,131605],{"class":149},[62,138292,2556],{"class":68},[62,138294,138120],{"class":1675},[62,138296,138297,138299,138301,138303,138305,138307,138309,138311,138313,138315,138317,138319],{"class":64,"line":113},[62,138298,138125],{"class":72},[62,138300,2755],{"class":1675},[62,138302,131617],{"class":122},[62,138304,5398],{"class":1675},[62,138306,1148],{"class":68},[62,138308,22038],{"class":149},[62,138310,5607],{"class":68},[62,138312,30327],{"class":149},[62,138314,64807],{"class":68},[62,138316,131632],{"class":1675},[62,138318,57090],{"class":68},[62,138320,138148],{"class":1675},[62,138322,138323,138325,138327,138329,138331,138333,138335,138337,138339],{"class":64,"line":129},[62,138324,138153],{"class":1675},[62,138326,131612],{"class":72},[62,138328,2755],{"class":1675},[62,138330,131617],{"class":122},[62,138332,5398],{"class":1675},[62,138334,1148],{"class":68},[62,138336,22038],{"class":149},[62,138338,21727],{"class":1675},[62,138340,153],{"class":72},[62,138342,138343,138345,138347,138349,138351,138353,138355,138357,138359,138361,138363,138365,138367,138369,138371,138373,138375,138377,138379,138381],{"class":64,"line":134},[62,138344,110541],{"class":68},[62,138346,131660],{"class":149},[62,138348,2556],{"class":68},[62,138350,121072],{"class":1675},[62,138352,131612],{"class":72},[62,138354,2755],{"class":1675},[62,138356,42183],{"class":122},[62,138358,5398],{"class":1675},[62,138360,760],{"class":68},[62,138362,30327],{"class":149},[62,138364,64807],{"class":68},[62,138366,131632],{"class":1675},[62,138368,57090],{"class":68},[62,138370,131637],{"class":1675},[62,138372,131612],{"class":72},[62,138374,2755],{"class":1675},[62,138376,42183],{"class":122},[62,138378,131693],{"class":1675},[62,138380,21727],{"class":1675},[62,138382,153],{"class":72},[26,138384,138386],{"id":138385},"using-node-to-write-directories","Using node to write directories",[22,138388,138389,138390,138393,138394,138397,138398,138400],{},"Now that we have the parts we need for the directory lets create the full blog post directory. I am storing everything in a relative folder to this script in the form of ",[59,138391,138392],{},"/blog/:year/:month/:day"," and then the name of the file would be ",[59,138395,138396],{},":slug.md",". So I am going to start by creating a variable called ",[59,138399,131703],{}," and I will create the path using a template literal.",[52,138402,138403],{"className":32791,"code":131707,"language":32793,"meta":57,"style":57},[59,138404,138405],{"__ignoreMap":57},[62,138406,138407,138409,138411,138413,138415,138417,138419,138421,138423,138425,138427],{"class":64,"line":65},[62,138408,110541],{"class":68},[62,138410,131716],{"class":149},[62,138412,2556],{"class":68},[62,138414,131721],{"class":1675},[62,138416,8604],{"class":72},[62,138418,131726],{"class":1675},[62,138420,131729],{"class":72},[62,138422,131726],{"class":1675},[62,138424,131734],{"class":72},[62,138426,21727],{"class":1675},[62,138428,153],{"class":72},[22,138430,138431,138432,138436],{},"Next we are going to tap into ",[677,138433,138435],{"href":131791,"rel":138434},[681],"Node's Files System API"," to actually create the directory. To use the file system module",[52,138438,138439],{"className":32791,"code":131796,"language":32793,"meta":57,"style":57},[59,138440,138441],{"__ignoreMap":57},[62,138442,138443,138445,138447,138449,138451,138453,138455],{"class":64,"line":65},[62,138444,110541],{"class":68},[62,138446,120730],{"class":149},[62,138448,2556],{"class":68},[62,138450,120735],{"class":122},[62,138452,2109],{"class":72},[62,138454,131813],{"class":1675},[62,138456,1133],{"class":72},[22,138458,138459,138460,138466],{},"There is a method in the file system API to make a directory. The default method is asynchronous but for our case we will actually want this to be synchronous so we will use ",[677,138461,138463],{"href":131858,"rel":138462},[681],[59,138464,138465],{},"mkdirSync()",". The first argument to this method is the path for the directory that you want to create. If you try and run this method using the blog post folder path:",[52,138468,138470],{"className":32791,"code":138469,"language":32793,"meta":57,"style":57},"fs.mkdirSync(blogPostFolder);\n",[59,138471,138472],{"__ignoreMap":57},[62,138473,138474,138476,138478],{"class":64,"line":65},[62,138475,131838],{"class":72},[62,138477,121138],{"class":122},[62,138479,138480],{"class":72},"(blogPostFolder);\n",[22,138482,138483],{},"You will get the following error:",[52,138485,138487],{"className":1663,"code":138486,"language":1665,"meta":57,"style":57},"vega recursive-dirs $ node app.js\n./blog/2019/02/21\nfs.js:115\n throw err;\n ^\n\nError: ENOENT: no such file or directory, mkdir './blog/2019/02/21'\n at Object.mkdirSync (fs.js:753:3)\n at Object.\u003Canonymous> (/Users/vega/dev/node/recursive-dirs/app.js:16:4)\n at Module._compile (internal/modules/cjs/loader.js:689:30)\n at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10)\n at Module.load (internal/modules/cjs/loader.js:599:32)\n at tryModuleLoad (internal/modules/cjs/loader.js:538:12)\n at Function.Module._load (internal/modules/cjs/loader.js:530:3)\n at Function.Module.runMain (internal/modules/cjs/loader.js:742:12)\n at startup (internal/bootstrap/node.js:283:19)\n at bootstrapNodeJSCore (internal/bootstrap/node.js:743:3)\n",[59,138488,138489,138503,138508,138513,138522,138527,138531,138557,138567,138586,138596,138606,138616,138626,138636,138645,138653],{"__ignoreMap":57},[62,138490,138491,138493,138496,138498,138500],{"class":64,"line":65},[62,138492,111184],{"class":122},[62,138494,138495],{"class":1675}," recursive-dirs",[62,138497,111190],{"class":72},[62,138499,32634],{"class":1675},[62,138501,138502],{"class":1675}," app.js\n",[62,138504,138505],{"class":64,"line":76},[62,138506,138507],{"class":122},"./blog/2019/02/21\n",[62,138509,138510],{"class":64,"line":82},[62,138511,138512],{"class":122},"fs.js:115\n",[62,138514,138515,138517,138520],{"class":64,"line":89},[62,138516,34131],{"class":122},[62,138518,138519],{"class":1675}," err",[62,138521,153],{"class":72},[62,138523,138524],{"class":64,"line":95},[62,138525,138526],{"class":122}," ^\n",[62,138528,138529],{"class":64,"line":101},[62,138530,79],{"emptyLinePlaceholder":13},[62,138532,138533,138536,138539,138542,138544,138546,138548,138551,138554],{"class":64,"line":107},[62,138534,138535],{"class":122},"Error:",[62,138537,138538],{"class":1675}," ENOENT:",[62,138540,138541],{"class":1675}," no",[62,138543,53659],{"class":1675},[62,138545,50388],{"class":1675},[62,138547,72786],{"class":1675},[62,138549,138550],{"class":1675}," directory,",[62,138552,138553],{"class":1675}," mkdir",[62,138555,138556],{"class":1675}," './blog/2019/02/21'\n",[62,138558,138559,138561,138564],{"class":64,"line":113},[62,138560,70808],{"class":122},[62,138562,138563],{"class":1675}," Object.mkdirSync",[62,138565,138566],{"class":72}," (fs.js:753:3)\n",[62,138568,138569,138571,138574,138576,138579,138581,138583],{"class":64,"line":129},[62,138570,70808],{"class":122},[62,138572,138573],{"class":1675}," Object.",[62,138575,760],{"class":68},[62,138577,138578],{"class":1675},"anonymou",[62,138580,57106],{"class":72},[62,138582,2583],{"class":68},[62,138584,138585],{"class":72}," (/Users/vega/dev/node/recursive-dirs/app.js:16:4)\n",[62,138587,138588,138590,138593],{"class":64,"line":134},[62,138589,70808],{"class":122},[62,138591,138592],{"class":1675}," Module._compile",[62,138594,138595],{"class":72}," (internal/modules/cjs/loader.js:689:30)\n",[62,138597,138598,138600,138603],{"class":64,"line":156},[62,138599,70808],{"class":122},[62,138601,138602],{"class":1675}," Object.Module._extensions..js",[62,138604,138605],{"class":72}," (internal/modules/cjs/loader.js:700:10)\n",[62,138607,138608,138610,138613],{"class":64,"line":161},[62,138609,70808],{"class":122},[62,138611,138612],{"class":1675}," Module.load",[62,138614,138615],{"class":72}," (internal/modules/cjs/loader.js:599:32)\n",[62,138617,138618,138620,138623],{"class":64,"line":167},[62,138619,70808],{"class":122},[62,138621,138622],{"class":1675}," tryModuleLoad",[62,138624,138625],{"class":72}," (internal/modules/cjs/loader.js:538:12)\n",[62,138627,138628,138630,138633],{"class":64,"line":173},[62,138629,70808],{"class":122},[62,138631,138632],{"class":1675}," Function.Module._load",[62,138634,138635],{"class":72}," (internal/modules/cjs/loader.js:530:3)\n",[62,138637,138638,138640,138642],{"class":64,"line":179},[62,138639,70808],{"class":122},[62,138641,125056],{"class":1675},[62,138643,138644],{"class":72}," (internal/modules/cjs/loader.js:742:12)\n",[62,138646,138647,138649,138651],{"class":64,"line":185},[62,138648,70808],{"class":122},[62,138650,125066],{"class":1675},[62,138652,125069],{"class":72},[62,138654,138655,138657,138659],{"class":64,"line":191},[62,138656,70808],{"class":122},[62,138658,125076],{"class":1675},[62,138660,125079],{"class":72},[22,138662,138663],{},"This is because there is no blog folder yet and if there is no parent folder how is it going to create sub folders. If you create a blog folder you will have the same problem because there is no 2019 folder.",[22,138665,138666,138667,138669],{},"The solution to this is to recursively create directories but by default this isn't the case. The 2nd argument to the ",[59,138668,138465],{}," method is an options object that contains a property called recursive. If you set this true and run your script again everything should work just fine.",[52,138671,138673],{"className":32791,"code":138672,"language":32793,"meta":57,"style":57},"fs.mkdirSync(blogPostFolder, { recursive: true });\n",[59,138674,138675],{"__ignoreMap":57},[62,138676,138677,138679,138681,138684,138686],{"class":64,"line":65},[62,138678,131838],{"class":72},[62,138680,121138],{"class":122},[62,138682,138683],{"class":72},"(blogPostFolder, { recursive: ",[62,138685,21775],{"class":149},[62,138687,21843],{"class":72},[29685,138689,138690],{},[22,138691,138692],{},"I am not 100% sure on this but it appears this option is working as of v10.15.1",[22,138694,138695],{},"If you have been following along you should end up with something like this",[52,138697,138699],{"className":32791,"code":138698,"language":32793,"meta":57,"style":57},"const fs = require(\"fs\");\n\nconst header = {\n date: \"2019-02-20 08:00:00\"\n};\n\nconst createdOn = new Date(header.date);\nconst year = createdOn.getFullYear();\nconst month = `${\n createdOn.getMonth() + 1 \u003C 10 ? \"0\" : \"\"\n}${createdOn.getMonth() + 1}`;\nconst day = `${createdOn.getDate() \u003C 10 ? \"0\" : \"\"}${createdOn.getDate()}`;\n\nconst blogPostFolder = `./blog/${year}/${month}/${day}`;\n\nfs.mkdirSync(blogPostFolder, { recursive: true });\n",[59,138700,138701,138717,138721,138731,138737,138741,138745,138759,138773,138783,138809,138829,138871,138875,138899,138903],{"__ignoreMap":57},[62,138702,138703,138705,138707,138709,138711,138713,138715],{"class":64,"line":65},[62,138704,110541],{"class":68},[62,138706,120730],{"class":149},[62,138708,2556],{"class":68},[62,138710,120735],{"class":122},[62,138712,2109],{"class":72},[62,138714,131813],{"class":1675},[62,138716,1133],{"class":72},[62,138718,138719],{"class":64,"line":76},[62,138720,79],{"emptyLinePlaceholder":13},[62,138722,138723,138725,138727,138729],{"class":64,"line":82},[62,138724,110541],{"class":68},[62,138726,137999],{"class":149},[62,138728,2556],{"class":68},[62,138730,126],{"class":72},[62,138732,138733,138735],{"class":64,"line":89},[62,138734,138008],{"class":72},[62,138736,138248],{"class":1675},[62,138738,138739],{"class":64,"line":95},[62,138740,107354],{"class":72},[62,138742,138743],{"class":64,"line":101},[62,138744,79],{"emptyLinePlaceholder":13},[62,138746,138747,138749,138751,138753,138755,138757],{"class":64,"line":107},[62,138748,110541],{"class":68},[62,138750,131573],{"class":149},[62,138752,2556],{"class":68},[62,138754,466],{"class":68},[62,138756,61498],{"class":122},[62,138758,138030],{"class":72},[62,138760,138761,138763,138765,138767,138769,138771],{"class":64,"line":113},[62,138762,110541],{"class":68},[62,138764,131588],{"class":149},[62,138766,2556],{"class":68},[62,138768,131593],{"class":72},[62,138770,131596],{"class":122},[62,138772,822],{"class":72},[62,138774,138775,138777,138779,138781],{"class":64,"line":129},[62,138776,110541],{"class":68},[62,138778,131605],{"class":149},[62,138780,2556],{"class":68},[62,138782,138120],{"class":1675},[62,138784,138785,138787,138789,138791,138793,138795,138797,138799,138801,138803,138805,138807],{"class":64,"line":134},[62,138786,138125],{"class":72},[62,138788,2755],{"class":1675},[62,138790,131617],{"class":122},[62,138792,5398],{"class":1675},[62,138794,1148],{"class":68},[62,138796,22038],{"class":149},[62,138798,5607],{"class":68},[62,138800,30327],{"class":149},[62,138802,64807],{"class":68},[62,138804,131632],{"class":1675},[62,138806,57090],{"class":68},[62,138808,138148],{"class":1675},[62,138810,138811,138813,138815,138817,138819,138821,138823,138825,138827],{"class":64,"line":156},[62,138812,138153],{"class":1675},[62,138814,131612],{"class":72},[62,138816,2755],{"class":1675},[62,138818,131617],{"class":122},[62,138820,5398],{"class":1675},[62,138822,1148],{"class":68},[62,138824,22038],{"class":149},[62,138826,21727],{"class":1675},[62,138828,153],{"class":72},[62,138830,138831,138833,138835,138837,138839,138841,138843,138845,138847,138849,138851,138853,138855,138857,138859,138861,138863,138865,138867,138869],{"class":64,"line":161},[62,138832,110541],{"class":68},[62,138834,131660],{"class":149},[62,138836,2556],{"class":68},[62,138838,121072],{"class":1675},[62,138840,131612],{"class":72},[62,138842,2755],{"class":1675},[62,138844,42183],{"class":122},[62,138846,5398],{"class":1675},[62,138848,760],{"class":68},[62,138850,30327],{"class":149},[62,138852,64807],{"class":68},[62,138854,131632],{"class":1675},[62,138856,57090],{"class":68},[62,138858,131637],{"class":1675},[62,138860,131612],{"class":72},[62,138862,2755],{"class":1675},[62,138864,42183],{"class":122},[62,138866,131693],{"class":1675},[62,138868,21727],{"class":1675},[62,138870,153],{"class":72},[62,138872,138873],{"class":64,"line":167},[62,138874,79],{"emptyLinePlaceholder":13},[62,138876,138877,138879,138881,138883,138885,138887,138889,138891,138893,138895,138897],{"class":64,"line":173},[62,138878,110541],{"class":68},[62,138880,131716],{"class":149},[62,138882,2556],{"class":68},[62,138884,131721],{"class":1675},[62,138886,8604],{"class":72},[62,138888,131726],{"class":1675},[62,138890,131729],{"class":72},[62,138892,131726],{"class":1675},[62,138894,131734],{"class":72},[62,138896,21727],{"class":1675},[62,138898,153],{"class":72},[62,138900,138901],{"class":64,"line":179},[62,138902,79],{"emptyLinePlaceholder":13},[62,138904,138905,138907,138909,138911,138913],{"class":64,"line":185},[62,138906,131838],{"class":72},[62,138908,121138],{"class":122},[62,138910,138683],{"class":72},[62,138912,21775],{"class":149},[62,138914,21843],{"class":72},[26,138916,1499],{"id":1498},[22,138918,138919],{},"This was just a small problem that came up during the migration. If anyone is interested in hearing specific on the migration script please let me know. I wanted to keep this post focused on the problem and I hope it helps someone out.",[22,138921,36004,138922,82545],{},[36006,138923],{},[1527,138925,138926],{},"html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}",{"title":57,"searchDepth":76,"depth":76,"links":138928},[138929,138930,138931],{"id":137959,"depth":76,"text":137960},{"id":138385,"depth":76,"text":138386},{"id":1498,"depth":76,"text":1499},{"_id":138933,"path":138934,"title":138935,"description":138936,"meta":138937,"body":138942},"content/blog/2019/02/18/twitter-cards-meta-tags.md","/blog/2019/02/18/twitter-cards-meta-tags","How to add Twitter Card Meta Tags to your Blog","In this tutorial you will learn what a Twitter Card is along with step by step instructions how to add them to your blog and validate that they are working.",{"slug":138938,"date":138939,"published":13,"tags":138940,"author":17,"cover":138941,"excerpt":-1},"twitter-cards-meta-tags","2019-02-18 22:00:00",[15775],"./twitter-cards.png",{"type":19,"value":138943,"toc":139540},[138944,138951,138954,138958,138961,138967,138970,138973,138979,138982,138986,138989,138992,139067,139070,139112,139116,139119,139237,139240,139245,139248,139275,139278,139282,139285,139310,139313,139340,139346,139349,139509,139513,139520,139524,139527,139532,139534,139537],[22,138945,138946,138947,138950],{},"For those of you who haven't been reading my blog lately I am in the middle of converting my website from WordPress over to a static site generator called ",[677,138948,104836],{"href":104834,"rel":138949},[681]," that is built on VueJS. There are a lot of things that I took for granted when using a proven blogging platform like WordPress. In WordPress when I created a new blog post and then shared it on Twitter I got this nice display of my blog post as apposed to just a link.",[22,138952,138953],{},"This might not seem like a big thing to most but why write something if nobody reads it? Engagement is the name of game on social media platforms like Twitter and Twitter Cards (and images in general) are proven to increase engagement. In this article I will tell you what a Twitter Card is and how to add how to add Twitter Card meta tags to your blog.",[26,138955,138957],{"id":138956},"what-is-a-twitter-card","What is a Twitter Card",[22,138959,138960],{},"Before we dive into the technical details on how to add Twitter cards to your blog it might make sense to understand what a Twitter Card actually is. I currently don't have any Twitter Cards setup for the home page of my website. After sitting down to write this article I realize that I should fix that. If I share out a link of my homepage on Twitter it looks like this.",[22,138962,138963],{},[653,138964],{"alt":138965,"src":138966},"Link with no Twitter Card","./twitter-link-no-card.png",[22,138968,138969],{},"Pretty boring right? No matter what my message is you aren't going to be very compelled to click that link.",[22,138971,138972],{},"Here is a recent blog post that I just posted a link to on Twitter.",[22,138974,138975],{},[653,138976],{"alt":138977,"src":138978},"Link with Twitter Card","./twitter-link-with-card.png",[22,138980,138981],{},"In this case you can see a nicely formatted card with a summary and an image. Now I will admit the image could use a little work as some text is running up against the side, but you get the idea. I think this is a real nice addition that you can add to your blog today and with just a few lines of code.",[26,138983,138985],{"id":138984},"what-are-meta-tags","What are meta tags",[22,138987,138988],{},"If you're familiar with HTML chances are you have come across meta tags before. Metadata is data (information) about data. This means that the meta tag provides metadata about the HTML document. Metadata will not be displayed on the page, but will be machine parsable. This just means that it will not be displayed but it will exist in your pages source code.",[22,138990,138991],{},"Meta tags are defined in the head of an HTML document and contain the following attributes:",[11922,138993,138994,139005],{},[11925,138995,138996],{},[11928,138997,138998,139001,139003],{},[11931,138999,139000],{},"Attribute",[11931,139002,14632],{},[11931,139004,11939],{},[11941,139006,139007,139018,139027,139044],{},[11928,139008,139009,139012,139015],{},[11946,139010,139011],{},"charset",[11946,139013,139014],{},"character_set",[11946,139016,139017],{},"Specifies the character encoding for the HTML document",[11928,139019,139020,139022,139024],{},[11946,139021,2230],{},[11946,139023,1727],{},[11946,139025,139026],{},"Gives the value associated with the http-equiv or name attribute",[11928,139028,139029,139032,139041],{},[11946,139030,139031],{},"http-equiv",[11946,139033,139034,139035,139037,139038,139040],{},"content-type",[36006,139036],{},"default-style",[36006,139039],{},"refresh",[11946,139042,139043],{},"Provides an HTTP header for the information/value of the content attribute",[11928,139045,139046,139048,139064],{},[11946,139047,3107],{},[11946,139049,139050,139051,8585,139053,3117,139055,139057,139058,139060,139061,139063],{},"application-name",[36006,139052],{},[36006,139054],{},[36006,139056],{},"generator",[36006,139059],{},"keywords",[36006,139062],{},"viewport",[11946,139065,139066],{},"Specifies a name for the metadata",[22,139068,139069],{},"This is an example of what a meta tag looks like.",[52,139071,139073],{"className":15773,"code":139072,"language":15775,"meta":57,"style":57},"\u003Chead>\n \u003Cmeta name=\"description\" content=\"Welcome to my website!\" />\n\u003C/head>\n",[59,139074,139075,139083,139104],{"__ignoreMap":57},[62,139076,139077,139079,139081],{"class":64,"line":65},[62,139078,760],{"class":72},[62,139080,15824],{"class":1780},[62,139082,1784],{"class":72},[62,139084,139085,139087,139089,139091,139093,139095,139097,139099,139102],{"class":64,"line":76},[62,139086,33056],{"class":72},[62,139088,20701],{"class":1780},[62,139090,16107],{"class":122},[62,139092,146],{"class":72},[62,139094,24100],{"class":1675},[62,139096,20727],{"class":122},[62,139098,146],{"class":72},[62,139100,139101],{"class":1675},"\"Welcome to my website!\"",[62,139103,67133],{"class":72},[62,139105,139106,139108,139110],{"class":64,"line":82},[62,139107,1818],{"class":72},[62,139109,15824],{"class":1780},[62,139111,1784],{"class":72},[636,139113,139115],{"id":139114},"meta-tag-use-cases","Meta Tag Use Cases",[22,139117,139118],{},"Now that you know what a meta tag it might be good to understand what they are useful for. Here are some of examples of meta tags that you might have come across already.",[52,139120,139122],{"className":15773,"code":139121,"language":15775,"meta":57,"style":57},"\u003Chead>\n \u003Cmeta charset=\"UTF-8\" />\n \u003Cmeta name=\"description\" content=\"Welcome to my website!\" />\n \u003Cmeta name=\"keywords\" content=\"HTML,CSS,JavaScript\" />\n \u003Cmeta name=\"author\" content=\"Dan Vega\" />\n \u003Cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n\u003C/head>\n",[59,139123,139124,139132,139146,139166,139188,139209,139229],{"__ignoreMap":57},[62,139125,139126,139128,139130],{"class":64,"line":65},[62,139127,760],{"class":72},[62,139129,15824],{"class":1780},[62,139131,1784],{"class":72},[62,139133,139134,139136,139138,139140,139142,139144],{"class":64,"line":76},[62,139135,33056],{"class":72},[62,139137,20701],{"class":1780},[62,139139,20704],{"class":122},[62,139141,146],{"class":72},[62,139143,20709],{"class":1675},[62,139145,67133],{"class":72},[62,139147,139148,139150,139152,139154,139156,139158,139160,139162,139164],{"class":64,"line":82},[62,139149,33056],{"class":72},[62,139151,20701],{"class":1780},[62,139153,16107],{"class":122},[62,139155,146],{"class":72},[62,139157,24100],{"class":1675},[62,139159,20727],{"class":122},[62,139161,146],{"class":72},[62,139163,139101],{"class":1675},[62,139165,67133],{"class":72},[62,139167,139168,139170,139172,139174,139176,139179,139181,139183,139186],{"class":64,"line":89},[62,139169,33056],{"class":72},[62,139171,20701],{"class":1780},[62,139173,16107],{"class":122},[62,139175,146],{"class":72},[62,139177,139178],{"class":1675},"\"keywords\"",[62,139180,20727],{"class":122},[62,139182,146],{"class":72},[62,139184,139185],{"class":1675},"\"HTML,CSS,JavaScript\"",[62,139187,67133],{"class":72},[62,139189,139190,139192,139194,139196,139198,139201,139203,139205,139207],{"class":64,"line":95},[62,139191,33056],{"class":72},[62,139193,20701],{"class":1780},[62,139195,16107],{"class":122},[62,139197,146],{"class":72},[62,139199,139200],{"class":1675},"\"author\"",[62,139202,20727],{"class":122},[62,139204,146],{"class":72},[62,139206,132040],{"class":1675},[62,139208,67133],{"class":72},[62,139210,139211,139213,139215,139217,139219,139221,139223,139225,139227],{"class":64,"line":101},[62,139212,33056],{"class":72},[62,139214,20701],{"class":1780},[62,139216,16107],{"class":122},[62,139218,146],{"class":72},[62,139220,20724],{"class":1675},[62,139222,20727],{"class":122},[62,139224,146],{"class":72},[62,139226,20732],{"class":1675},[62,139228,67133],{"class":72},[62,139230,139231,139233,139235],{"class":64,"line":107},[62,139232,1818],{"class":72},[62,139234,15824],{"class":1780},[62,139236,1784],{"class":72},[22,139238,139239],{},"If you done any kind of Search Engine Optimization (SEO) you have probably come across the meta description.",[29685,139241,139242],{},[22,139243,139244],{},"The meta description is a snippet of up to about 155 characters – a tag in HTML – which summarizes a page's content. Search engines show the meta description in search results mostly when the searched-for phrase is within the description, so optimizing the meta description is crucial for on-page SEO .",[22,139246,139247],{},"Most meta tags are nothing more than name and content, in fact you could create your own if you wanted to.",[52,139249,139251],{"className":15773,"code":139250,"language":15775,"meta":57,"style":57},"\u003Cmeta name=\"msg\" content=\"hello meta tags\" />\n",[59,139252,139253],{"__ignoreMap":57},[62,139254,139255,139257,139259,139261,139263,139266,139268,139270,139273],{"class":64,"line":65},[62,139256,760],{"class":72},[62,139258,20701],{"class":1780},[62,139260,16107],{"class":122},[62,139262,146],{"class":72},[62,139264,139265],{"class":1675},"\"msg\"",[62,139267,20727],{"class":122},[62,139269,146],{"class":72},[62,139271,139272],{"class":1675},"\"hello meta tags\"",[62,139274,67133],{"class":72},[22,139276,139277],{},"While this is valid syntax it really doesn't do anything. If you were to write some program that scanned html looking for this specific meta tag than it would make sense to include, otherwise its useless.",[26,139279,139281],{"id":139280},"adding-twitter-cards-to-your-blog-or-website","Adding Twitter Cards to your blog or website",[22,139283,139284],{},"No that you know a little bit more about meta tags lets take that knowledge and apply it to Twitter Cards. The first meta tag you need to set is the twitter:card meta tag.",[52,139286,139288],{"className":15773,"code":139287,"language":15775,"meta":57,"style":57},"\u003Cmeta name=\"twitter:card\" content=\"summary_large_image\" />\n",[59,139289,139290],{"__ignoreMap":57},[62,139291,139292,139294,139296,139298,139300,139302,139304,139306,139308],{"class":64,"line":65},[62,139293,760],{"class":72},[62,139295,20701],{"class":1780},[62,139297,16107],{"class":122},[62,139299,146],{"class":72},[62,139301,134145],{"class":1675},[62,139303,20727],{"class":122},[62,139305,146],{"class":72},[62,139307,134152],{"class":1675},[62,139309,67133],{"class":72},[22,139311,139312],{},"This tag can have one of the following values",[915,139314,139315,139321,139327,139334],{},[37,139316,139317],{},[677,139318,110998],{"href":139319,"rel":139320},"https://developer.twitter.com/en/docs/tweets/optimize-with-cards/overview/summary",[681],[37,139322,139323],{},[677,139324,139326],{"href":139319,"rel":139325},[681],"summary_large_image",[37,139328,139329],{},[677,139330,139333],{"href":139331,"rel":139332},"https://developer.twitter.com/en/docs/tweets/optimize-with-cards/overview/player-card",[681],"player",[37,139335,139336],{},[677,139337,50375],{"href":139338,"rel":139339},"https://developer.twitter.com/en/docs/tweets/optimize-with-cards/overview/app-card",[681],[22,139341,139342,139343,139345],{},"In my case and the example I showed you earlier of the blog post I am using ",[646,139344,139326],{},". The other tags are pretty self explinatory if you compare them to the example card. The one thing I will say is that make sure you give the twitter:image tag the full path to the image you want to use.",[22,139347,139348],{},"Here are all of the tags I used for that blog post and what it looks like.",[52,139350,139352],{"className":15773,"code":139351,"language":15775,"meta":57,"style":57},"\u003Chead>\n \u003Cmeta name=\"twitter:card\" content=\"summary_large_image\" />\n \u003Cmeta\n name=\"twitter:description\"\n content=\"How to create your first npm package and publish it.\"\n />\n \u003Cmeta name=\"twitter:title\" content=\"Creating your first npm package\" />\n \u003Cmeta name=\"twitter:site\" content=\"@therealdanvega\" />\n \u003Cmeta\n name=\"twitter:image\"\n content=\"https://www.danvega.me/assets/static/npm_cover.bd64798.eced3da.png\"\n />\n \u003Cmeta name=\"twitter:creator\" content=\"@therealdanvega\" />\n\u003C/head>\n",[59,139353,139354,139362,139382,139389,139399,139409,139413,139433,139453,139459,139468,139477,139481,139501],{"__ignoreMap":57},[62,139355,139356,139358,139360],{"class":64,"line":65},[62,139357,760],{"class":72},[62,139359,15824],{"class":1780},[62,139361,1784],{"class":72},[62,139363,139364,139366,139368,139370,139372,139374,139376,139378,139380],{"class":64,"line":76},[62,139365,33056],{"class":72},[62,139367,20701],{"class":1780},[62,139369,16107],{"class":122},[62,139371,146],{"class":72},[62,139373,134145],{"class":1675},[62,139375,20727],{"class":122},[62,139377,146],{"class":72},[62,139379,134152],{"class":1675},[62,139381,67133],{"class":72},[62,139383,139384,139386],{"class":64,"line":82},[62,139385,33056],{"class":72},[62,139387,139388],{"class":1780},"meta\n",[62,139390,139391,139394,139396],{"class":64,"line":89},[62,139392,139393],{"class":122}," name",[62,139395,146],{"class":72},[62,139397,139398],{"class":1675},"\"twitter:description\"\n",[62,139400,139401,139404,139406],{"class":64,"line":95},[62,139402,139403],{"class":122}," content",[62,139405,146],{"class":72},[62,139407,139408],{"class":1675},"\"How to create your first npm package and publish it.\"\n",[62,139410,139411],{"class":64,"line":101},[62,139412,85800],{"class":72},[62,139414,139415,139417,139419,139421,139423,139425,139427,139429,139431],{"class":64,"line":107},[62,139416,33056],{"class":72},[62,139418,20701],{"class":1780},[62,139420,16107],{"class":122},[62,139422,146],{"class":72},[62,139424,134189],{"class":1675},[62,139426,20727],{"class":122},[62,139428,146],{"class":72},[62,139430,134196],{"class":1675},[62,139432,67133],{"class":72},[62,139434,139435,139437,139439,139441,139443,139445,139447,139449,139451],{"class":64,"line":113},[62,139436,33056],{"class":72},[62,139438,20701],{"class":1780},[62,139440,16107],{"class":122},[62,139442,146],{"class":72},[62,139444,134211],{"class":1675},[62,139446,20727],{"class":122},[62,139448,146],{"class":72},[62,139450,134218],{"class":1675},[62,139452,67133],{"class":72},[62,139454,139455,139457],{"class":64,"line":129},[62,139456,33056],{"class":72},[62,139458,139388],{"class":1780},[62,139460,139461,139463,139465],{"class":64,"line":134},[62,139462,139393],{"class":122},[62,139464,146],{"class":72},[62,139466,139467],{"class":1675},"\"twitter:image\"\n",[62,139469,139470,139472,139474],{"class":64,"line":156},[62,139471,139403],{"class":122},[62,139473,146],{"class":72},[62,139475,139476],{"class":1675},"\"https://www.danvega.me/assets/static/npm_cover.bd64798.eced3da.png\"\n",[62,139478,139479],{"class":64,"line":161},[62,139480,85800],{"class":72},[62,139482,139483,139485,139487,139489,139491,139493,139495,139497,139499],{"class":64,"line":167},[62,139484,33056],{"class":72},[62,139486,20701],{"class":1780},[62,139488,16107],{"class":122},[62,139490,146],{"class":72},[62,139492,134255],{"class":1675},[62,139494,20727],{"class":122},[62,139496,146],{"class":72},[62,139498,134218],{"class":1675},[62,139500,67133],{"class":72},[62,139502,139503,139505,139507],{"class":64,"line":173},[62,139504,1818],{"class":72},[62,139506,15824],{"class":1780},[62,139508,1784],{"class":72},[22,139510,139511],{},[653,139512],{"alt":138977,"src":138978},[22,139514,139515,139516,2755],{},"If you want to learn more about the different types of cards you can create and all of the options please read through the ",[677,139517,86173],{"href":139518,"rel":139519},"https://developer.twitter.com/en/docs/tweets/optimize-with-cards/overview/abouts-cards",[681],[636,139521,139523],{"id":139522},"twitter-card-validator","Twitter Card Validator",[22,139525,139526],{},"If you ever have problems with your Twitter Cards not displaying correctly there is a way to test them. You can use the Twitter Card Validator, plug in the url you want to test and see what the outcome is along with some debugging information. Here I have entered another blog post and I get a preview of what my twitter card is going to look like.",[22,139528,139529],{},[653,139530],{"alt":139523,"src":139531},"./twitter-card-validator.png",[26,139533,1499],{"id":1498},[22,139535,139536],{},"I mentioned this at the start of this article that I was converting my site from WordPress to Gridsome. If you're interested in how I added Twitter cards to this blog let me know. If you're also interested in how to add similar meta tags for other social networks you can do some research on open graph or just let me know that you would like to see another article on that.",[1527,139538,139539],{},"html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .suaqH, html code.shiki .suaqH{--shiki-default:#22863A;--shiki-dark:#85E89D;--shiki-sepia:#22863A}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}",{"title":57,"searchDepth":76,"depth":76,"links":139541},[139542,139543,139546,139549],{"id":138956,"depth":76,"text":138957},{"id":138984,"depth":76,"text":138985,"children":139544},[139545],{"id":139114,"depth":82,"text":139115},{"id":139280,"depth":76,"text":139281,"children":139547},[139548],{"id":139522,"depth":82,"text":139523},{"id":1498,"depth":76,"text":1499},{"_id":139551,"path":139552,"title":139553,"description":139554,"meta":139555,"body":139560},"content/blog/2019/02/13/html-template-tag.md","/blog/2019/02/13/html-template-tag","HTML Template Tag in Vanilla JavaScript and Vue","A look at what the HTML template tag is and how it can be used in Vanilla JavaScript as well as what its role in Vue is.",{"slug":139556,"date":139557,"published":13,"tags":139558,"author":17,"cover":139559,"excerpt":-1},"html-template-tag","2019-02-13 22:30:00",[15775,32793,105335],"./html-template-cover.png",{"type":19,"value":139561,"toc":141372},[139562,139569,139582,139591,139598,139601,139604,139612,139615,139619,139627,139633,139637,139640,139643,140187,140189,140196,140369,140372,140457,140466,140528,140531,140617,140620,140663,140666,140988,140992,140995,141001,141004,141072,141116,141119,141238,141242,141245,141249,141252,141350,141353,141364,141366,141369],[22,139563,139564,139565,139568],{},"For the past few months, I have been writing a ton of documentation, tutorials, and exercises around VueJS and Vanilla JavaScript. Just for some context, I am Curriculum Developer at ",[677,139566,104491],{"href":107914,"rel":139567},[681],", a coding bootcamp that teaches students how to code in 14 weeks. With that in mind, all of the content is geared towards beginners but made for everyone.",[22,139570,139571,139572,19931,139576,139581],{},"I was recently working some tutorials and exercises around The Fetch API and I wanted to put together a nice example of how to read some JSON data from a local file and then add it to a page. In a simple example I would just use a ",[677,139573,21611],{"href":139574,"rel":139575},"https://developer.mozilla.org/en-US/docs/Web/API/Document/createElement",[681],[677,139577,139580],{"href":139578,"rel":139579},"https://developer.mozilla.org/en-US/docs/Web/API/Document/createTextNode",[681],"createTextNode"," and append the items to the DOM.",[22,139583,139584,139585,139590],{},"In a more complex example where there is a lot more markup involved it can become very cumbersome to create elements, nodes as well as dealing with attributes and classes. In this instance, a great solution is ",[677,139586,139589],{"href":139587,"rel":139588},"https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template",[681],"The Content Element Template",". I also realized that a lot of developers (beginning and vets) might not know what this is or why we would use it.",[22,139592,139593,139594,139597],{},"In this article, I am going to take a look at the ",[59,139595,139596],{},"\u003Ctemplate/>"," tag in HTML & Vanilla JavaScript. When you know why this tag exists it might make more sense why its used in Vue Single File Components.",[26,139599,139589],{"id":139600},"the-content-element-template",[22,139602,139603],{},"You can think of the template tag in HTML the same way you would think of any other template. A template is a mold or pattern that gives you a starting point to create something else from. The MDN docs define The HTML Content Template as:",[29685,139605,139606,139609],{},[22,139607,139608],{},"The HTML Content Template element is a mechanism for holding client-side content that is not to be rendered when a page is loaded but may subsequently be instantiated during runtime using JavaScript.",[22,139610,139611],{},"Think of a template as a content fragment that is being stored for subsequent use in the document. While the parser does process the contents of the template element while loading the page, it does so only to ensure that those contents are valid; the element's contents are not rendered, however.",[22,139613,139614],{},"That sounds pretty straight forward but if it doesn't make total sense yet don't worry. We are going to take a look at a practical example that will hopefully clear everything up for us.",[26,139616,139618],{"id":139617},"html-content-template-demo","HTML Content Template Demo",[22,139620,139621,139622,139626],{},"I put together that shows off how to use the template tag in Vanilla JavaScript. If you want to check out the source code for this demo you can find it on ",[677,139623,50830],{"href":139624,"rel":139625},"https://github.com/cfaddict/html-template-article",[681],". We are going to build a page that loads a list of user cards based on some JSON data and it will end up looking like this.",[22,139628,139629],{},[653,139630],{"alt":139631,"src":139632},"Demo","./html-content-demo.png",[636,139634,139636],{"id":139635},"markup","Markup",[22,139638,139639],{},"As I said before the goal of this project was to read in some user data from a JSON file and then write the user information to the page. This gets really cumbersome when you have to create elements one by one and add them to the page.",[22,139641,139642],{},"A better way to approach this is to build out what the markup and CSS are going to look like and then wrap the markup in a template tag. The following HTML is what I ended up with. When I was done I just add a template tag around the markup and give it an id.",[52,139644,139646],{"className":15773,"code":139645,"language":15775,"meta":57,"style":57},"\u003Ctemplate id=\"user-card-template\">\n \u003Cdiv class=\"user\">\n \u003Cdiv class=\"profile\">\n \u003Cimg src=\"\" class=\"avatar\" />\n \u003Ch2>\u003C/h2>\n \u003Cspan class=\"title\">\u003C/span>\n \u003Cdiv class=\"social\">\n \u003Ca href=\"https://www.github.com\" target=\"_blank\"\n >\u003Ci class=\"fab fa-github fa-2x\" target=\"_blank\">\u003C/i\n >\u003C/a>\n \u003Ca href=\"https://www.reddit.com\" target=\"_blank\"\n >\u003Ci class=\"fab fa-reddit-alien fa-2x\">\u003C/i\n >\u003C/a>\n \u003Ca href=\"https://www.twitter.com\" target=\"_blank\"\n >\u003Ci class=\"fab fa-twitter fa-2x\">\u003C/i\n >\u003C/a>\n \u003Ca href=\"https://www.instagram.com\" target=\"_blank\"\n >\u003Ci class=\"fab fa-instagram fa-2x\">\u003C/i\n >\u003C/a>\n \u003Ca href=\"http://www.facebook.com\" target=\"_blank\"\n >\u003Ci class=\"fab fa-facebook-f fa-2x\">\u003C/i\n >\u003C/a>\n \u003C/div>\n \u003C/div>\n \u003Cdiv class=\"stats\">\n \u003Cdiv class=\"posts\">\n \u003Ch3>\u003C/h3>\n \u003Cspan>Posts\u003C/span>\n \u003C/div>\n \u003Cdiv class=\"likes\">\n \u003Ch3>\u003C/h3>\n \u003Cspan>Likes\u003C/span>\n \u003C/div>\n \u003Cdiv class=\"followers\">\n \u003Ch3>\u003C/h3>\n \u003Cspan>Followers\u003C/span>\n \u003C/div>\n \u003C/div>\n \u003C/div>\n\u003C/template>\n",[59,139647,139648,139663,139677,139692,139713,139725,139743,139758,139778,139805,139814,139833,139850,139858,139877,139894,139902,139921,139938,139946,139965,139982,139990,139998,140006,140021,140035,140047,140059,140067,140082,140094,140107,140115,140130,140142,140155,140163,140171,140179],{"__ignoreMap":57},[62,139649,139650,139652,139654,139656,139658,139661],{"class":64,"line":65},[62,139651,760],{"class":72},[62,139653,105991],{"class":1780},[62,139655,20831],{"class":122},[62,139657,146],{"class":72},[62,139659,139660],{"class":1675},"\"user-card-template\"",[62,139662,1784],{"class":72},[62,139664,139665,139667,139669,139671,139673,139675],{"class":64,"line":76},[62,139666,33056],{"class":72},[62,139668,15944],{"class":1780},[62,139670,119],{"class":122},[62,139672,146],{"class":72},[62,139674,22491],{"class":1675},[62,139676,1784],{"class":72},[62,139678,139679,139681,139683,139685,139687,139690],{"class":64,"line":82},[62,139680,1789],{"class":72},[62,139682,15944],{"class":1780},[62,139684,119],{"class":122},[62,139686,146],{"class":72},[62,139688,139689],{"class":1675},"\"profile\"",[62,139691,1784],{"class":72},[62,139693,139694,139696,139698,139700,139702,139704,139706,139708,139711],{"class":64,"line":89},[62,139695,92881],{"class":72},[62,139697,653],{"class":1780},[62,139699,15849],{"class":122},[62,139701,146],{"class":72},[62,139703,25895],{"class":1675},[62,139705,119],{"class":122},[62,139707,146],{"class":72},[62,139709,139710],{"class":1675},"\"avatar\"",[62,139712,67133],{"class":72},[62,139714,139715,139717,139719,139721,139723],{"class":64,"line":95},[62,139716,92881],{"class":72},[62,139718,26],{"class":1780},[62,139720,15857],{"class":72},[62,139722,26],{"class":1780},[62,139724,1784],{"class":72},[62,139726,139727,139729,139731,139733,139735,139737,139739,139741],{"class":64,"line":101},[62,139728,92881],{"class":72},[62,139730,62],{"class":1780},[62,139732,119],{"class":122},[62,139734,146],{"class":72},[62,139736,42161],{"class":1675},[62,139738,15857],{"class":72},[62,139740,62],{"class":1780},[62,139742,1784],{"class":72},[62,139744,139745,139747,139749,139751,139753,139756],{"class":64,"line":107},[62,139746,92881],{"class":72},[62,139748,15944],{"class":1780},[62,139750,119],{"class":122},[62,139752,146],{"class":72},[62,139754,139755],{"class":1675},"\"social\"",[62,139757,1784],{"class":72},[62,139759,139760,139762,139764,139766,139768,139771,139773,139775],{"class":64,"line":113},[62,139761,1896],{"class":72},[62,139763,677],{"class":1780},[62,139765,16494],{"class":122},[62,139767,146],{"class":72},[62,139769,139770],{"class":1675},"\"https://www.github.com\"",[62,139772,57441],{"class":122},[62,139774,146],{"class":72},[62,139776,139777],{"class":1675},"\"_blank\"\n",[62,139779,139780,139783,139786,139788,139790,139793,139795,139797,139800,139802],{"class":64,"line":129},[62,139781,139782],{"class":72}," >\u003C",[62,139784,139785],{"class":1780},"i",[62,139787,119],{"class":122},[62,139789,146],{"class":72},[62,139791,139792],{"class":1675},"\"fab fa-github fa-2x\"",[62,139794,57441],{"class":122},[62,139796,146],{"class":72},[62,139798,139799],{"class":1675},"\"_blank\"",[62,139801,15857],{"class":72},[62,139803,139804],{"class":1780},"i\n",[62,139806,139807,139810,139812],{"class":64,"line":134},[62,139808,139809],{"class":72}," >\u003C/",[62,139811,677],{"class":1780},[62,139813,1784],{"class":72},[62,139815,139816,139818,139820,139822,139824,139827,139829,139831],{"class":64,"line":156},[62,139817,1896],{"class":72},[62,139819,677],{"class":1780},[62,139821,16494],{"class":122},[62,139823,146],{"class":72},[62,139825,139826],{"class":1675},"\"https://www.reddit.com\"",[62,139828,57441],{"class":122},[62,139830,146],{"class":72},[62,139832,139777],{"class":1675},[62,139834,139835,139837,139839,139841,139843,139846,139848],{"class":64,"line":161},[62,139836,139782],{"class":72},[62,139838,139785],{"class":1780},[62,139840,119],{"class":122},[62,139842,146],{"class":72},[62,139844,139845],{"class":1675},"\"fab fa-reddit-alien fa-2x\"",[62,139847,15857],{"class":72},[62,139849,139804],{"class":1780},[62,139851,139852,139854,139856],{"class":64,"line":167},[62,139853,139809],{"class":72},[62,139855,677],{"class":1780},[62,139857,1784],{"class":72},[62,139859,139860,139862,139864,139866,139868,139871,139873,139875],{"class":64,"line":173},[62,139861,1896],{"class":72},[62,139863,677],{"class":1780},[62,139865,16494],{"class":122},[62,139867,146],{"class":72},[62,139869,139870],{"class":1675},"\"https://www.twitter.com\"",[62,139872,57441],{"class":122},[62,139874,146],{"class":72},[62,139876,139777],{"class":1675},[62,139878,139879,139881,139883,139885,139887,139890,139892],{"class":64,"line":179},[62,139880,139782],{"class":72},[62,139882,139785],{"class":1780},[62,139884,119],{"class":122},[62,139886,146],{"class":72},[62,139888,139889],{"class":1675},"\"fab fa-twitter fa-2x\"",[62,139891,15857],{"class":72},[62,139893,139804],{"class":1780},[62,139895,139896,139898,139900],{"class":64,"line":185},[62,139897,139809],{"class":72},[62,139899,677],{"class":1780},[62,139901,1784],{"class":72},[62,139903,139904,139906,139908,139910,139912,139915,139917,139919],{"class":64,"line":191},[62,139905,1896],{"class":72},[62,139907,677],{"class":1780},[62,139909,16494],{"class":122},[62,139911,146],{"class":72},[62,139913,139914],{"class":1675},"\"https://www.instagram.com\"",[62,139916,57441],{"class":122},[62,139918,146],{"class":72},[62,139920,139777],{"class":1675},[62,139922,139923,139925,139927,139929,139931,139934,139936],{"class":64,"line":209},[62,139924,139782],{"class":72},[62,139926,139785],{"class":1780},[62,139928,119],{"class":122},[62,139930,146],{"class":72},[62,139932,139933],{"class":1675},"\"fab fa-instagram fa-2x\"",[62,139935,15857],{"class":72},[62,139937,139804],{"class":1780},[62,139939,139940,139942,139944],{"class":64,"line":220},[62,139941,139809],{"class":72},[62,139943,677],{"class":1780},[62,139945,1784],{"class":72},[62,139947,139948,139950,139952,139954,139956,139959,139961,139963],{"class":64,"line":226},[62,139949,1896],{"class":72},[62,139951,677],{"class":1780},[62,139953,16494],{"class":122},[62,139955,146],{"class":72},[62,139957,139958],{"class":1675},"\"http://www.facebook.com\"",[62,139960,57441],{"class":122},[62,139962,146],{"class":72},[62,139964,139777],{"class":1675},[62,139966,139967,139969,139971,139973,139975,139978,139980],{"class":64,"line":231},[62,139968,139782],{"class":72},[62,139970,139785],{"class":1780},[62,139972,119],{"class":122},[62,139974,146],{"class":72},[62,139976,139977],{"class":1675},"\"fab fa-facebook-f fa-2x\"",[62,139979,15857],{"class":72},[62,139981,139804],{"class":1780},[62,139983,139984,139986,139988],{"class":64,"line":236},[62,139985,139809],{"class":72},[62,139987,677],{"class":1780},[62,139989,1784],{"class":72},[62,139991,139992,139994,139996],{"class":64,"line":242},[62,139993,92989],{"class":72},[62,139995,15944],{"class":1780},[62,139997,1784],{"class":72},[62,139999,140000,140002,140004],{"class":64,"line":247},[62,140001,1982],{"class":72},[62,140003,15944],{"class":1780},[62,140005,1784],{"class":72},[62,140007,140008,140010,140012,140014,140016,140019],{"class":64,"line":252},[62,140009,1789],{"class":72},[62,140011,15944],{"class":1780},[62,140013,119],{"class":122},[62,140015,146],{"class":72},[62,140017,140018],{"class":1675},"\"stats\"",[62,140020,1784],{"class":72},[62,140022,140023,140025,140027,140029,140031,140033],{"class":64,"line":257},[62,140024,92881],{"class":72},[62,140026,15944],{"class":1780},[62,140028,119],{"class":122},[62,140030,146],{"class":72},[62,140032,68226],{"class":1675},[62,140034,1784],{"class":72},[62,140036,140037,140039,140041,140043,140045],{"class":64,"line":271},[62,140038,1896],{"class":72},[62,140040,636],{"class":1780},[62,140042,15857],{"class":72},[62,140044,636],{"class":1780},[62,140046,1784],{"class":72},[62,140048,140049,140051,140053,140055,140057],{"class":64,"line":281},[62,140050,1896],{"class":72},[62,140052,62],{"class":1780},[62,140054,68472],{"class":72},[62,140056,62],{"class":1780},[62,140058,1784],{"class":72},[62,140060,140061,140063,140065],{"class":64,"line":286},[62,140062,92989],{"class":72},[62,140064,15944],{"class":1780},[62,140066,1784],{"class":72},[62,140068,140069,140071,140073,140075,140077,140080],{"class":64,"line":291},[62,140070,92881],{"class":72},[62,140072,15944],{"class":1780},[62,140074,119],{"class":122},[62,140076,146],{"class":72},[62,140078,140079],{"class":1675},"\"likes\"",[62,140081,1784],{"class":72},[62,140083,140084,140086,140088,140090,140092],{"class":64,"line":296},[62,140085,1896],{"class":72},[62,140087,636],{"class":1780},[62,140089,15857],{"class":72},[62,140091,636],{"class":1780},[62,140093,1784],{"class":72},[62,140095,140096,140098,140100,140103,140105],{"class":64,"line":302},[62,140097,1896],{"class":72},[62,140099,62],{"class":1780},[62,140101,140102],{"class":72},">Likes\u003C/",[62,140104,62],{"class":1780},[62,140106,1784],{"class":72},[62,140108,140109,140111,140113],{"class":64,"line":308},[62,140110,92989],{"class":72},[62,140112,15944],{"class":1780},[62,140114,1784],{"class":72},[62,140116,140117,140119,140121,140123,140125,140128],{"class":64,"line":314},[62,140118,92881],{"class":72},[62,140120,15944],{"class":1780},[62,140122,119],{"class":122},[62,140124,146],{"class":72},[62,140126,140127],{"class":1675},"\"followers\"",[62,140129,1784],{"class":72},[62,140131,140132,140134,140136,140138,140140],{"class":64,"line":320},[62,140133,1896],{"class":72},[62,140135,636],{"class":1780},[62,140137,15857],{"class":72},[62,140139,636],{"class":1780},[62,140141,1784],{"class":72},[62,140143,140144,140146,140148,140151,140153],{"class":64,"line":326},[62,140145,1896],{"class":72},[62,140147,62],{"class":1780},[62,140149,140150],{"class":72},">Followers\u003C/",[62,140152,62],{"class":1780},[62,140154,1784],{"class":72},[62,140156,140157,140159,140161],{"class":64,"line":338},[62,140158,92989],{"class":72},[62,140160,15944],{"class":1780},[62,140162,1784],{"class":72},[62,140164,140165,140167,140169],{"class":64,"line":343},[62,140166,1982],{"class":72},[62,140168,15944],{"class":1780},[62,140170,1784],{"class":72},[62,140172,140173,140175,140177],{"class":64,"line":357},[62,140174,33187],{"class":72},[62,140176,15944],{"class":1780},[62,140178,1784],{"class":72},[62,140180,140181,140183,140185],{"class":64,"line":366},[62,140182,1818],{"class":72},[62,140184,105991],{"class":1780},[62,140186,1784],{"class":72},[636,140188,105534],{"id":32793},[22,140190,140191,140192,140195],{},"Now that I have my markup its time we take a look at the JavaScript. I have a JSON file called ",[59,140193,140194],{},"users.json"," that has an array of 9 users that look like this.",[52,140197,140199],{"className":32791,"code":140198,"language":32793,"meta":57,"style":57},"{\n \"id\": 1,\n \"fullname\": \"Jonathan Stark\",\n \"title\": \"Software Developer\",\n \"avatar\": \"img/user_1.png\",\n \"social\": {\n \"github\": \"github_username\",\n \"reddit\": \"reddit_username\",\n \"twitter\": \"twitter_username\",\n \"instagram\": \"instagram_username\",\n \"facebook\": \"facebook_username\"\n },\n \"stats\": {\n \"posts\": \"150\",\n \"likes\": \"680\",\n \"followers\": \"199\"\n }\n}\n",[59,140200,140201,140205,140215,140227,140239,140251,140258,140270,140282,140294,140306,140316,140320,140327,140339,140351,140361,140365],{"__ignoreMap":57},[62,140202,140203],{"class":64,"line":65},[62,140204,3680],{"class":72},[62,140206,140207,140209,140211,140213],{"class":64,"line":76},[62,140208,85256],{"class":1675},[62,140210,3696],{"class":72},[62,140212,6689],{"class":149},[62,140214,3338],{"class":72},[62,140216,140217,140220,140222,140225],{"class":64,"line":82},[62,140218,140219],{"class":1675}," \"fullname\"",[62,140221,3696],{"class":72},[62,140223,140224],{"class":1675},"\"Jonathan Stark\"",[62,140226,3338],{"class":72},[62,140228,140229,140232,140234,140237],{"class":64,"line":89},[62,140230,140231],{"class":1675}," \"title\"",[62,140233,3696],{"class":72},[62,140235,140236],{"class":1675},"\"Software Developer\"",[62,140238,3338],{"class":72},[62,140240,140241,140244,140246,140249],{"class":64,"line":95},[62,140242,140243],{"class":1675}," \"avatar\"",[62,140245,3696],{"class":72},[62,140247,140248],{"class":1675},"\"img/user_1.png\"",[62,140250,3338],{"class":72},[62,140252,140253,140256],{"class":64,"line":101},[62,140254,140255],{"class":1675}," \"social\"",[62,140257,3688],{"class":72},[62,140259,140260,140263,140265,140268],{"class":64,"line":107},[62,140261,140262],{"class":1675}," \"github\"",[62,140264,3696],{"class":72},[62,140266,140267],{"class":1675},"\"github_username\"",[62,140269,3338],{"class":72},[62,140271,140272,140275,140277,140280],{"class":64,"line":113},[62,140273,140274],{"class":1675}," \"reddit\"",[62,140276,3696],{"class":72},[62,140278,140279],{"class":1675},"\"reddit_username\"",[62,140281,3338],{"class":72},[62,140283,140284,140287,140289,140292],{"class":64,"line":129},[62,140285,140286],{"class":1675}," \"twitter\"",[62,140288,3696],{"class":72},[62,140290,140291],{"class":1675},"\"twitter_username\"",[62,140293,3338],{"class":72},[62,140295,140296,140299,140301,140304],{"class":64,"line":134},[62,140297,140298],{"class":1675}," \"instagram\"",[62,140300,3696],{"class":72},[62,140302,140303],{"class":1675},"\"instagram_username\"",[62,140305,3338],{"class":72},[62,140307,140308,140311,140313],{"class":64,"line":156},[62,140309,140310],{"class":1675}," \"facebook\"",[62,140312,3696],{"class":72},[62,140314,140315],{"class":1675},"\"facebook_username\"\n",[62,140317,140318],{"class":64,"line":161},[62,140319,50124],{"class":72},[62,140321,140322,140325],{"class":64,"line":167},[62,140323,140324],{"class":1675}," \"stats\"",[62,140326,3688],{"class":72},[62,140328,140329,140332,140334,140337],{"class":64,"line":173},[62,140330,140331],{"class":1675}," \"posts\"",[62,140333,3696],{"class":72},[62,140335,140336],{"class":1675},"\"150\"",[62,140338,3338],{"class":72},[62,140340,140341,140344,140346,140349],{"class":64,"line":179},[62,140342,140343],{"class":1675}," \"likes\"",[62,140345,3696],{"class":72},[62,140347,140348],{"class":1675},"\"680\"",[62,140350,3338],{"class":72},[62,140352,140353,140356,140358],{"class":64,"line":185},[62,140354,140355],{"class":1675}," \"followers\"",[62,140357,3696],{"class":72},[62,140359,140360],{"class":1675},"\"199\"\n",[62,140362,140363],{"class":64,"line":191},[62,140364,223],{"class":72},[62,140366,140367],{"class":64,"line":209},[62,140368,379],{"class":72},[22,140370,140371],{},"The first step is to read the JSON in and to do so we will use the Fetch API. If you have used fetch before this is nothing new.",[52,140373,140375],{"className":32791,"code":140374,"language":32793,"meta":57,"style":57},"fetch(\"users.json\")\n .then(response => {\n return response.json();\n })\n .then(users => {\n // we have an array of users\n })\n .catch(err => console.error(err));\n",[59,140376,140377,140388,140402,140412,140416,140430,140435,140439],{"__ignoreMap":57},[62,140378,140379,140381,140383,140386],{"class":64,"line":65},[62,140380,113135],{"class":122},[62,140382,2109],{"class":72},[62,140384,140385],{"class":1675},"\"users.json\"",[62,140387,2212],{"class":72},[62,140389,140390,140392,140394,140396,140398,140400],{"class":64,"line":76},[62,140391,111267],{"class":72},[62,140393,36912],{"class":122},[62,140395,2109],{"class":72},[62,140397,13389],{"class":889},[62,140399,85402],{"class":68},[62,140401,126],{"class":72},[62,140403,140404,140406,140408,140410],{"class":64,"line":82},[62,140405,2599],{"class":68},[62,140407,12760],{"class":72},[62,140409,3673],{"class":122},[62,140411,822],{"class":72},[62,140413,140414],{"class":64,"line":89},[62,140415,120831],{"class":72},[62,140417,140418,140420,140422,140424,140426,140428],{"class":64,"line":95},[62,140419,111267],{"class":72},[62,140421,36912],{"class":122},[62,140423,2109],{"class":72},[62,140425,96112],{"class":889},[62,140427,85402],{"class":68},[62,140429,126],{"class":72},[62,140431,140432],{"class":64,"line":101},[62,140433,140434],{"class":85}," // we have an array of users\n",[62,140436,140437],{"class":64,"line":107},[62,140438,120831],{"class":72},[62,140440,140441,140443,140445,140447,140449,140451,140453,140455],{"class":64,"line":113},[62,140442,111267],{"class":72},[62,140444,883],{"class":122},[62,140446,2109],{"class":72},[62,140448,107309],{"class":889},[62,140450,85402],{"class":68},[62,140452,58268],{"class":72},[62,140454,21875],{"class":122},[62,140456,107320],{"class":72},[22,140458,140459,140460,140465],{},"Now that we have an array of users we can begin to work with our template. First, we need to check to see if the user's browser supports the HTML Content Template tag. As long as you're using a modern browser ",[677,140461,140464],{"href":140462,"rel":140463},"https://caniuse.com/#feat=template",[681],"it should support it"," but it is best practice to make this check.",[52,140467,140469],{"className":32791,"code":140468,"language":32793,"meta":57,"style":57},"if('content' in document.createElement('template')) {\n\n});\n} else {\n console.error('Your browser does not support templates');\n}\n",[59,140470,140471,140494,140498,140502,140510,140524],{"__ignoreMap":57},[62,140472,140473,140475,140477,140480,140482,140484,140486,140488,140491],{"class":64,"line":65},[62,140474,34116],{"class":68},[62,140476,2109],{"class":72},[62,140478,140479],{"class":1675},"'content'",[62,140481,57031],{"class":68},[62,140483,21445],{"class":72},[62,140485,21611],{"class":122},[62,140487,2109],{"class":72},[62,140489,140490],{"class":1675},"'template'",[62,140492,140493],{"class":72},")) {\n",[62,140495,140496],{"class":64,"line":76},[62,140497,79],{"emptyLinePlaceholder":13},[62,140499,140500],{"class":64,"line":82},[62,140501,85531],{"class":72},[62,140503,140504,140506,140508],{"class":64,"line":89},[62,140505,63241],{"class":72},[62,140507,12783],{"class":68},[62,140509,126],{"class":72},[62,140511,140512,140515,140517,140519,140522],{"class":64,"line":95},[62,140513,140514],{"class":72}," console.",[62,140516,21875],{"class":122},[62,140518,2109],{"class":72},[62,140520,140521],{"class":1675},"'Your browser does not support templates'",[62,140523,1133],{"class":72},[62,140525,140526],{"class":64,"line":101},[62,140527,379],{"class":72},[22,140529,140530],{},"Now that we know the browser supports this feature we need to get a reference to the parent container that we will be appending each user card to and in this case it's the element with the id of users. We will then iterate over each element in our array.",[52,140532,140534],{"className":32791,"code":140533,"language":32793,"meta":57,"style":57},"if (\"content\" in document.createElement(\"template\")) {\n const container = document.getElementById(\"users\");\n users.forEach(user => {});\n} else {\n console.error(\"Your browser does not support templates\");\n}\n",[59,140535,140536,140557,140576,140592,140600,140613],{"__ignoreMap":57},[62,140537,140538,140540,140542,140544,140546,140548,140550,140552,140555],{"class":64,"line":65},[62,140539,34116],{"class":68},[62,140541,744],{"class":72},[62,140543,123479],{"class":1675},[62,140545,57031],{"class":68},[62,140547,21445],{"class":72},[62,140549,21611],{"class":122},[62,140551,2109],{"class":72},[62,140553,140554],{"class":1675},"\"template\"",[62,140556,140493],{"class":72},[62,140558,140559,140561,140564,140566,140568,140570,140572,140574],{"class":64,"line":76},[62,140560,85414],{"class":68},[62,140562,140563],{"class":149}," container",[62,140565,2556],{"class":68},[62,140567,21445],{"class":72},[62,140569,21448],{"class":122},[62,140571,2109],{"class":72},[62,140573,22278],{"class":1675},[62,140575,1133],{"class":72},[62,140577,140578,140581,140583,140585,140587,140589],{"class":64,"line":82},[62,140579,140580],{"class":72}," users.",[62,140582,4215],{"class":122},[62,140584,2109],{"class":72},[62,140586,2502],{"class":889},[62,140588,85402],{"class":68},[62,140590,140591],{"class":72}," {});\n",[62,140593,140594,140596,140598],{"class":64,"line":89},[62,140595,63241],{"class":72},[62,140597,12783],{"class":68},[62,140599,126],{"class":72},[62,140601,140602,140604,140606,140608,140611],{"class":64,"line":95},[62,140603,130928],{"class":72},[62,140605,21875],{"class":122},[62,140607,2109],{"class":72},[62,140609,140610],{"class":1675},"\"Your browser does not support templates\"",[62,140612,1133],{"class":72},[62,140614,140615],{"class":64,"line":101},[62,140616,379],{"class":72},[22,140618,140619],{},"During each iteration of our user's array, we are going to create a copy (clone) of our template. The way that we do that is by getting a reference to the element, getting the content (what's inside the template tag) and then cloning it. We are passing true to the cloneNode method so that we use a deep clone and grab all of the children with it.",[52,140621,140623],{"className":32791,"code":140622,"language":32793,"meta":57,"style":57},"const tmpl = document\n .getElementById(\"user-card-template\")\n .content.cloneNode(true);\n",[59,140624,140625,140637,140649],{"__ignoreMap":57},[62,140626,140627,140629,140632,140634],{"class":64,"line":65},[62,140628,110541],{"class":68},[62,140630,140631],{"class":149}," tmpl",[62,140633,2556],{"class":68},[62,140635,140636],{"class":72}," document\n",[62,140638,140639,140641,140643,140645,140647],{"class":64,"line":76},[62,140640,111267],{"class":72},[62,140642,21448],{"class":122},[62,140644,2109],{"class":72},[62,140646,139660],{"class":1675},[62,140648,2212],{"class":72},[62,140650,140651,140654,140657,140659,140661],{"class":64,"line":82},[62,140652,140653],{"class":72}," .content.",[62,140655,140656],{"class":122},"cloneNode",[62,140658,2109],{"class":72},[62,140660,21775],{"class":149},[62,140662,1133],{"class":72},[22,140664,140665],{},"From there we can simply query the template for a specific element and set its content to the value we are reading in from the user array. In most cases, I am just setting the inner text of the element. Finally, we use the reference to our container element and append everything that was inside the template tag to our page.",[52,140667,140669],{"className":32791,"code":140668,"language":32793,"meta":57,"style":57},"fetch(\"users.json\")\n .then(response => {\n return response.json();\n })\n .then(users => {\n if (\"content\" in document.createElement(\"template\")) {\n const container = document.getElementById(\"users\");\n users.forEach(user => {\n const tmpl = document\n .getElementById(\"user-card-template\")\n .content.cloneNode(true);\n tmpl.querySelector(\"h2\").innerText = user.fullname;\n tmpl.querySelector(\".title\").innerText = user.title;\n tmpl.querySelector(\"img\").setAttribute(\"src\", user.avatar);\n tmpl.querySelector(\".posts h3\").innerText = user.stats.posts;\n tmpl.querySelector(\".likes h3\").innerText = user.stats.likes;\n tmpl.querySelector(\".followers h3\").innerText = user.stats.followers;\n container.appendChild(tmpl);\n });\n } else {\n console.error(\"Your browser does not support templates\");\n }\n })\n .catch(err => console.error(err));\n",[59,140670,140671,140681,140695,140705,140709,140723,140743,140761,140776,140786,140799,140812,140833,140851,140873,140891,140909,140927,140937,140941,140949,140962,140966,140970],{"__ignoreMap":57},[62,140672,140673,140675,140677,140679],{"class":64,"line":65},[62,140674,113135],{"class":122},[62,140676,2109],{"class":72},[62,140678,140385],{"class":1675},[62,140680,2212],{"class":72},[62,140682,140683,140685,140687,140689,140691,140693],{"class":64,"line":76},[62,140684,111267],{"class":72},[62,140686,36912],{"class":122},[62,140688,2109],{"class":72},[62,140690,13389],{"class":889},[62,140692,85402],{"class":68},[62,140694,126],{"class":72},[62,140696,140697,140699,140701,140703],{"class":64,"line":82},[62,140698,2599],{"class":68},[62,140700,12760],{"class":72},[62,140702,3673],{"class":122},[62,140704,822],{"class":72},[62,140706,140707],{"class":64,"line":89},[62,140708,120831],{"class":72},[62,140710,140711,140713,140715,140717,140719,140721],{"class":64,"line":95},[62,140712,111267],{"class":72},[62,140714,36912],{"class":122},[62,140716,2109],{"class":72},[62,140718,96112],{"class":889},[62,140720,85402],{"class":68},[62,140722,126],{"class":72},[62,140724,140725,140727,140729,140731,140733,140735,140737,140739,140741],{"class":64,"line":101},[62,140726,14187],{"class":68},[62,140728,744],{"class":72},[62,140730,123479],{"class":1675},[62,140732,57031],{"class":68},[62,140734,21445],{"class":72},[62,140736,21611],{"class":122},[62,140738,2109],{"class":72},[62,140740,140554],{"class":1675},[62,140742,140493],{"class":72},[62,140744,140745,140747,140749,140751,140753,140755,140757,140759],{"class":64,"line":107},[62,140746,115079],{"class":68},[62,140748,140563],{"class":149},[62,140750,2556],{"class":68},[62,140752,21445],{"class":72},[62,140754,21448],{"class":122},[62,140756,2109],{"class":72},[62,140758,22278],{"class":1675},[62,140760,1133],{"class":72},[62,140762,140763,140766,140768,140770,140772,140774],{"class":64,"line":113},[62,140764,140765],{"class":72}," users.",[62,140767,4215],{"class":122},[62,140769,2109],{"class":72},[62,140771,2502],{"class":889},[62,140773,85402],{"class":68},[62,140775,126],{"class":72},[62,140777,140778,140780,140782,140784],{"class":64,"line":129},[62,140779,21437],{"class":68},[62,140781,140631],{"class":149},[62,140783,2556],{"class":68},[62,140785,140636],{"class":72},[62,140787,140788,140791,140793,140795,140797],{"class":64,"line":134},[62,140789,140790],{"class":72}," .",[62,140792,21448],{"class":122},[62,140794,2109],{"class":72},[62,140796,139660],{"class":1675},[62,140798,2212],{"class":72},[62,140800,140801,140804,140806,140808,140810],{"class":64,"line":156},[62,140802,140803],{"class":72}," .content.",[62,140805,140656],{"class":122},[62,140807,2109],{"class":72},[62,140809,21775],{"class":149},[62,140811,1133],{"class":72},[62,140813,140814,140817,140820,140822,140825,140828,140830],{"class":64,"line":161},[62,140815,140816],{"class":72}," tmpl.",[62,140818,140819],{"class":122},"querySelector",[62,140821,2109],{"class":72},[62,140823,140824],{"class":1675},"\"h2\"",[62,140826,140827],{"class":72},").innerText ",[62,140829,146],{"class":68},[62,140831,140832],{"class":72}," user.fullname;\n",[62,140834,140835,140837,140839,140841,140844,140846,140848],{"class":64,"line":167},[62,140836,140816],{"class":72},[62,140838,140819],{"class":122},[62,140840,2109],{"class":72},[62,140842,140843],{"class":1675},"\".title\"",[62,140845,140827],{"class":72},[62,140847,146],{"class":68},[62,140849,140850],{"class":72}," user.title;\n",[62,140852,140853,140855,140857,140859,140862,140864,140866,140868,140870],{"class":64,"line":173},[62,140854,140816],{"class":72},[62,140856,140819],{"class":122},[62,140858,2109],{"class":72},[62,140860,140861],{"class":1675},"\"img\"",[62,140863,15503],{"class":72},[62,140865,79209],{"class":122},[62,140867,2109],{"class":72},[62,140869,49867],{"class":1675},[62,140871,140872],{"class":72},", user.avatar);\n",[62,140874,140875,140877,140879,140881,140884,140886,140888],{"class":64,"line":179},[62,140876,140816],{"class":72},[62,140878,140819],{"class":122},[62,140880,2109],{"class":72},[62,140882,140883],{"class":1675},"\".posts h3\"",[62,140885,140827],{"class":72},[62,140887,146],{"class":68},[62,140889,140890],{"class":72}," user.stats.posts;\n",[62,140892,140893,140895,140897,140899,140902,140904,140906],{"class":64,"line":185},[62,140894,140816],{"class":72},[62,140896,140819],{"class":122},[62,140898,2109],{"class":72},[62,140900,140901],{"class":1675},"\".likes h3\"",[62,140903,140827],{"class":72},[62,140905,146],{"class":68},[62,140907,140908],{"class":72}," user.stats.likes;\n",[62,140910,140911,140913,140915,140917,140920,140922,140924],{"class":64,"line":191},[62,140912,140816],{"class":72},[62,140914,140819],{"class":122},[62,140916,2109],{"class":72},[62,140918,140919],{"class":1675},"\".followers h3\"",[62,140921,140827],{"class":72},[62,140923,146],{"class":68},[62,140925,140926],{"class":72}," user.stats.followers;\n",[62,140928,140929,140932,140934],{"class":64,"line":209},[62,140930,140931],{"class":72}," container.",[62,140933,21670],{"class":122},[62,140935,140936],{"class":72},"(tmpl);\n",[62,140938,140939],{"class":64,"line":220},[62,140940,106288],{"class":72},[62,140942,140943,140945,140947],{"class":64,"line":226},[62,140944,121127],{"class":72},[62,140946,12783],{"class":68},[62,140948,126],{"class":72},[62,140950,140951,140954,140956,140958,140960],{"class":64,"line":231},[62,140952,140953],{"class":72}," console.",[62,140955,21875],{"class":122},[62,140957,2109],{"class":72},[62,140959,140610],{"class":1675},[62,140961,1133],{"class":72},[62,140963,140964],{"class":64,"line":236},[62,140965,223],{"class":72},[62,140967,140968],{"class":64,"line":242},[62,140969,120831],{"class":72},[62,140971,140972,140974,140976,140978,140980,140982,140984,140986],{"class":64,"line":247},[62,140973,111267],{"class":72},[62,140975,883],{"class":122},[62,140977,2109],{"class":72},[62,140979,107309],{"class":889},[62,140981,85402],{"class":68},[62,140983,58268],{"class":72},[62,140985,21875],{"class":122},[62,140987,107320],{"class":72},[636,140989,140991],{"id":140990},"conditionals","Conditionals",[22,140993,140994],{},"After writing this article my friend Todd asked me a really good question.",[22,140996,140997],{},[677,140998,140999],{"href":140999,"rel":141000},"https://twitter.com/recursivecodes/status/1089184943673090048",[681],[22,141002,141003],{},"What we have done here is cloned the markup that is inside of the template tag. Because this is normal markup we can do whatever we want with it. So let's say that some users have an account on all of the social networks and some don't.",[52,141005,141007],{"className":32791,"code":141006,"language":32793,"meta":57,"style":57},"\"social\": {\n \"github\": \"github_username\",\n \"reddit\": \"reddit_username\",\n \"twitter\": \"twitter_username\",\n \"instagram\": \"instagram_username\",\n \"facebook\": \"facebook_username\"\n}\n",[59,141008,141009,141015,141026,141037,141048,141059,141068],{"__ignoreMap":57},[62,141010,141011,141013],{"class":64,"line":65},[62,141012,139755],{"class":1675},[62,141014,3688],{"class":72},[62,141016,141017,141020,141022,141024],{"class":64,"line":76},[62,141018,141019],{"class":1675}," \"github\"",[62,141021,3696],{"class":72},[62,141023,140267],{"class":1675},[62,141025,3338],{"class":72},[62,141027,141028,141031,141033,141035],{"class":64,"line":82},[62,141029,141030],{"class":1675}," \"reddit\"",[62,141032,3696],{"class":72},[62,141034,140279],{"class":1675},[62,141036,3338],{"class":72},[62,141038,141039,141042,141044,141046],{"class":64,"line":89},[62,141040,141041],{"class":1675}," \"twitter\"",[62,141043,3696],{"class":72},[62,141045,140291],{"class":1675},[62,141047,3338],{"class":72},[62,141049,141050,141053,141055,141057],{"class":64,"line":95},[62,141051,141052],{"class":1675}," \"instagram\"",[62,141054,3696],{"class":72},[62,141056,140303],{"class":1675},[62,141058,3338],{"class":72},[62,141060,141061,141064,141066],{"class":64,"line":101},[62,141062,141063],{"class":1675}," \"facebook\"",[62,141065,3696],{"class":72},[62,141067,140315],{"class":1675},[62,141069,141070],{"class":64,"line":107},[62,141071,379],{"class":72},[52,141073,141075],{"className":32791,"code":141074,"language":32793,"meta":57,"style":57},"\"social\": {\n \"github\": \"github_username\",\n \"reddit\": \"reddit_username\",\n \"twitter\": \"twitter_username\"\n}\n",[59,141076,141077,141083,141093,141103,141112],{"__ignoreMap":57},[62,141078,141079,141081],{"class":64,"line":65},[62,141080,139755],{"class":1675},[62,141082,3688],{"class":72},[62,141084,141085,141087,141089,141091],{"class":64,"line":76},[62,141086,141019],{"class":1675},[62,141088,3696],{"class":72},[62,141090,140267],{"class":1675},[62,141092,3338],{"class":72},[62,141094,141095,141097,141099,141101],{"class":64,"line":82},[62,141096,141030],{"class":1675},[62,141098,3696],{"class":72},[62,141100,140279],{"class":1675},[62,141102,3338],{"class":72},[62,141104,141105,141107,141109],{"class":64,"line":89},[62,141106,141041],{"class":1675},[62,141108,3696],{"class":72},[62,141110,141111],{"class":1675},"\"twitter_username\"\n",[62,141113,141114],{"class":64,"line":95},[62,141115,379],{"class":72},[22,141117,141118],{},"What we could do here is iterate over all the know social networks that we support and if the users.social object doesn't have that key then we will remove that element from the DOM. Again we are working with normal elements here so we can do things like set the visibility to hidden or remove them completely. In this case, we want to remove it because if just hide it then we will have this empty space in certain cases and that doesn't look good.",[52,141120,141122],{"className":32791,"code":141121,"language":32793,"meta":57,"style":57},"// this is a list of social networks we display under a users profile\nconst socialLinks = [\"github\", \"reddit\", \"twitter\", \"instagram\", \"facebook\"];\n// iterate over that list and check to see if they have an account on that network\nsocialLinks.forEach(social => {\n // if they don't have a link in the JSON data hide that link & icon\n if (!user.social.hasOwnProperty(social)) {\n tmpl.querySelector(`.${social}`).remove();\n }\n});\n",[59,141123,141124,141129,141165,141170,141186,141191,141208,141230,141234],{"__ignoreMap":57},[62,141125,141126],{"class":64,"line":65},[62,141127,141128],{"class":85},"// this is a list of social networks we display under a users profile\n",[62,141130,141131,141133,141136,141138,141140,141143,141145,141148,141150,141153,141155,141158,141160,141163],{"class":64,"line":76},[62,141132,110541],{"class":68},[62,141134,141135],{"class":149}," socialLinks",[62,141137,2556],{"class":68},[62,141139,36860],{"class":72},[62,141141,141142],{"class":1675},"\"github\"",[62,141144,976],{"class":72},[62,141146,141147],{"class":1675},"\"reddit\"",[62,141149,976],{"class":72},[62,141151,141152],{"class":1675},"\"twitter\"",[62,141154,976],{"class":72},[62,141156,141157],{"class":1675},"\"instagram\"",[62,141159,976],{"class":72},[62,141161,141162],{"class":1675},"\"facebook\"",[62,141164,109215],{"class":72},[62,141166,141167],{"class":64,"line":82},[62,141168,141169],{"class":85},"// iterate over that list and check to see if they have an account on that network\n",[62,141171,141172,141175,141177,141179,141182,141184],{"class":64,"line":89},[62,141173,141174],{"class":72},"socialLinks.",[62,141176,4215],{"class":122},[62,141178,2109],{"class":72},[62,141180,141181],{"class":889},"social",[62,141183,85402],{"class":68},[62,141185,126],{"class":72},[62,141187,141188],{"class":64,"line":95},[62,141189,141190],{"class":85}," // if they don't have a link in the JSON data hide that link & icon\n",[62,141192,141193,141195,141197,141199,141202,141205],{"class":64,"line":101},[62,141194,107558],{"class":68},[62,141196,744],{"class":72},[62,141198,6277],{"class":68},[62,141200,141201],{"class":72},"user.social.",[62,141203,141204],{"class":122},"hasOwnProperty",[62,141206,141207],{"class":72},"(social)) {\n",[62,141209,141210,141213,141215,141217,141220,141222,141224,141226,141228],{"class":64,"line":107},[62,141211,141212],{"class":72}," tmpl.",[62,141214,140819],{"class":122},[62,141216,2109],{"class":72},[62,141218,141219],{"class":1675},"`.${",[62,141221,141181],{"class":72},[62,141223,21727],{"class":1675},[62,141225,15503],{"class":72},[62,141227,23761],{"class":122},[62,141229,822],{"class":72},[62,141231,141232],{"class":64,"line":113},[62,141233,3731],{"class":72},[62,141235,141236],{"class":64,"line":129},[62,141237,85531],{"class":72},[636,141239,141241],{"id":141240},"html-template-in-vanilla-javascript-wrapup","HTML Template in Vanilla JavaScript Wrapup",[22,141243,141244],{},"And that is really all you need to create a template in markup, clone it and add data to it. I will mention this because it is important to know but if you take this approach and view source you will only see the template code. This means that if you have data that needs to be written to the page that needs to be search engine friendly this probably isn't a good solution.",[26,141246,141248],{"id":141247},"template-tag-in-vue","Template Tag in Vue",[22,141250,141251],{},"Now that we know what the template tag is it should make a little more sense what Vue is using this for. If you create a new Single File Component in Vue you will have some code that looks like this.",[52,141253,141255],{"className":15773,"code":141254,"language":15775,"meta":57,"style":57},"\u003Ctemplate>\n \u003Cdiv id=\"users\">\n \u003C!-- markup here -->\n \u003C/div>\n\u003C/template>\n\n\u003Cscript>\n // js here\n\u003C/script>\n\n\u003Cstyle>\n /* css here */\n\u003C/style>\n",[59,141256,141257,141265,141279,141284,141292,141300,141304,141312,141317,141325,141329,141337,141342],{"__ignoreMap":57},[62,141258,141259,141261,141263],{"class":64,"line":65},[62,141260,760],{"class":72},[62,141262,105991],{"class":1780},[62,141264,1784],{"class":72},[62,141266,141267,141269,141271,141273,141275,141277],{"class":64,"line":76},[62,141268,33056],{"class":72},[62,141270,15944],{"class":1780},[62,141272,20831],{"class":122},[62,141274,146],{"class":72},[62,141276,22278],{"class":1675},[62,141278,1784],{"class":72},[62,141280,141281],{"class":64,"line":82},[62,141282,141283],{"class":85}," \u003C!-- markup here -->\n",[62,141285,141286,141288,141290],{"class":64,"line":89},[62,141287,33187],{"class":72},[62,141289,15944],{"class":1780},[62,141291,1784],{"class":72},[62,141293,141294,141296,141298],{"class":64,"line":95},[62,141295,1818],{"class":72},[62,141297,105991],{"class":1780},[62,141299,1784],{"class":72},[62,141301,141302],{"class":64,"line":101},[62,141303,79],{"emptyLinePlaceholder":13},[62,141305,141306,141308,141310],{"class":64,"line":107},[62,141307,760],{"class":72},[62,141309,15846],{"class":1780},[62,141311,1784],{"class":72},[62,141313,141314],{"class":64,"line":113},[62,141315,141316],{"class":85}," // js here\n",[62,141318,141319,141321,141323],{"class":64,"line":129},[62,141320,1818],{"class":72},[62,141322,15846],{"class":1780},[62,141324,1784],{"class":72},[62,141326,141327],{"class":64,"line":134},[62,141328,79],{"emptyLinePlaceholder":13},[62,141330,141331,141333,141335],{"class":64,"line":156},[62,141332,760],{"class":72},[62,141334,1527],{"class":1780},[62,141336,1784],{"class":72},[62,141338,141339],{"class":64,"line":161},[62,141340,141341],{"class":85}," /* css here */\n",[62,141343,141344,141346,141348],{"class":64,"line":167},[62,141345,1818],{"class":72},[62,141347,1527],{"class":1780},[62,141349,1784],{"class":72},[22,141351,141352],{},"It should be really clear why we need a top-level element inside of the template tag now. Vue is going to compile everything inside of the template tag into the Virtual DOM. The Vue documentation describes the template syntax as:",[29685,141354,141355,141358,141361],{},[22,141356,141357],{},"Vue.js uses an HTML-based template syntax that allows you to declaratively bind the rendered DOM to the underlying Vue instance’s data. All Vue.js templates are valid HTML that can be parsed by spec-compliant browsers and HTML parsers.",[22,141359,141360],{},"Under the hood, Vue compiles the templates into Virtual DOM render functions. Combined with the reactivity system, Vue is able to intelligently figure out the minimal number of components to re-render and apply the minimal amount of DOM manipulations when the app state changes.",[22,141362,141363],{},"If you are familiar with Virtual DOM concepts and prefer the raw power of JavaScript, you can also directly write render functions instead of templates, with optional JSX support.",[26,141365,1499],{"id":1498},[22,141367,141368],{},"If you have never used the template tag before I hope you learned something new today. Please feel free to ask any questions about this article or the demo I built.",[1527,141370,141371],{},"html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .suaqH, html code.shiki .suaqH{--shiki-default:#22863A;--shiki-dark:#85E89D;--shiki-sepia:#22863A}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}html pre.shiki code .spoOn, html code.shiki .spoOn{--shiki-default:#E36209;--shiki-dark:#FFAB70;--shiki-sepia:#E36209}html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .s4-Ip, html code.shiki .s4-Ip{--shiki-default:#6A737D;--shiki-dark:#6A737D;--shiki-sepia:#6A737D}",{"title":57,"searchDepth":76,"depth":76,"links":141373},[141374,141375,141381,141382],{"id":139600,"depth":76,"text":139589},{"id":139617,"depth":76,"text":139618,"children":141376},[141377,141378,141379,141380],{"id":139635,"depth":82,"text":139636},{"id":32793,"depth":82,"text":105534},{"id":140990,"depth":82,"text":140991},{"id":141240,"depth":82,"text":141241},{"id":141247,"depth":76,"text":141248},{"id":1498,"depth":76,"text":1499},{"_id":141384,"path":141385,"title":128512,"description":141386,"meta":141387,"body":141392},"content/blog/2019/02/10/creating-your-first-npm-package.md","/blog/2019/02/10/creating-your-first-npm-package","How to create your first npm package and publish it.",{"slug":141388,"date":141389,"published":13,"tags":141390,"author":17,"cover":141391,"excerpt":-1},"creating-your-first-npm-package","2019-02-10 11:00:00",[32634,32645,32793],"./npm_cover.png",{"type":19,"value":141393,"toc":143037},[141394,141403,141409,141412,141414,141417,141420,141441,141445,141452,141455,141466,141469,141930,141934,141937,141978,141981,141985,141988,141991,142118,142122,142125,142139,142142,142156,142159,142287,142300,142304,142307,142319,142322,142327,142330,142343,142349,142352,142416,142419,142428,142431,142450,142454,142457,142512,142515,142654,142658,142661,142673,142676,142738,142745,142748,142759,142762,142962,142965,142977,142985,142988,142991,143003,143006,143008,143011,143025,143027,143030,143034],[22,141395,141396,141397,141402],{},"This weekend I started working on my first ever npm package. I can't believe for how long I have been writing code that I never bothered to create my own npm package but here we are. I ",[677,141398,141401],{"href":141399,"rel":141400},"https://www.danvega.me/blog/hello-gridsome",[681],"built the site"," you're reading this article on using Gridsome and markdown. In the markdown files I wanted an easy way to insert a twitter status nad embed the tweet. In fact here is a tweet just to prove its working.",[22,141404,141405],{},[677,141406,141407],{"href":141407,"rel":141408},"https://twitter.com/therealdanvega/status/1094219965480292353",[681],[22,141410,141411],{},"I will tell you more about that Gridsome plugin in a future blog post but for now I want to show you how you can create your very first npm package. I learned a few things while working on this and I would like to share them with you.",[26,141413,2874],{"id":2873},[22,141415,141416],{},"I am going to assume you at least know what node & npm is and have written JavaScript before. If you don't know either of these and want me to write something up on getting started with those please let me know.",[22,141418,141419],{},"There are a few things that you're going to need before we dive in and start writing some code.",[915,141421,141422,141429,141435],{},[37,141423,141424,141428],{},[677,141425,125822],{"href":141426,"rel":141427},"https://code.visualstudio.com/",[681]," or your favorite editor",[37,141430,141431],{},[677,141432,141434],{"href":51558,"rel":141433},[681],"Node & NPM",[37,141436,141437],{},[677,141438,141440],{"href":51563,"rel":141439},[681],"NPM Account",[26,141442,141444],{"id":141443},"creating-your-npm-package","Creating your npm package",[22,141446,141447,141448,141451],{},"The first thing you are going to do is create a new folder to hold your npm package. For this example I am going to create a new directory called ",[646,141449,141450],{},"wrap-with-poo",". Yes, you read that correctly.",[22,141453,141454],{},"Go into that folder and type the following:",[52,141456,141458],{"className":1663,"code":141457,"language":1665,"meta":57,"style":57},"npm init\n",[59,141459,141460],{"__ignoreMap":57},[62,141461,141462,141464],{"class":64,"line":65},[62,141463,32645],{"class":122},[62,141465,32775],{"class":1675},[22,141467,141468],{},"This will ask you a bunch of questions and then create a package.json. If you don't know answers to certain questions just yet don't worry, you can come back and answer them later.",[52,141470,141472],{"className":1663,"code":141471,"language":1665,"meta":57,"style":57},"This utility will walk you through creating a package.json file.\nIt only covers the most common items, and tries to guess sensible defaults.\n\nSee `npm help json` for definitive documentation on these fields\nand exactly what they do.\n\nUse `npm install \u003Cpkg>` afterwards to install a package and\nsave it as a dependency in the package.json file.\n\nPress ^C at any time to quit.\npackage name: (wrap-with-poop)\nversion: (1.0.0) 0.0.1\ndescription: This package will take any string you give it and wrap it with the poop emjoi\nentry point: (index.js)\ntest command:\ngit repository:\nkeywords: node,npm\nauthor: Dan Vega\nlicense: (ISC) MIT\nAbout to write to /Users/vega/dev/npm/wrap-with-poop/package.json:\n\n{\n \"name\": \"wrap-with-poop\",\n \"version\": \"0.0.1\",\n \"description\": \"This package will take any string you give it and wrap it with the poop emjoi\",\n \"main\": \"index.js\",\n \"scripts\": {\n \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n },\n \"keywords\": [\n \"node\",\n \"npm\"\n ],\n \"author\": \"Dan Vega\",\n \"license\": \"MIT\"\n}\n\n\nIs this OK? (yes) yes\n",[59,141473,141474,141503,141541,141545,141562,141578,141582,141618,141640,141644,141665,141675,141683,141725,141735,141742,141749,141757,141768,141776,141790,141794,141798,141807,141816,141825,141834,141842,141859,141863,141871,141878,141883,141887,141896,141905,141909,141913,141917],{"__ignoreMap":57},[62,141475,141476,141479,141482,141484,141487,141489,141492,141495,141497,141500],{"class":64,"line":65},[62,141477,141478],{"class":122},"This",[62,141480,141481],{"class":1675}," utility",[62,141483,50380],{"class":1675},[62,141485,141486],{"class":1675}," walk",[62,141488,50362],{"class":1675},[62,141490,141491],{"class":1675}," through",[62,141493,141494],{"class":1675}," creating",[62,141496,28870],{"class":1675},[62,141498,141499],{"class":1675}," package.json",[62,141501,141502],{"class":1675}," file.\n",[62,141504,141505,141508,141511,141514,141516,141519,141522,141525,141527,141530,141532,141535,141538],{"class":64,"line":76},[62,141506,141507],{"class":122},"It",[62,141509,141510],{"class":1675}," only",[62,141512,141513],{"class":1675}," covers",[62,141515,53632],{"class":1675},[62,141517,141518],{"class":1675}," most",[62,141520,141521],{"class":1675}," common",[62,141523,141524],{"class":1675}," items,",[62,141526,50404],{"class":1675},[62,141528,141529],{"class":1675}," tries",[62,141531,57317],{"class":1675},[62,141533,141534],{"class":1675}," guess",[62,141536,141537],{"class":1675}," sensible",[62,141539,141540],{"class":1675}," defaults.\n",[62,141542,141543],{"class":64,"line":82},[62,141544,79],{"emptyLinePlaceholder":13},[62,141546,141547,141550,141552,141554,141557,141559],{"class":64,"line":89},[62,141548,141549],{"class":122},"See",[62,141551,125118],{"class":1675},[62,141553,32645],{"class":122},[62,141555,141556],{"class":1675}," help json`",[62,141558,53629],{"class":68},[62,141560,141561],{"class":72}," definitive documentation on these fields\n",[62,141563,141564,141566,141569,141572,141575],{"class":64,"line":95},[62,141565,84694],{"class":122},[62,141567,141568],{"class":1675}," exactly",[62,141570,141571],{"class":1675}," what",[62,141573,141574],{"class":1675}," they",[62,141576,141577],{"class":1675}," do.\n",[62,141579,141580],{"class":64,"line":101},[62,141581,79],{"emptyLinePlaceholder":13},[62,141583,141584,141587,141589,141591,141594,141596,141599,141601,141603,141606,141608,141610,141612,141615],{"class":64,"line":107},[62,141585,141586],{"class":122},"Use",[62,141588,125118],{"class":1675},[62,141590,32645],{"class":122},[62,141592,141593],{"class":1675}," install ",[62,141595,760],{"class":68},[62,141597,141598],{"class":1675},"pkg",[62,141600,2583],{"class":68},[62,141602,31872],{"class":1675},[62,141604,141605],{"class":122}," afterwards",[62,141607,57317],{"class":1675},[62,141609,32750],{"class":1675},[62,141611,28870],{"class":1675},[62,141613,141614],{"class":1675}," package",[62,141616,141617],{"class":1675}," and\n",[62,141619,141620,141622,141625,141627,141629,141632,141634,141636,141638],{"class":64,"line":113},[62,141621,22562],{"class":122},[62,141623,141624],{"class":1675}," it",[62,141626,53662],{"class":1675},[62,141628,28870],{"class":1675},[62,141630,141631],{"class":1675}," dependency",[62,141633,57031],{"class":1675},[62,141635,53632],{"class":1675},[62,141637,141499],{"class":1675},[62,141639,141502],{"class":1675},[62,141641,141642],{"class":64,"line":129},[62,141643,79],{"emptyLinePlaceholder":13},[62,141645,141646,141649,141652,141654,141657,141660,141662],{"class":64,"line":134},[62,141647,141648],{"class":122},"Press",[62,141650,141651],{"class":1675}," ^C",[62,141653,125157],{"class":1675},[62,141655,141656],{"class":1675}," any",[62,141658,141659],{"class":1675}," time",[62,141661,57317],{"class":1675},[62,141663,141664],{"class":1675}," quit.\n",[62,141666,141667,141669,141672],{"class":64,"line":156},[62,141668,69],{"class":122},[62,141670,141671],{"class":1675}," name:",[62,141673,141674],{"class":72}," (wrap-with-poop)\n",[62,141676,141677,141680],{"class":64,"line":161},[62,141678,141679],{"class":122},"version:",[62,141681,141682],{"class":72}," (1.0.0) 0.0.1\n",[62,141684,141685,141688,141690,141692,141694,141697,141699,141701,141703,141706,141708,141710,141713,141715,141717,141719,141722],{"class":64,"line":167},[62,141686,141687],{"class":122},"description:",[62,141689,125175],{"class":1675},[62,141691,141614],{"class":1675},[62,141693,50380],{"class":1675},[62,141695,141696],{"class":1675}," take",[62,141698,141656],{"class":1675},[62,141700,110754],{"class":1675},[62,141702,50362],{"class":1675},[62,141704,141705],{"class":1675}," give",[62,141707,141624],{"class":1675},[62,141709,50404],{"class":1675},[62,141711,141712],{"class":1675}," wrap",[62,141714,141624],{"class":1675},[62,141716,112416],{"class":1675},[62,141718,53632],{"class":1675},[62,141720,141721],{"class":1675}," poop",[62,141723,141724],{"class":1675}," emjoi\n",[62,141726,141727,141729,141732],{"class":64,"line":173},[62,141728,5952],{"class":122},[62,141730,141731],{"class":1675}," point:",[62,141733,141734],{"class":72}," (index.js)\n",[62,141736,141737,141739],{"class":64,"line":179},[62,141738,28137],{"class":149},[62,141740,141741],{"class":1675}," command:\n",[62,141743,141744,141746],{"class":64,"line":185},[62,141745,127282],{"class":122},[62,141747,141748],{"class":1675}," repository:\n",[62,141750,141751,141754],{"class":64,"line":191},[62,141752,141753],{"class":122},"keywords:",[62,141755,141756],{"class":1675}," node,npm\n",[62,141758,141759,141762,141765],{"class":64,"line":209},[62,141760,141761],{"class":122},"author:",[62,141763,141764],{"class":1675}," Dan",[62,141766,141767],{"class":1675}," Vega\n",[62,141769,141770,141773],{"class":64,"line":220},[62,141771,141772],{"class":122},"license:",[62,141774,141775],{"class":72}," (ISC) MIT\n",[62,141777,141778,141781,141783,141785,141787],{"class":64,"line":226},[62,141779,141780],{"class":122},"About",[62,141782,57317],{"class":1675},[62,141784,57310],{"class":1675},[62,141786,57317],{"class":1675},[62,141788,141789],{"class":1675}," /Users/vega/dev/npm/wrap-with-poop/package.json:\n",[62,141791,141792],{"class":64,"line":231},[62,141793,79],{"emptyLinePlaceholder":13},[62,141795,141796],{"class":64,"line":236},[62,141797,3680],{"class":72},[62,141799,141800,141802,141804],{"class":64,"line":242},[62,141801,87967],{"class":122},[62,141803,1266],{"class":149},[62,141805,141806],{"class":1675}," \"wrap-with-poop\",\n",[62,141808,141809,141811,141813],{"class":64,"line":247},[62,141810,125557],{"class":122},[62,141812,1266],{"class":149},[62,141814,141815],{"class":1675}," \"0.0.1\",\n",[62,141817,141818,141820,141822],{"class":64,"line":252},[62,141819,127117],{"class":122},[62,141821,1266],{"class":149},[62,141823,141824],{"class":1675}," \"This package will take any string you give it and wrap it with the poop emjoi\",\n",[62,141826,141827,141829,141831],{"class":64,"line":257},[62,141828,127129],{"class":122},[62,141830,1266],{"class":149},[62,141832,141833],{"class":1675}," \"index.js\",\n",[62,141835,141836,141838,141840],{"class":64,"line":271},[62,141837,125580],{"class":122},[62,141839,1266],{"class":149},[62,141841,126],{"class":1675},[62,141843,141844,141846,141848,141851,141853,141855,141857],{"class":64,"line":281},[62,141845,127147],{"class":122},[62,141847,1266],{"class":149},[62,141849,141850],{"class":1675}," \"echo ",[62,141852,127155],{"class":149},[62,141854,127158],{"class":1675},[62,141856,127155],{"class":149},[62,141858,127163],{"class":1675},[62,141860,141861],{"class":64,"line":286},[62,141862,32848],{"class":72},[62,141864,141865,141867,141869],{"class":64,"line":291},[62,141866,127172],{"class":122},[62,141868,1266],{"class":149},[62,141870,103023],{"class":72},[62,141872,141873,141876],{"class":64,"line":296},[62,141874,141875],{"class":122}," \"node\"",[62,141877,3338],{"class":122},[62,141879,141880],{"class":64,"line":302},[62,141881,141882],{"class":122}," \"npm\"\n",[62,141884,141885],{"class":64,"line":308},[62,141886,50066],{"class":72},[62,141888,141889,141891,141893],{"class":64,"line":314},[62,141890,127216],{"class":122},[62,141892,1266],{"class":149},[62,141894,141895],{"class":1675}," \"Dan Vega\",\n",[62,141897,141898,141900,141902],{"class":64,"line":320},[62,141899,127228],{"class":122},[62,141901,1266],{"class":149},[62,141903,141904],{"class":1675}," \"MIT\"\n",[62,141906,141907],{"class":64,"line":326},[62,141908,379],{"class":72},[62,141910,141911],{"class":64,"line":338},[62,141912,79],{"emptyLinePlaceholder":13},[62,141914,141915],{"class":64,"line":343},[62,141916,79],{"emptyLinePlaceholder":13},[62,141918,141919,141922,141924,141927],{"class":64,"line":357},[62,141920,141921],{"class":122},"Is",[62,141923,9961],{"class":1675},[62,141925,141926],{"class":1675}," OK?",[62,141928,141929],{"class":72}," (yes) yes\n",[26,141931,141933],{"id":141932},"writing-your-plugin","Writing your plugin",[22,141935,141936],{},"Next open this project up in Visual Studio Code and create index.js. The reason you're creating this file is because in your package.json you said that this was your entry point. In your index.js add the following code:",[52,141938,141940],{"className":32791,"code":141939,"language":32793,"meta":57,"style":57},"module.exports = str => {\n return `💩${str}💩`;\n};\n",[59,141941,141942,141959,141974],{"__ignoreMap":57},[62,141943,141944,141946,141948,141950,141952,141955,141957],{"class":64,"line":65},[62,141945,32813],{"class":149},[62,141947,2755],{"class":72},[62,141949,32818],{"class":149},[62,141951,2556],{"class":68},[62,141953,141954],{"class":889}," str",[62,141956,85402],{"class":68},[62,141958,126],{"class":72},[62,141960,141961,141963,141966,141969,141972],{"class":64,"line":76},[62,141962,82091],{"class":68},[62,141964,141965],{"class":1675}," `💩${",[62,141967,141968],{"class":72},"str",[62,141970,141971],{"class":1675},"}💩`",[62,141973,153],{"class":72},[62,141975,141976],{"class":64,"line":82},[62,141977,107354],{"class":72},[22,141979,141980],{},"The module.exports object allows us to organize some related code and then expose it as a module. This means that when we are done we could import this module into another application. In this case we are assigning an arrow function which means we are exposing a single function that takes an argument called str and returns that string wrapped with the poo emoji. That is all you need to do with this project. It is a pretty simple package but it will help walk through a few things.",[26,141982,141984],{"id":141983},"npm-local-development","npm local development",[22,141986,141987],{},"Now that you have our package ready to go you need to test it in another project. In the real world you should be writing some unit tests against it but I want to save that for another article & screencast.",[22,141989,141990],{},"Next create a new directory (outside of your package) called wrap-with-poo-testing. You will again need to run npm init but this time you can add the -y argument to skip all of the questions, they are less important this time.",[52,141992,141994],{"className":1663,"code":141993,"language":1665,"meta":57,"style":57},"npm init -y\n\nWrote to /Users/vega/dev/npm/wrap-with-poo/package.json:\n\n{\n \"name\": \"wrap-with-poop\",\n \"version\": \"1.0.0\",\n \"description\": \"\",\n \"main\": \"index.js\",\n \"scripts\": {\n \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n },\n \"keywords\": [],\n \"author\": \"\",\n \"license\": \"ISC\"\n}\n",[59,141995,141996,142004,142008,142018,142022,142026,142034,142043,142052,142060,142068,142084,142088,142097,142105,142114],{"__ignoreMap":57},[62,141997,141998,142000,142002],{"class":64,"line":65},[62,141999,32645],{"class":122},[62,142001,3282],{"class":1675},[62,142003,32727],{"class":149},[62,142005,142006],{"class":64,"line":76},[62,142007,79],{"emptyLinePlaceholder":13},[62,142009,142010,142013,142015],{"class":64,"line":82},[62,142011,142012],{"class":122},"Wrote",[62,142014,57317],{"class":1675},[62,142016,142017],{"class":1675}," /Users/vega/dev/npm/wrap-with-poo/package.json:\n",[62,142019,142020],{"class":64,"line":89},[62,142021,79],{"emptyLinePlaceholder":13},[62,142023,142024],{"class":64,"line":95},[62,142025,3680],{"class":72},[62,142027,142028,142030,142032],{"class":64,"line":101},[62,142029,87967],{"class":122},[62,142031,1266],{"class":149},[62,142033,141806],{"class":1675},[62,142035,142036,142038,142040],{"class":64,"line":107},[62,142037,125557],{"class":122},[62,142039,1266],{"class":149},[62,142041,142042],{"class":1675}," \"1.0.0\",\n",[62,142044,142045,142047,142049],{"class":64,"line":113},[62,142046,127117],{"class":122},[62,142048,1266],{"class":149},[62,142050,142051],{"class":1675}," \"\",\n",[62,142053,142054,142056,142058],{"class":64,"line":129},[62,142055,127129],{"class":122},[62,142057,1266],{"class":149},[62,142059,141833],{"class":1675},[62,142061,142062,142064,142066],{"class":64,"line":134},[62,142063,125580],{"class":122},[62,142065,1266],{"class":149},[62,142067,126],{"class":1675},[62,142069,142070,142072,142074,142076,142078,142080,142082],{"class":64,"line":156},[62,142071,127147],{"class":122},[62,142073,1266],{"class":149},[62,142075,141850],{"class":1675},[62,142077,127155],{"class":149},[62,142079,127158],{"class":1675},[62,142081,127155],{"class":149},[62,142083,127163],{"class":1675},[62,142085,142086],{"class":64,"line":161},[62,142087,32848],{"class":72},[62,142089,142090,142092,142094],{"class":64,"line":167},[62,142091,127172],{"class":122},[62,142093,1266],{"class":149},[62,142095,142096],{"class":72}," [],\n",[62,142098,142099,142101,142103],{"class":64,"line":173},[62,142100,127216],{"class":122},[62,142102,1266],{"class":149},[62,142104,142051],{"class":1675},[62,142106,142107,142109,142111],{"class":64,"line":179},[62,142108,127228],{"class":122},[62,142110,1266],{"class":149},[62,142112,142113],{"class":1675}," \"ISC\"\n",[62,142115,142116],{"class":64,"line":185},[62,142117,379],{"class":72},[636,142119,142121],{"id":142120},"npm-install","NPM Install",[22,142123,142124],{},"In this project create a new file called app.js. This is where you are going to use your new wrap-with-poo package. This is normally where you would normally install the npm package you needed by running the following command.",[52,142126,142128],{"className":1663,"code":142127,"language":1665,"meta":57,"style":57},"npm install wrap-with-poo\n",[59,142129,142130],{"__ignoreMap":57},[62,142131,142132,142134,142136],{"class":64,"line":65},[62,142133,32645],{"class":122},[62,142135,32750],{"class":1675},[62,142137,142138],{"class":1675}," wrap-with-poo\n",[22,142140,142141],{},"The problem with this is that you haven't published your new plugin yet so it isn't in npm. You need a way to reference the package locally while your developing it. You could run npm install with the absolute path to the package.",[52,142143,142145],{"className":1663,"code":142144,"language":1665,"meta":57,"style":57},"npm install /Users/vega/dev/npm/wrap-with-poo\n",[59,142146,142147],{"__ignoreMap":57},[62,142148,142149,142151,142153],{"class":64,"line":65},[62,142150,32645],{"class":122},[62,142152,32750],{"class":1675},[62,142154,142155],{"class":1675}," /Users/vega/dev/npm/wrap-with-poo\n",[22,142157,142158],{},"Which would update your package.json to look like this",[52,142160,142162],{"className":32791,"code":142161,"language":32793,"meta":57,"style":57},"{\n \"name\": \"npm\",\n \"version\": \"1.0.0\",\n \"description\": \"\",\n \"main\": \"index.js\",\n \"scripts\": {\n \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n },\n \"keywords\": [],\n \"author\": \"\",\n \"license\": \"ISC\",\n \"dependencies\": {\n \"wrap-with-poo\": \"file:../wrap-with-poo\"\n }\n}\n",[59,142163,142164,142168,142179,142190,142200,142210,142216,142232,142236,142242,142252,142263,142269,142279,142283],{"__ignoreMap":57},[62,142165,142166],{"class":64,"line":65},[62,142167,3680],{"class":72},[62,142169,142170,142172,142174,142177],{"class":64,"line":76},[62,142171,87967],{"class":1675},[62,142173,3696],{"class":72},[62,142175,142176],{"class":1675},"\"npm\"",[62,142178,3338],{"class":72},[62,142180,142181,142183,142185,142188],{"class":64,"line":82},[62,142182,125557],{"class":1675},[62,142184,3696],{"class":72},[62,142186,142187],{"class":1675},"\"1.0.0\"",[62,142189,3338],{"class":72},[62,142191,142192,142194,142196,142198],{"class":64,"line":89},[62,142193,127117],{"class":1675},[62,142195,3696],{"class":72},[62,142197,25895],{"class":1675},[62,142199,3338],{"class":72},[62,142201,142202,142204,142206,142208],{"class":64,"line":95},[62,142203,127129],{"class":1675},[62,142205,3696],{"class":72},[62,142207,127134],{"class":1675},[62,142209,3338],{"class":72},[62,142211,142212,142214],{"class":64,"line":101},[62,142213,125580],{"class":1675},[62,142215,3688],{"class":72},[62,142217,142218,142220,142222,142224,142226,142228,142230],{"class":64,"line":107},[62,142219,127147],{"class":1675},[62,142221,3696],{"class":72},[62,142223,127152],{"class":1675},[62,142225,127155],{"class":149},[62,142227,127158],{"class":1675},[62,142229,127155],{"class":149},[62,142231,127163],{"class":1675},[62,142233,142234],{"class":64,"line":113},[62,142235,32848],{"class":72},[62,142237,142238,142240],{"class":64,"line":129},[62,142239,127172],{"class":1675},[62,142241,50013],{"class":72},[62,142243,142244,142246,142248,142250],{"class":64,"line":134},[62,142245,127216],{"class":1675},[62,142247,3696],{"class":72},[62,142249,25895],{"class":1675},[62,142251,3338],{"class":72},[62,142253,142254,142256,142258,142261],{"class":64,"line":156},[62,142255,127228],{"class":1675},[62,142257,3696],{"class":72},[62,142259,142260],{"class":1675},"\"ISC\"",[62,142262,3338],{"class":72},[62,142264,142265,142267],{"class":64,"line":161},[62,142266,125649],{"class":1675},[62,142268,3688],{"class":72},[62,142270,142271,142274,142276],{"class":64,"line":167},[62,142272,142273],{"class":1675}," \"wrap-with-poo\"",[62,142275,3696],{"class":72},[62,142277,142278],{"class":1675},"\"file:../wrap-with-poo\"\n",[62,142280,142281],{"class":64,"line":173},[62,142282,3731],{"class":72},[62,142284,142285],{"class":64,"line":179},[62,142286,379],{"class":72},[22,142288,142289,142290,142295,142296,2755],{},"If you need to test out ",[677,142291,142294],{"href":142292,"rel":142293},"https://docs.npmjs.com/misc/scripts",[681],"pre-install/post-install hooks"," in your package then you will want to use this approach. If you don't care about the best way to develop NPM projects locally is by using ",[677,142297,128519],{"href":142298,"rel":142299},"https://docs.npmjs.com/cli/link.html",[681],[636,142301,142303],{"id":142302},"npm-link","NPM Link",[22,142305,142306],{},"npm link is a process that allows you to create a symbolic link between your project and the dependency. First you need to move in to the directory wrap-with-poo and run the following command.",[52,142308,142310],{"className":1663,"code":142309,"language":1665,"meta":57,"style":57},"npm link\n",[59,142311,142312],{"__ignoreMap":57},[62,142313,142314,142316],{"class":64,"line":65},[62,142315,32645],{"class":122},[62,142317,142318],{"class":1675}," link\n",[22,142320,142321],{},"This will take your package and create a symbolic link in the npm global folder to it.",[22,142323,142324],{},[646,142325,142326],{},"/Users/vega/.nvm/versions/node/v10.15.0/lib/node_modules/wrap-with-poo -> /Users/vega/dev/npm/wrap-with-poo",[22,142328,142329],{},"This means that your project can be used in any project with one more simple step. The next thing you need to do is to move into the project wrap-with-poo-testing and run the following command.",[52,142331,142333],{"className":1663,"code":142332,"language":1665,"meta":57,"style":57},"npm link wrap-with-poo\n",[59,142334,142335],{"__ignoreMap":57},[62,142336,142337,142339,142341],{"class":64,"line":65},[62,142338,32645],{"class":122},[62,142340,127959],{"class":1675},[62,142342,142138],{"class":1675},[22,142344,142345,142346],{},"This will output the following: ",[646,142347,142348],{},"/Users/vega/dev/npm/wrap-with-poo-testing/node_modules/wrap-with-poo -> /Users/vega/.nvm/versions/node/v10.15.0/lib/node_modules/wra\np-with-poo -> /Users/vega/dev/npm/wrap-with-poo",[22,142350,142351],{},"That is all there is to it, no need to install the dependency. You are ready to begin writing some code to play with you new plugin. Open up the app.js and add the following code.",[52,142353,142355],{"className":32791,"code":142354,"language":32793,"meta":57,"style":57},"const poo = require(\"wrap-with-poo\");\nconst boring = \"This is a boring string\";\nconst fun = poo(boring);\n\nconsole.log(fun);\n",[59,142356,142357,142375,142389,142403,142407],{"__ignoreMap":57},[62,142358,142359,142361,142364,142366,142368,142370,142373],{"class":64,"line":65},[62,142360,110541],{"class":68},[62,142362,142363],{"class":149}," poo",[62,142365,2556],{"class":68},[62,142367,120735],{"class":122},[62,142369,2109],{"class":72},[62,142371,142372],{"class":1675},"\"wrap-with-poo\"",[62,142374,1133],{"class":72},[62,142376,142377,142379,142382,142384,142387],{"class":64,"line":76},[62,142378,110541],{"class":68},[62,142380,142381],{"class":149}," boring",[62,142383,2556],{"class":68},[62,142385,142386],{"class":1675}," \"This is a boring string\"",[62,142388,153],{"class":72},[62,142390,142391,142393,142396,142398,142400],{"class":64,"line":82},[62,142392,110541],{"class":68},[62,142394,142395],{"class":149}," fun",[62,142397,2556],{"class":68},[62,142399,142363],{"class":122},[62,142401,142402],{"class":72},"(boring);\n",[62,142404,142405],{"class":64,"line":89},[62,142406,79],{"emptyLinePlaceholder":13},[62,142408,142409,142411,142413],{"class":64,"line":95},[62,142410,110298],{"class":72},[62,142412,58271],{"class":122},[62,142414,142415],{"class":72},"(fun);\n",[22,142417,142418],{},"And run the following command from the integrated terminal",[52,142420,142422],{"className":32791,"code":142421,"language":32793,"meta":57,"style":57},"node app.js\n",[59,142423,142424],{"__ignoreMap":57},[62,142425,142426],{"class":64,"line":65},[62,142427,142421],{"class":72},[22,142429,142430],{},"And you will get the following output",[52,142432,142434],{"className":1663,"code":142433,"language":1665,"meta":57,"style":57},"💩This is a boring string💩\n",[59,142435,142436],{"__ignoreMap":57},[62,142437,142438,142441,142443,142445,142447],{"class":64,"line":65},[62,142439,142440],{"class":122},"💩This",[62,142442,50409],{"class":1675},[62,142444,28870],{"class":1675},[62,142446,142381],{"class":1675},[62,142448,142449],{"class":1675}," string💩\n",[26,142451,142453],{"id":142452},"publish-source-code","Publish Source Code",[22,142455,142456],{},"Now that we know our project works its time to make it public for everyone to use. If you haven't done so yet I would add your project to Github or whatever source code hosting you prefer.",[52,142458,142460],{"className":1663,"code":142459,"language":1665,"meta":57,"style":57},"git init\ngit add .\ngit commit -m \"Initial commit\"\ngit remote add origin https://github.com/cfaddict/wrap-with-poo.git\ngit push -u origin master\n",[59,142461,142462,142468,142476,142487,142500],{"__ignoreMap":57},[62,142463,142464,142466],{"class":64,"line":65},[62,142465,127282],{"class":122},[62,142467,32775],{"class":1675},[62,142469,142470,142472,142474],{"class":64,"line":76},[62,142471,127282],{"class":122},[62,142473,28863],{"class":1675},[62,142475,127293],{"class":1675},[62,142477,142478,142480,142482,142484],{"class":64,"line":82},[62,142479,127282],{"class":122},[62,142481,127300],{"class":1675},[62,142483,127303],{"class":149},[62,142485,142486],{"class":1675}," \"Initial commit\"\n",[62,142488,142489,142491,142493,142495,142497],{"class":64,"line":89},[62,142490,127282],{"class":122},[62,142492,127313],{"class":1675},[62,142494,28863],{"class":1675},[62,142496,127318],{"class":1675},[62,142498,142499],{"class":1675}," https://github.com/cfaddict/wrap-with-poo.git\n",[62,142501,142502,142504,142506,142508,142510],{"class":64,"line":95},[62,142503,127282],{"class":122},[62,142505,127328],{"class":1675},[62,142507,127331],{"class":149},[62,142509,127318],{"class":1675},[62,142511,127336],{"class":1675},[22,142513,142514],{},"Now that it is on Github go back and add an entry to your package.json so everyone knows where to find the source code using the homepage key.",[52,142516,142518],{"className":32791,"code":142517,"language":32793,"meta":57,"style":57},"{\n \"name\": \"wrap-with-poo\",\n \"version\": \"0.0.1\",\n \"description\": \"This package will wrap any string you give it with the poop emoji\",\n \"main\": \"index.js\",\n \"scripts\": {\n \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n },\n \"keywords\": [\n \"node\",\n \"npm\",\n \"poop\"\n ],\n \"author\": \"Dan Vega\",\n \"license\": \"MIT\",\n \"homepage\": \"https://github.com/cfaddict/wrap-with-poo\"\n}\n",[59,142519,142520,142524,142534,142545,142556,142566,142572,142588,142592,142598,142604,142611,142616,142620,142630,142640,142650],{"__ignoreMap":57},[62,142521,142522],{"class":64,"line":65},[62,142523,3680],{"class":72},[62,142525,142526,142528,142530,142532],{"class":64,"line":76},[62,142527,87967],{"class":1675},[62,142529,3696],{"class":72},[62,142531,142372],{"class":1675},[62,142533,3338],{"class":72},[62,142535,142536,142538,142540,142543],{"class":64,"line":82},[62,142537,125557],{"class":1675},[62,142539,3696],{"class":72},[62,142541,142542],{"class":1675},"\"0.0.1\"",[62,142544,3338],{"class":72},[62,142546,142547,142549,142551,142554],{"class":64,"line":89},[62,142548,127117],{"class":1675},[62,142550,3696],{"class":72},[62,142552,142553],{"class":1675},"\"This package will wrap any string you give it with the poop emoji\"",[62,142555,3338],{"class":72},[62,142557,142558,142560,142562,142564],{"class":64,"line":95},[62,142559,127129],{"class":1675},[62,142561,3696],{"class":72},[62,142563,127134],{"class":1675},[62,142565,3338],{"class":72},[62,142567,142568,142570],{"class":64,"line":101},[62,142569,125580],{"class":1675},[62,142571,3688],{"class":72},[62,142573,142574,142576,142578,142580,142582,142584,142586],{"class":64,"line":107},[62,142575,127147],{"class":1675},[62,142577,3696],{"class":72},[62,142579,127152],{"class":1675},[62,142581,127155],{"class":149},[62,142583,127158],{"class":1675},[62,142585,127155],{"class":149},[62,142587,127163],{"class":1675},[62,142589,142590],{"class":64,"line":113},[62,142591,32848],{"class":72},[62,142593,142594,142596],{"class":64,"line":129},[62,142595,127172],{"class":1675},[62,142597,3709],{"class":72},[62,142599,142600,142602],{"class":64,"line":134},[62,142601,141875],{"class":1675},[62,142603,3338],{"class":72},[62,142605,142606,142609],{"class":64,"line":156},[62,142607,142608],{"class":1675}," \"npm\"",[62,142610,3338],{"class":72},[62,142612,142613],{"class":64,"line":161},[62,142614,142615],{"class":1675}," \"poop\"\n",[62,142617,142618],{"class":64,"line":167},[62,142619,50066],{"class":72},[62,142621,142622,142624,142626,142628],{"class":64,"line":173},[62,142623,127216],{"class":1675},[62,142625,3696],{"class":72},[62,142627,132040],{"class":1675},[62,142629,3338],{"class":72},[62,142631,142632,142634,142636,142638],{"class":64,"line":179},[62,142633,127228],{"class":1675},[62,142635,3696],{"class":72},[62,142637,127233],{"class":1675},[62,142639,3338],{"class":72},[62,142641,142642,142645,142647],{"class":64,"line":185},[62,142643,142644],{"class":1675}," \"homepage\"",[62,142646,3696],{"class":72},[62,142648,142649],{"class":1675},"\"https://github.com/cfaddict/wrap-with-poo\"\n",[62,142651,142652],{"class":64,"line":191},[62,142653,379],{"class":72},[26,142655,142657],{"id":142656},"publishing-npm-package","Publishing NPM Package",[22,142659,142660],{},"It is now time to publish our project to npm so that anyone can use it. If this is your first time publishing a package open up a terminal in the wrap-with-poo directory and type the following command.",[52,142662,142664],{"className":1663,"code":142663,"language":1665,"meta":57,"style":57},"npm adduser\n",[59,142665,142666],{"__ignoreMap":57},[62,142667,142668,142670],{"class":64,"line":65},[62,142669,32645],{"class":122},[62,142671,142672],{"class":1675}," adduser\n",[22,142674,142675],{},"This will ask you for your npm account information such as username, password and email.",[52,142677,142679],{"className":1663,"code":142678,"language":1665,"meta":57,"style":57},"vega wrap-with-poo (master) $ npm adduser\nUsername: therealdanvega\nPassword:\nEmail: (this IS public) danvega@gmail.com\nLogged in as therealdanvega on https://registry.npmjs.org/.\n",[59,142680,142681,142691,142699,142704,142721],{"__ignoreMap":57},[62,142682,142683,142685,142688],{"class":64,"line":65},[62,142684,111184],{"class":122},[62,142686,142687],{"class":1675}," wrap-with-poo",[62,142689,142690],{"class":72}," (master) $ npm adduser\n",[62,142692,142693,142696],{"class":64,"line":76},[62,142694,142695],{"class":122},"Username:",[62,142697,142698],{"class":1675}," therealdanvega\n",[62,142700,142701],{"class":64,"line":82},[62,142702,142703],{"class":122},"Password:\n",[62,142705,142706,142709,142712,142715,142718],{"class":64,"line":89},[62,142707,142708],{"class":122},"Email:",[62,142710,142711],{"class":72}," (this ",[62,142713,142714],{"class":1675},"IS",[62,142716,142717],{"class":1675}," public",[62,142719,142720],{"class":72},") danvega@gmail.com\n",[62,142722,142723,142726,142728,142730,142733,142735],{"class":64,"line":95},[62,142724,142725],{"class":122},"Logged",[62,142727,57031],{"class":1675},[62,142729,53662],{"class":1675},[62,142731,142732],{"class":1675}," therealdanvega",[62,142734,57056],{"class":1675},[62,142736,142737],{"class":1675}," https://registry.npmjs.org/.\n",[22,142739,142740,142741,142744],{},"Now you're ready to publish but there are a couple of things you need to remember. First every npm package must have a unique name. I would head over to ",[677,142742,32645],{"href":51563,"rel":142743},[681]," and do a quick search for your package. I have already published the package wrap-with-poo so yours will need a new unique name.",[22,142746,142747],{},"The next thing you need to know is that your version number matters. I start with 0.0.1 and work my way up from there. Once you publish a specific version you can't publish the same version again. It is a good idea to build a number of features into a version and then publish that. If you run",[52,142749,142751],{"className":1663,"code":142750,"language":1665,"meta":57,"style":57},"npm version\n",[59,142752,142753],{"__ignoreMap":57},[62,142754,142755,142757],{"class":64,"line":65},[62,142756,32645],{"class":122},[62,142758,111909],{"class":1675},[22,142760,142761],{},"It will tell you what your current version is.",[52,142763,142765],{"className":32791,"code":142764,"language":32793,"meta":57,"style":57},"{ 'wrap-with-poo': '0.0.1',\n npm: '6.7.0',\n ares: '1.15.0',\n cldr: '33.1',\n http_parser: '2.8.0',\n icu: '62.1',\n modules: '64',\n napi: '3',\n nghttp2: '1.34.0',\n node: '10.15.0',\n openssl: '1.1.0j',\n tz: '2018e',\n unicode: '11.0',\n uv: '1.23.2',\n v8: '6.8.275.32-node.45',\n zlib: '1.2.11' }\n",[59,142766,142767,142782,142794,142806,142818,142830,142842,142854,142866,142878,142890,142902,142914,142926,142938,142950],{"__ignoreMap":57},[62,142768,142769,142772,142775,142777,142780],{"class":64,"line":65},[62,142770,142771],{"class":72},"{ ",[62,142773,142774],{"class":1675},"'wrap-with-poo'",[62,142776,3696],{"class":72},[62,142778,142779],{"class":1675},"'0.0.1'",[62,142781,3338],{"class":72},[62,142783,142784,142787,142789,142792],{"class":64,"line":76},[62,142785,142786],{"class":122}," npm",[62,142788,3696],{"class":72},[62,142790,142791],{"class":1675},"'6.7.0'",[62,142793,3338],{"class":72},[62,142795,142796,142799,142801,142804],{"class":64,"line":82},[62,142797,142798],{"class":122}," ares",[62,142800,3696],{"class":72},[62,142802,142803],{"class":1675},"'1.15.0'",[62,142805,3338],{"class":72},[62,142807,142808,142811,142813,142816],{"class":64,"line":89},[62,142809,142810],{"class":122}," cldr",[62,142812,3696],{"class":72},[62,142814,142815],{"class":1675},"'33.1'",[62,142817,3338],{"class":72},[62,142819,142820,142823,142825,142828],{"class":64,"line":95},[62,142821,142822],{"class":122}," http_parser",[62,142824,3696],{"class":72},[62,142826,142827],{"class":1675},"'2.8.0'",[62,142829,3338],{"class":72},[62,142831,142832,142835,142837,142840],{"class":64,"line":101},[62,142833,142834],{"class":122}," icu",[62,142836,3696],{"class":72},[62,142838,142839],{"class":1675},"'62.1'",[62,142841,3338],{"class":72},[62,142843,142844,142847,142849,142852],{"class":64,"line":107},[62,142845,142846],{"class":122}," modules",[62,142848,3696],{"class":72},[62,142850,142851],{"class":1675},"'64'",[62,142853,3338],{"class":72},[62,142855,142856,142859,142861,142864],{"class":64,"line":113},[62,142857,142858],{"class":122}," napi",[62,142860,3696],{"class":72},[62,142862,142863],{"class":1675},"'3'",[62,142865,3338],{"class":72},[62,142867,142868,142871,142873,142876],{"class":64,"line":129},[62,142869,142870],{"class":122}," nghttp2",[62,142872,3696],{"class":72},[62,142874,142875],{"class":1675},"'1.34.0'",[62,142877,3338],{"class":72},[62,142879,142880,142883,142885,142888],{"class":64,"line":134},[62,142881,142882],{"class":122}," node",[62,142884,3696],{"class":72},[62,142886,142887],{"class":1675},"'10.15.0'",[62,142889,3338],{"class":72},[62,142891,142892,142895,142897,142900],{"class":64,"line":156},[62,142893,142894],{"class":122}," openssl",[62,142896,3696],{"class":72},[62,142898,142899],{"class":1675},"'1.1.0j'",[62,142901,3338],{"class":72},[62,142903,142904,142907,142909,142912],{"class":64,"line":161},[62,142905,142906],{"class":122}," tz",[62,142908,3696],{"class":72},[62,142910,142911],{"class":1675},"'2018e'",[62,142913,3338],{"class":72},[62,142915,142916,142919,142921,142924],{"class":64,"line":167},[62,142917,142918],{"class":122}," unicode",[62,142920,3696],{"class":72},[62,142922,142923],{"class":1675},"'11.0'",[62,142925,3338],{"class":72},[62,142927,142928,142931,142933,142936],{"class":64,"line":173},[62,142929,142930],{"class":122}," uv",[62,142932,3696],{"class":72},[62,142934,142935],{"class":1675},"'1.23.2'",[62,142937,3338],{"class":72},[62,142939,142940,142943,142945,142948],{"class":64,"line":179},[62,142941,142942],{"class":122}," v8",[62,142944,3696],{"class":72},[62,142946,142947],{"class":1675},"'6.8.275.32-node.45'",[62,142949,3338],{"class":72},[62,142951,142952,142955,142957,142960],{"class":64,"line":185},[62,142953,142954],{"class":122}," zlib",[62,142956,3696],{"class":72},[62,142958,142959],{"class":1675},"'1.2.11'",[62,142961,122649],{"class":72},[22,142963,142964],{},"If everything looks good you can publish your new project by running",[52,142966,142968],{"className":1663,"code":142967,"language":1665,"meta":57,"style":57},"npm publish\n",[59,142969,142970],{"__ignoreMap":57},[62,142971,142972,142974],{"class":64,"line":65},[62,142973,32645],{"class":122},[62,142975,142976],{"class":1675}," publish\n",[22,142978,142979,142980,2755],{},"This might take a few seconds but if everything went ok your package should now be ",[677,142981,142984],{"href":142982,"rel":142983},"https://www.npmjs.com/settings/therealdanvega/packages",[681],"live on npm",[22,142986,142987],{},"Congrats on publishing your first npm package!!!",[22,142989,142990],{},"Now you can go into any project that already has a package.json and type the following",[52,142992,142993],{"className":1663,"code":142127,"language":1665,"meta":57,"style":57},[59,142994,142995],{"__ignoreMap":57},[62,142996,142997,142999,143001],{"class":64,"line":65},[62,142998,32645],{"class":122},[62,143000,32750],{"class":1675},[62,143002,142138],{"class":1675},[22,143004,143005],{},"And use it just like we did in our testing example above.",[26,143007,37639],{"id":86173},[22,143009,143010],{},"I know some people say that you should create documentation from the beginning but I am not always sure what my code is going to end up looking like so I usually wait on this. Create a README.md in the root of your project and add some information about your project.",[915,143012,143013,143016,143019,143022],{},[37,143014,143015],{},"What does your npm package do?",[37,143017,143018],{},"Why did you create it.",[37,143020,143021],{},"How do you install it?",[37,143023,143024],{},"Are there any configuration options?",[26,143026,1499],{"id":1498},[22,143028,143029],{},"As I said at the beginning of this article I can't believe I published my first npm package this weekend. I just never really had a need to do so until now but I was really excited to learn how easy it was. If this is your first npm package please leave me some comments or tweet at me when yours is live!",[22,143031,80332,143032,82545],{},[36006,143033],{},[1527,143035,143036],{},"html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}html pre.shiki code .spoOn, html code.shiki .spoOn{--shiki-default:#E36209;--shiki-dark:#FFAB70;--shiki-sepia:#E36209}",{"title":57,"searchDepth":76,"depth":76,"links":143038},[143039,143040,143041,143042,143046,143047,143048,143049],{"id":2873,"depth":76,"text":2874},{"id":141443,"depth":76,"text":141444},{"id":141932,"depth":76,"text":141933},{"id":141983,"depth":76,"text":141984,"children":143043},[143044,143045],{"id":142120,"depth":82,"text":142121},{"id":142302,"depth":82,"text":142303},{"id":142452,"depth":76,"text":142453},{"id":142656,"depth":76,"text":142657},{"id":86173,"depth":76,"text":37639},{"id":1498,"depth":76,"text":1499},{"_id":143051,"path":143052,"title":143053,"description":143054,"meta":143055,"body":143060},"content/blog/2019/01/31/hello-gridsome.md","/blog/2019/01/31/hello-gridsome","Hello, Gridsome!","A quick write up on why I started a new blog and what I plan to do with it.",{"slug":143056,"date":143057,"published":13,"tags":143058,"author":-1,"cover":143059,"excerpt":-1},"hello-gridsome","2019-01-31 10:00:00",[105335],"./gridsome.png",{"type":19,"value":143061,"toc":143474},[143062,143065,143075,143078,143087,143090,143102,143105,143108,143112,143121,143124,143127,143136,143139,143142,143146,143163,143184,143187,143190,143192,143198,143202,143209,143213,143216,143221,143223,143229,143385,143405,143409,143416,143420,143423,143426,143458,143460,143467,143471],[22,143063,143064],{},"I would like to welcome you to a new side project of mine. I thought I would take this opportunity to tell you about the motivation behind creating this website and the technologies that power it.",[22,143066,143067],{},[4534,143068,143069,143070,143074],{},"TLDR; I want to move my ",[677,143071,119709],{"href":143072,"rel":143073},"https://www.therealdanvega.com",[681]," from WordPress to Gridsome.",[26,143076,143077],{"id":60034},"Blogging",[22,143079,143080,143081,143086],{},"While writing this article I did a bit of digging through my archives and found out that my ",[677,143082,143085],{"href":143083,"rel":143084},"https://therealdanvega.com/blog/2005/10/07/why-you-should-now-about-sifr",[681],"first blog post"," was written October 7, 2005. That means I will be celebrating 14 years of blogging later this year which on one hand is pretty amazing and on the other makes me feel really old.",[636,143088,143089],{"id":60043},"Why I started blogging",[22,143091,143092,143093,143097,143098,143101],{},"I started blogging because I thought it would be a great chance for me to learn about how open source software worked. At the time I was a ColdFusion developer and ",[677,143094,60051],{"href":143095,"rel":143096},"https://github.com/teamcfadvance/BlogCFC5",[681]," was a popular open source blogging platform written by ",[677,143099,104655],{"href":60057,"rel":143100},[681],". I learned so much by looking at how someone else wrote code and structured a project and I will always be thankful for that experience.",[22,143103,143104],{},"Another reason I started blogging was to get involved in the community. There were a lot of really great developers that would often blog about what they working on and I really looked up to them. I wanted to be one of the cool kids and tell others what I was working on.",[22,143106,143107],{},"Finally, I thought this was a great chance for me to help others. In the early days, I often found myself struggling with projects that I was working on and thought to myself someone else must be experiencing the same issues as me. This was a great opportunity for me to take what I was learning and share it with other developers so that they didn't make the same mistakes that I did.",[636,143109,143111],{"id":143110},"moving-to-wordpress","Moving to WordPress",[22,143113,143114,143115,143120],{},"In 2014 I decided to ",[677,143116,143119],{"href":143117,"rel":143118},"https://therealdanvega.com/blog/2014/11/25/welcome-new-home",[681],"move my blog"," over to WordPress. It had been a while since I was using ColdFusion and I just didn't want to maintain it any longer. I also thought that this was a good chance to give the blog a fresh look. The migration to WordPress went pretty smooth thanks in part to open source. There were some migration scripts floating around that really helped me through that process.",[22,143122,143123],{},"While the WordPress platform has served me well over the years I have had my problems with it. First I originally was running this site on a Digital Ocean droplet for $5 a month which was great because at the time it wasn't really generating any revenue.",[22,143125,143126],{},"I quickly found out that WordPress doesn't perform well on that plan because it was a huge resource hog. It would go down often and require me to login and restart the server. After upgrading to the $20/month plan a lot of those problems went away.",[22,143128,143129,143130,143135],{},"There was still the problem of me having to manage a server. I am not a server admin and really didn't enjoy everything that went along with that. I ended up getting really lucky and finding someone on Upwork to manage the server. His name is Justin and he was able to fix my server when it when down as well as make sure the server was up to date with security patches. He runs a hosting company called ",[677,143131,143134],{"href":143132,"rel":143133},"https://bigscoots.com/",[681],"Big Scoots"," which is now where my website is hosted. I can't recommend him or his company enough if you're looking for a WordPress host. I have had 0 problems since moving to them.",[22,143137,143138],{},"The other main issue I have with WordPress is that I am not a PHP developer nor do I care to be. I have been able to get in there and mess around with the layouts and templates but that is about as far as I have gone. I also don't have a local WordPress installation so working on theme updates isn't easy for me.",[22,143140,143141],{},"While I have had some issues I do think WordPress is an amazing product. It's just no longer the product for me.",[636,143143,143145],{"id":143144},"new-solution-requirements","New Solution Requirements",[22,143147,143148,143149,143152,143153,143156,143157,143159,143160,143162],{},"Now that you know my story and where I have been we can talk about what I am looking for. I have become a huge fan of the ",[646,143150,143151],{},"JAMStack"," which stands for ",[646,143154,143155],{},"J","avaScript + ",[646,143158,70492],{},"PI + ",[646,143161,70528],{},"arkup. The idea behind this is pretty simple, serve up a static site for everything that you can and then reach out to an API for any data or functionality that your site needs. This is a big shift from the traditional server-side monolithic applications of the past and one I am very excited about.",[22,143164,143165,143166,143169,143170,143175,143176,143179,143180,143183],{},"Static Site Generators (SSGs) like ",[677,143167,130879],{"href":130877,"rel":143168},[681]," have become really popular and they have their advantages. First, we get to serve a static site to our users which means it is going to be extremely fast. My visitors are coming to my site to check out content, not some loading bar. The other big advantage is the options we have when it comes to hosting our static sites. If you want to host it on something like ",[677,143171,143174],{"href":143172,"rel":143173},"https://aws.amazon.com/free/free-tier/",[681],"Amazon Web Services (AWS)"," it can be very inexpensive. There is also one of my favorite services around ",[677,143177,119675],{"href":119673,"rel":143178},[681],", which is ",[646,143181,143182],{},"FREE"," for personal projects.",[22,143185,143186],{},"Another advantage of this approach is that I am not tied to some server-side language. I get to build my layouts and views in good old HTML/CSS/JavaScript. Another feature I am looking for is the ability to write all of my blog posts in Markdown. I write a ton of documentation for work and in my personal projects so I have become very comfortable writing in this format.",[22,143188,143189],{},"I had a bunch of options when building out this new site. I can't wait to tell you about what I found but for now, I am going to just talk about the one ended up using.",[26,143191,104836],{"id":113419},[22,143193,143194,143195,2755],{},"I mentioned Gatsby earlier and while this was definitely a possible solution for me I wanted something that was Vue based. I really love Vue, the ecosystem and the community so I wanted a solution that would keep me there. After looking around and creating a few projects i decided on ",[677,143196,104836],{"href":104834,"rel":143197},[681],[636,143199,143201],{"id":143200},"what-is-gridsome","What is Gridsome",[22,143203,143204,143205,143208],{},"Gridsome is a static site generator similar to Gatsby (though still new so not as feature rich) for the ",[677,143206,105462],{"href":117748,"rel":143207},[681]," framework. Gridsome has a ton of features but here are just a few. What this means for me is I get to write Vue applications that can then generate a static website. Now I know what you're thinking, Vue can do that on its own and you're right. Let's take a look at a few more features of Gridsome.",[636,143210,143212],{"id":143211},"how-gridsome-works","How Gridsome Works",[22,143214,143215],{},"If you were just going to have a plain static site you wouldn't really need something like Gridsome. Where Gridsome really shines is its ability to use multiple data source and combine them into a single GraphQL data layer. This means that you can work with local files like Markdown as well headless CMS's like WordPress, Contentful and so on. If you're new to GraphQL don't worry I was too and it's pretty easy to pickup.",[22,143217,143218],{},[653,143219],{"alt":143212,"src":143220},"./how-gridsome-works.png",[636,143222,130541],{"id":29692},[22,143224,143225,143226],{},"As I mentioned one of those data sources is Markdown which made me very happy because I can start writing in a familiar environment. I am going to go over this in detail in another post but the quick version is that this was really easy to set up. You start with some configuration to use the plugin ",[59,143227,143228],{},"@gridsome/source-filesystem",[52,143230,143232],{"className":32791,"code":143231,"language":32793,"meta":57,"style":57},"module.exports = {\n plugins: [\n {\n use: \"@gridsome/source-filesystem\",\n options: {\n path: \"blog/**/*.md\",\n typeName: \"Post\",\n route: \"/blog/:slug\",\n resolveAbsolutePaths: true,\n remark: {\n autolinkClassName: \"fas fa-hashtag\",\n externalLinksTarget: \"_blank\",\n externalLinksRel: [\"nofollow\", \"noopener\", \"noreferrer\"],\n plugins: [[\"gridsome-plugin-remark-shiki\", { theme: \"nord\" }]]\n }\n }\n }\n ]\n};\n",[59,143233,143234,143246,143251,143255,143264,143268,143277,143286,143295,143304,143309,143319,143328,143348,143365,143369,143373,143377,143381],{"__ignoreMap":57},[62,143235,143236,143238,143240,143242,143244],{"class":64,"line":65},[62,143237,32813],{"class":149},[62,143239,2755],{"class":72},[62,143241,32818],{"class":149},[62,143243,2556],{"class":68},[62,143245,126],{"class":72},[62,143247,143248],{"class":64,"line":76},[62,143249,143250],{"class":72}," plugins: [\n",[62,143252,143253],{"class":64,"line":82},[62,143254,49857],{"class":72},[62,143256,143257,143259,143262],{"class":64,"line":89},[62,143258,133783],{"class":72},[62,143260,143261],{"class":1675},"\"@gridsome/source-filesystem\"",[62,143263,3338],{"class":72},[62,143265,143266],{"class":64,"line":95},[62,143267,133792],{"class":72},[62,143269,143270,143272,143275],{"class":64,"line":101},[62,143271,133797],{"class":72},[62,143273,143274],{"class":1675},"\"blog/**/*.md\"",[62,143276,3338],{"class":72},[62,143278,143279,143281,143284],{"class":64,"line":107},[62,143280,128657],{"class":72},[62,143282,143283],{"class":1675},"\"Post\"",[62,143285,3338],{"class":72},[62,143287,143288,143290,143293],{"class":64,"line":113},[62,143289,128667],{"class":72},[62,143291,143292],{"class":1675},"\"/blog/:slug\"",[62,143294,3338],{"class":72},[62,143296,143297,143300,143302],{"class":64,"line":129},[62,143298,143299],{"class":72}," resolveAbsolutePaths: ",[62,143301,21775],{"class":149},[62,143303,3338],{"class":72},[62,143305,143306],{"class":64,"line":134},[62,143307,143308],{"class":72}," remark: {\n",[62,143310,143311,143314,143317],{"class":64,"line":156},[62,143312,143313],{"class":72}," autolinkClassName: ",[62,143315,143316],{"class":1675},"\"fas fa-hashtag\"",[62,143318,3338],{"class":72},[62,143320,143321,143324,143326],{"class":64,"line":161},[62,143322,143323],{"class":72}," externalLinksTarget: ",[62,143325,139799],{"class":1675},[62,143327,3338],{"class":72},[62,143329,143330,143333,143336,143338,143341,143343,143346],{"class":64,"line":167},[62,143331,143332],{"class":72}," externalLinksRel: [",[62,143334,143335],{"class":1675},"\"nofollow\"",[62,143337,976],{"class":72},[62,143339,143340],{"class":1675},"\"noopener\"",[62,143342,976],{"class":72},[62,143344,143345],{"class":1675},"\"noreferrer\"",[62,143347,32833],{"class":72},[62,143349,143350,143353,143356,143359,143362],{"class":64,"line":173},[62,143351,143352],{"class":72}," plugins: [[",[62,143354,143355],{"class":1675},"\"gridsome-plugin-remark-shiki\"",[62,143357,143358],{"class":72},", { theme: ",[62,143360,143361],{"class":1675},"\"nord\"",[62,143363,143364],{"class":72}," }]]\n",[62,143366,143367],{"class":64,"line":179},[62,143368,533],{"class":72},[62,143370,143371],{"class":64,"line":185},[62,143372,29042],{"class":72},[62,143374,143375],{"class":64,"line":191},[62,143376,223],{"class":72},[62,143378,143379],{"class":64,"line":209},[62,143380,7661],{"class":72},[62,143382,143383],{"class":64,"line":220},[62,143384,107354],{"class":72},[22,143386,143387,143388,143392,143393,143398,143399,143404],{},"In that plugin, you configure some options like what the path will be and the component that will handle the type. I'm also using the ",[677,143389,143391],{"href":127050,"rel":143390},[681],"Gridsome Transformer Remark"," plugin which is the Markdown transformer for Gridsome. ",[677,143394,143397],{"href":143395,"rel":143396},"https://github.com/remarkjs/remark",[681],"Remark"," can include a number of options and plugins itself one of which is a syntax highlighter called ",[677,143400,143403],{"href":143401,"rel":143402},"https://github.com/EldoranDev/gridsome-plugin-remark-shiki",[681],"shiki"," which is what formatted the code that is right above this.",[636,143406,143408],{"id":143407},"gridsome-is-awesome","Gridsome is awesome",[22,143410,143411,143412,143415],{},"This was just the start of what Gridsome can do. I have so many things to share so I hope you will sign up for my newsletter or ",[677,143413,126788],{"href":93581,"rel":143414},[681]," and learn about all the cool things you can do in Gridsome and in Vue.",[26,143417,143419],{"id":143418},"should-i-move-my-existing-website","Should I move my existing website?",[22,143421,143422],{},"Now the real question and much harder than a simple yes or no. Should I move my existing website to Gridsome? I am really happy with this website and the workflow I have for adding new content to it.",[22,143424,143425],{},"Moving my close to 1000 blog posts is easier said than done. 1 question that comes into play is should I just get rid of all the posts that I don't think hold much weight anymore? This could slim down the migration process substantially. These are some of the concerns I have to look into before making my decision.",[915,143427,143428,143431,143434,143437,143440,143449,143452,143455],{},[37,143429,143430],{},"Existing URLs must keep the same format /month/day/year/slug",[37,143432,143433],{},"I have lots of images and should probably move those to something like S3",[37,143435,143436],{},"I have photo galleries on posts, what to do with that.",[37,143438,143439],{},"I need to make sure existing code blocks work with this new syntax highlighter.",[37,143441,143442,143443,143448],{},"Should I use ",[677,143444,143447],{"href":143445,"rel":143446},"https://disqus.com/",[681],"Disqus"," comments (free vs paid) or roll my own with Firebase?",[37,143450,143451],{},"I want to display tweets in a post.",[37,143453,143454],{},"When I share a post on social media there are some meta tags to customize the display.",[37,143456,143457],{},"SSL: I need to make sure the entire site runs on https.",[26,143459,1499],{"id":1498},[22,143461,143462,143463,2755],{},"I still have some things to think about but I would love to hear your thoughts on this. If you have migrated from WordPress to Gridsome I would really love to hear what that process was like for you. If you're interested in hearing how I decided on Gridsome or how I created this site, stay tuned! In the meantime, you can check out the source for this site ",[677,143464,8441],{"href":143465,"rel":143466},"https://github.com/cfaddict/danvega-me/",[681],[22,143468,80332,143469,82545],{},[36006,143470],{},[1527,143472,143473],{},"html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}",{"title":57,"searchDepth":76,"depth":76,"links":143475},[143476,143481,143487,143488],{"id":60034,"depth":76,"text":143077,"children":143477},[143478,143479,143480],{"id":60043,"depth":82,"text":143089},{"id":143110,"depth":82,"text":143111},{"id":143144,"depth":82,"text":143145},{"id":113419,"depth":76,"text":104836,"children":143482},[143483,143484,143485,143486],{"id":143200,"depth":82,"text":143201},{"id":143211,"depth":82,"text":143212},{"id":29692,"depth":82,"text":130541},{"id":143407,"depth":82,"text":143408},{"id":143418,"depth":76,"text":143419},{"id":1498,"depth":76,"text":1499},{"_id":143490,"path":143491,"title":143492,"description":143492,"meta":143493,"body":143498},"content/blog/2019/01/01/happy-new-year-my-2019-goals.md","/blog/2019/01/01/happy-new-year-my-2019-goals","Happy New Year! My 2019 Goals",{"slug":143494,"date":143495,"published":13,"tags":143496,"author":-1,"cover":143497,"excerpt":-1},"happy-new-year-my-2019-goals","2019-01-01T08:38:10-05:00",[33475],"./2019-cover.png",{"type":19,"value":143499,"toc":143799},[143500,143509,143512,143516,143519,143541,143545,143548,143565,143569,143572,143574,143577,143582,143584,143587,143669,143675,143679,143682,143685,143699,143707,143710,143714,143721,143725,143728,143731,143735,143738,143743,143758,143762,143765,143767,143770,143775,143779,143788,143794,143796],[22,143501,143502,143503,143508],{},"It is hard to believe that another year has come and gone. After I spent some time last week ",[677,143504,143507],{"href":143505,"rel":143506},"https://therealdanvega.com/blog/2018/12/24/my-2018-year-in-review",[681],"reflecting on 2018"," it's time we look ahead to the new year and set some new goals.",[22,143510,143511],{},"If you read my last post you know that my free time to work on side projects and learn new things is minimal these days. With that in mind, my goals have to be smart and planned out this year. These are my goals for 2019 in no specific order.",[26,143513,143515],{"id":143514},"personal-goals","Personal Goals",[22,143517,143518],{},"The best thing I did in 2018 was giving up soda. I was drinking too much Diet Coke in 2017 and I decided to completely give it up this. I am taking this into 2019 and absolutely no soda is for the entire year. At this point health is very important to me now and going forward so its going to be the majority of my personal goals.",[915,143520,143521,143524,143535,143538],{},[37,143522,143523],{},"No Diet Coke or soda of any kind",[37,143525,143526,143527],{},"Exercise 3-5x a week.\n",[915,143528,143529,143532],{},[37,143530,143531],{},"Minimum of 12x per month",[37,143533,143534],{},"Run 10 miles a week when possible (I live in Cleveland)",[37,143536,143537],{},"Be a good person & be a great role model for my daughter.",[37,143539,143540],{},"Get back to journaling every morning.",[636,143542,143544],{"id":143543},"household-projects","Household Projects",[22,143546,143547],{},"There are a lot of projects around the house that need to get done but these are the projects that need my attention in 2019.",[915,143549,143550,143553,143556,143559,143562],{},[37,143551,143552],{},"Baby proof the entire house",[37,143554,143555],{},"New carpet upstairs and downstairs",[37,143557,143558],{},"New bathtub in the main bathroom.",[37,143560,143561],{},"Update the garage, add storage and update gym.",[37,143563,143564],{},"Continue with updates to the office",[26,143566,143568],{"id":143567},"learning-goals","Learning Goals",[22,143570,143571],{},"I say this every year but I am a serial learner. I LOVE to learn new things and then tell all of you about what I just learned. WIth that most of my goals this year revolve around investing in myself and continuing to learn.",[636,143573,37756],{"id":86219},[22,143575,143576],{},"Last year I had a goal of 30 books to read and while I got out to a hot start it came to a complete stop once my daughter was born. This year I am going to dial back the goal to an easy 8 books. I already have 4 that I want to read to start the year off so I will start with those and move forward.",[22,143578,143579],{},[653,143580],{"alt":57,"src":143581},"./books-bookstore-book-reading-159711-1024x681.jpeg",[636,143583,37576],{"id":86245},[22,143585,143586],{},"I absolutely love listening to podcasts on my way to work, at work, going home from work and If I can get back to the gym with a treadmill I will use it there as well. There is just something about being entertained and learning something new at the same time. With that these are some of my favorite podcasts but certainly not my entire list. If you have any suggestions for podcasts I would love to hear them below.",[915,143588,143589,143596,143603,143610,143617,143624,143631,143638,143643,143648,143655,143662],{},[37,143590,143591],{},[677,143592,143595],{"href":143593,"rel":143594},"https://www.indiehackers.com/",[681],"Indie Hackers",[37,143597,143598],{},[677,143599,143602],{"href":143600,"rel":143601},"https://syntax.fm/",[681],"Syntax",[37,143604,143605],{},[677,143606,143609],{"href":143607,"rel":143608},"https://www.hanselminutes.com/",[681],"Hansel Minutes",[37,143611,143612],{},[677,143613,143616],{"href":143614,"rel":143615},"https://softwareengineeringdaily.com/",[681],"Software Engineering Dialy",[37,143618,143619],{},[677,143620,143623],{"href":143621,"rel":143622},"https://changelog.com/podcast",[681],"The Changelog",[37,143625,143626],{},[677,143627,143630],{"href":143628,"rel":143629},"https://changelog.com/jsparty",[681],"JS Party",[37,143632,143633],{},[677,143634,143637],{"href":143635,"rel":143636},"http://www.fullstackradio.com/",[681],"Full Stack Radio",[37,143639,143640],{},[677,143641,130394],{"href":130392,"rel":143642},[681],[37,143644,143645],{},[677,143646,130401],{"href":130399,"rel":143647},[681],[37,143649,143650],{},[677,143651,143654],{"href":143652,"rel":143653},"https://www.codenewbie.org/podcast",[681],"Code Newbie",[37,143656,143657],{},[677,143658,143661],{"href":143659,"rel":143660},"https://techguylabs.com/",[681],"The Tech Guy with Leo Laporte",[37,143663,143664],{},[677,143665,143668],{"href":143666,"rel":143667},"https://tim.blog/podcast/",[681],"The Tim Ferris Show",[22,143670,143671],{},[653,143672],{"alt":143673,"src":143674},"2018 Goals - Start a podcast","./microphone-audio-computer-sound-recording-55800-1-1024x680.jpeg",[636,143676,143678],{"id":143677},"things-to-learn","Things to Learn",[22,143680,143681],{},"As always my \"stuff to learn\" list is a mile long and instead of getting crazy ambitious here I am going to narrow it down to 3 things I want to learn in 2019.",[28831,143683,105537],{"id":143684},"vuejs",[22,143686,143687,143688,143692,143693,143698],{},"I already have a solid foundation in Vue but I will be doubling down on Vue in 2019. We are teaching Vue this year at work to our ",[677,143689,143691],{"href":107914,"rel":143690},[681],"bootcamp students"," and I am running a ",[677,143694,143697],{"href":143695,"rel":143696},"https://www.meetup.com/vuecle/",[681],"local meetup group called VueCLE",". This will involve diving deep into everything Vue related and becoming an expert at things I haven't learned yet like Nuxt.js.",[22,143700,143701],{},[677,143702,143704],{"href":117748,"rel":143703},[681],[653,143705],{"alt":105537,"src":143706},"./2018-12-29_06-31-02.png",[22,143708,143709],{},"I don't think I have ever been as excited about anything as a programmer as I am about where JavaScript is heading and a framework like Vue.",[28831,143711,143713],{"id":143712},"live-video","Live Video",[22,143715,143716,143717,2755],{},"My life's work is centered around delivering content. Live video is becoming more and more popular and the tools available to make this easy to use and available to everyone is just getting better and better. I want to start creating some live streams on platforms like YouTube, Facebook & Twitch. There is some awesome software for helping you do this on the Mac called ",[677,143718,143720],{"href":126721,"rel":143719},[681],"ecamm live",[22,143722,143723],{},[653,143724],{"alt":57,"src":52899},[22,143726,143727],{},"SAMSUNG CSC",[22,143729,143730],{},"I need to learn how to use that software, the different platforms and define what my live sessions will look like and how often to go live. This is going to be a fun process so look for more of this in 2019.",[28831,143732,143734],{"id":143733},"the-cloud","The Cloud",[22,143736,143737],{},"I realize this last one is kind of vague but it needs to be. I don't know a whole lot about DevOps or the cloud so I don't know what I need to know. What I do know is that I like to build things and when I build things I need a way to get them out to the public for everyone to see. I am fascinated with all things AWS and just the amount of services they provide.",[22,143739,143740],{},[653,143741],{"alt":57,"src":143742},"./aws-logo.png",[22,143744,143745,143746,143751,143752,143757],{},"I was recently introduced to ",[677,143747,143750],{"href":143748,"rel":143749},"http://www.acloud.guru",[681],"A Cloud Guru"," and I will be going through some of their courses to get caught up on all things \"cloud\". I was also really inspired by ",[677,143753,143756],{"href":143754,"rel":143755},"https://acloud.guru/series/serverlessconf-sf-2018/view/6c7c00f8-3183-db34-83fc-f8931e070da5",[681],"a talk given from the founder and CEO of A Cloud Guru"," on how they built their entire platform on serverless.",[26,143759,143761],{"id":143760},"proffessional-goals","Proffessional Goals",[22,143763,143764],{},"At Tech Elevator we have some exciting things happening this year that I can't talk about just yet so I am going to leave you in suspense but I hope to talk more about these as the year goes on.",[636,143766,31404],{"id":37521},[22,143768,143769],{},"My main focus at my day job and outside of work is content creation. Outside of work I really need to define what that looks like. I have a blog, YouTube Channel, Courses and I feel like I am all over the place with this. I'm not quite sure blogging makes much sense for me anymore except maybe to talk about a video I posted or a course I created. I just don't know, I have a lot of questions here that I need to answer. When I figure out what this looks like I will share it with you.",[22,143771,143772],{},[653,143773],{"alt":37949,"src":143774},"./pexels-photo-1024x682.jpg",[636,143776,143778],{"id":143777},"side-hustle","Side Hustle",[22,143780,143781,143782,143787],{},"Last year I started a company called ",[677,143783,143786],{"href":143784,"rel":143785},"https://codemonkeyu.com/",[681],"CodeMonkey LLC"," just so I could separate what I was making teaching online from my personal income. I haven't done anything with this yet and I am not sure that I will. I did reach out the person who owns the dot com domain and he offered to sell it to me for 100k. So I guess you could say I got that going for me.",[22,143789,143790],{},[653,143791],{"alt":143792,"src":143793},"Code Monkey, LLC","./badge-logo-1024x1024.png",[26,143795,1499],{"id":1498},[22,143797,143798],{},"I just want to wish all of you a happy New Year! I wish all of you and your families continued health and happiness. 2019 looks to be an exciting year for me personally and professionally and I am looking forward to it.",{"title":57,"searchDepth":76,"depth":76,"links":143800},[143801,143804,143809,143813],{"id":143514,"depth":76,"text":143515,"children":143802},[143803],{"id":143543,"depth":82,"text":143544},{"id":143567,"depth":76,"text":143568,"children":143805},[143806,143807,143808],{"id":86219,"depth":82,"text":37756},{"id":86245,"depth":82,"text":37576},{"id":143677,"depth":82,"text":143678},{"id":143760,"depth":76,"text":143761,"children":143810},[143811,143812],{"id":37521,"depth":82,"text":31404},{"id":143777,"depth":82,"text":143778},{"id":1498,"depth":76,"text":1499},{"_id":143815,"path":143816,"title":143817,"description":143817,"meta":143818,"body":143823},"content/blog/2018/12/24/my-2018-year-in-review.md","/blog/2018/12/24/my-2018-year-in-review","My 2018 Year in Review",{"slug":143819,"date":143820,"published":13,"tags":143821,"author":-1,"cover":143822,"excerpt":-1},"my-2018-year-in-review","2018-12-24T08:52:12-05:00",[33475],"./2018-cover.png",{"type":19,"value":143824,"toc":144031},[143825,143828,143831,143836,143839,143848,143853,143856,143858,143861,143898,143902,143905,143909,143912,143918,143922,143925,143930,143934,143937,143941,143944,143950,143954,143957,143959,143962,143966,143970,143979,143983,143992,143996,144000,144003,144007,144010,144014,144018,144021,144026,144028],[22,143826,143827],{},"It is that time again friends. Time to find out what goals I fell short of and which ones I made good on. This is my 2018 year in review and in a couple weeks I will posting my goals for 2019.",[22,143829,143830],{},"This year was life-changing as my wife and I welcomed our first child, Isabella Margaret Vega into the world. I never knew I could love anything as much as I love this little girl and she was completely changed my life, for the better.",[22,143832,143833],{},[653,143834],{"alt":57,"src":143835},"./UNADJUSTEDNONRAW_thumb_1e55-768x1024.jpg",[22,143837,143838],{},"I think I could pretty much end the post there. I knew having a child would be life-changing but I didn't understand just how much life would change. I use to have so much time to work on a project or learning a new skill but that for the most part has gone out the window.",[22,143840,143841,143842,143847],{},"I also ",[677,143843,143846],{"href":143844,"rel":143845},"https://therealdanvega.com/blog/2018/09/07/i-am-joining-tech-elevator",[681],"joined Tech Elevator"," this year and looking back on it that was certainly the right decision for me. I love the people that I work with, the mission that we have and seeing our students transform their lives in just 14 weeks.",[22,143849,143850],{},[653,143851],{"alt":57,"src":143852},"./0-1024x995.jpeg",[22,143854,143855],{},"I am going to revisit each of the goals I had last year and let you know whether I completed them or not. If you didn't already guess I fell short on a lot of them but I am glad I did because some of them are no longer goals I want to pursue.",[26,143857,143515],{"id":143514},[22,143859,143860],{},"For the most part, I met my personal goals. I didn't drink a single ounce of soda in 2108 and I am pretty proud of that one. The only one I didn't complete here was running 20 miles per week. That was probably pretty ambitious considereing I spend 3-4 days a week in the gym.",[915,143862,143863,143866,143869,143872,143889,143892,143895],{},[37,143864,143865],{},"Exercise 3-5x per week",[37,143867,143868],{},"Run 20 miles per week (When the weather breaks here in Ohio)",[37,143870,143871],{},"No more soda",[37,143873,143874,143875],{},"Morning routine\n",[915,143876,143877,143880,143883,143886],{},[37,143878,143879],{},"Meditate 5 - 10 minutes",[37,143881,143882],{},"Journal (Productivity and Focus) 5 min",[37,143884,143885],{},"Read for 30 min",[37,143887,143888],{},"Move for 5 minutes",[37,143890,143891],{},"Complete my home office",[37,143893,143894],{},"Complete the nursery (Baby on the way) ",[37,143896,143897],{},"Spend more time with my family",[26,143899,143901],{"id":143900},"education-goals","Education Goals",[22,143903,143904],{},"These were a list of things that I wanted to learn more about. To be fair this list is very long but these are what I narrowed it down to in 2018 and looking back on it I made some bad choices here.",[636,143906,143908],{"id":143907},"building-an-ios-app","Building an iOS app",[22,143910,143911],{},"I am not sure why but I have always wanted to build a mobile application and more specifically one for iOS. I have done some coding challenges in Swift and I love the programming language. I haven't however had a good idea for a practical application that I want to build. Until I do so I think this is going to sit on the shelf.",[22,143913,143914],{},[653,143915],{"alt":143916,"src":143917},"2018 Goals - Build an iOS app","./twitter-facebook-together-exchange-of-information-147413-1024x682.jpeg",[636,143919,143921],{"id":143920},"cryptocurrency-blockchain","Cryptocurrency & Blockchain",[22,143923,143924],{},"In one of my best moves of 2018 I didn't purchase any bitcoin which turned out to be a huge win. With that said I am still a big believer in the blockchain and want to learn more. Just like the iOS skill though I haven't found a practical need to learn anything in this space so for now it's probably going to be down on my list.",[22,143926,143927],{},[653,143928],{"alt":57,"src":143929},"./pexels-photo-730569-1024x768.jpeg",[636,143931,143933],{"id":143932},"alexa-skills","Alexa Skills",[22,143935,143936],{},"1 year later and I love the 2 Echo's that we have in our house. I mainly use them to play music, books or podcasts in different rooms but there are also a few skills that I am using. This is another instance of me wanting to learn something just to learn something with no practical reason to learn it. This will go on the backlog but stay pretty hi.",[636,143938,143940],{"id":143939},"marketing","Marketing",[22,143942,143943],{},"This is something I did spend some time learning at the beginning of the year. I learned a few things about ads, analytics and using different sales funnels. As my time to work on this sort of thing became less and less I started to focus on creating new content and not worrying about the marketing sides of things. I wouldn't call this one a pass but I also wouldn't say its a failure.",[22,143945,143946],{},[653,143947],{"alt":143948,"src":143949},"2018 Goals - Marekting","./pexels-photo-266176-1024x838.jpeg",[636,143951,143953],{"id":143952},"augmented-reality","Augmented Reality",[22,143955,143956],{},"This is one of those technologies that I think is going to continue to get more interesting. I didn't do anything this year in this this space and sadly this is going to go on the backburner.",[26,143958,37756],{"id":86219},[22,143960,143961],{},"I set a pretty lofty goal of reading 30 books and I missed that by a lot. I will blame missing this goal on the baby but I probably could have found a way to read more than 5 books this year.",[22,143963,143964],{},[653,143965],{"alt":57,"src":143581},[26,143967,143969],{"id":143968},"course-development","Course Development",[22,143971,143972,143973,143978],{},"My goal was to release 4 new courses this year and that didn't happen. I did, however, release one new course, ",[677,143974,143977],{"href":143975,"rel":143976},"https://therealdanvega.com/blog/2018/11/20/new-course-getting-started-with-spring-boot-2",[681],"Getting Started with Spring Boot 2."," This was a course I spent a lot of time working on and it ended up being a course with over 13 hours of content so you could say I created 4 courses and wrapped them up into 1. My main focus with my time is going to be spent creating new content but going into 2019 I need to have a better approach. Creating 1 course a year is not going to do much for me.",[636,143980,143982],{"id":143981},"video-production","Video Production",[22,143984,143985,143986,143991],{},"One of my biggest goals for the year was to get better at video production. I am going to call this goal completed. Now, I am no rockstar but I got much better this year at using Adobe Premiere Pro & After Effects. ",[677,143987,143990],{"href":143988,"rel":143989},"https://www.youtube.com/watch?v=cz_7KhbfaNE&t=1s",[681],"Here is the trailer"," I created for my Spring Boot 2 course.",[22,143993,143994],{},[653,143995],{"alt":143673,"src":143674},[26,143997,143999],{"id":143998},"podcasting","Podcasting",[22,144001,144002],{},"I love podcasts. They are one of the tools that I use to learn and advance my skill set. It is that love of learning that has always made me curious about creating a podcast. I don't know what my podcast would be about except that it would be somewhere in the Software Development space. I had no time to pursue this goal this year but it's going towards the top of the list for something I want to explore further in 2019.",[26,144004,144006],{"id":144005},"business-ventures","Business Ventures",[22,144008,144009],{},"Last year I announced that I started a new company called CodeMonkey, LLC. This was mainly to organize what I was doing with content creation. I did have some ideas around this but the theme of this year continues and I didn't get to any of them. I did reach to the person that owns Code Monkey dot com and he said he would sell me the domain for 100k so he could buy a Porsche. So there's that.",[22,144011,144012],{},[653,144013],{"alt":143792,"src":143793},[636,144015,144017],{"id":144016},"toys-for-shots-2018","Toys for Shots 2018",[22,144019,144020],{},"This was another successful year that took up a lot of my free time after September. We ended up having a little over 500 people attend the event and more importantly, we raised 20+ boxes of toys. This provided almost $15,000 worth of toys for children right here in Northeast Ohio. What we accompolish in 1 night is so amazing and I am so thankful for everyone involved.",[22,144022,144023],{},[653,144024],{"alt":57,"src":144025},"./48373962_1086875221489929_4036450573048872960_o-1024x819.jpg",[26,144027,1499],{"id":1498},[22,144029,144030],{},"From a goal setting standpoint this year was a pretty big failure. I think it's more of a failure on my part on being a little bit smarter about the goals that I set. With that I can't wait to share with you what my plans are for 2019. From a personal standpoint I could care less about failing on some of these goals because I have spent the bulk of my year raising the most amazing daughter ever.",{"title":57,"searchDepth":76,"depth":76,"links":144032},[144033,144034,144041,144042,144045,144046,144049],{"id":143514,"depth":76,"text":143515},{"id":143900,"depth":76,"text":143901,"children":144035},[144036,144037,144038,144039,144040],{"id":143907,"depth":82,"text":143908},{"id":143920,"depth":82,"text":143921},{"id":143932,"depth":82,"text":143933},{"id":143939,"depth":82,"text":143940},{"id":143952,"depth":82,"text":143953},{"id":86219,"depth":76,"text":37756},{"id":143968,"depth":76,"text":143969,"children":144043},[144044],{"id":143981,"depth":82,"text":143982},{"id":143998,"depth":76,"text":143999},{"id":144005,"depth":76,"text":144006,"children":144047},[144048],{"id":144016,"depth":82,"text":144017},{"id":1498,"depth":76,"text":1499},{"_id":144051,"path":144052,"title":144053,"description":144053,"meta":144054,"body":144061},"content/blog/2018/12/21/macbook-pro-setup-my-setup-with-detailed-instructions.md","/blog/2018/12/21/macbook-pro-setup-my-setup-with-detailed-instructions","MacBook Pro Setup: My setup with detailed instructions",{"slug":144055,"date":144056,"published":13,"tags":144057,"author":-1,"cover":144060,"excerpt":-1},"macbook-pro-setup-my-setup-with-detailed-instructions","2018-12-21T14:17:22-05:00",[144058,144059],"mac","web development","./tobias-lystad-606045-unsplash-1024x683.jpg",{"type":19,"value":144062,"toc":145345},[144063,144066,144069,144089,144092,144097,144101,144104,144107,144112,144115,144119,144122,144127,144136,144144,144148,144151,144162,144165,144167,144176,144189,144192,144195,144198,144204,144207,144230,144233,144244,144248,144257,144274,144277,144342,144346,144349,144362,144370,144374,144377,144385,144403,144410,144413,144450,144454,144457,144504,144506,144509,144513,144516,144519,144531,144534,144549,144557,144583,144586,144592,144596,144599,144606,144610,144613,144616,144625,144649,144652,144667,144671,144762,144766,144769,144772,144792,144796,144819,144822,144825,144839,144842,144854,144857,144875,144878,144931,144934,144950,144953,144956,144970,144973,144989,144992,145004,145007,145060,145062,145071,145090,145097,145107,145110,145126,145129,145144,145148,145151,145155,145183,145187,145190,145201,145215,145223,145231,145235,145238,145243,145259,145264,145272,145276,145279,145291,145295,145298,145318,145322,145325,145331,145333,145336,145342],[22,144064,144065],{},"I thought I would take some time and document what I do when I get a new machine. This is going to walk through some preferences I configure, applications I install and settings I use for different programs. I am always curious to see how other software developers are setting up their development machines so I figured it would be a good opportunity to share mine.",[22,144067,144068],{},"I am a Software Developer so most of my configuration will be around programming. My current MacBook configuration is as follows:",[915,144070,144071,144074,144077,144080,144083,144086],{},[37,144072,144073],{},"MacBook Pro (13-inch, 2017, Two Thunderbolt 3 ports)",[37,144075,144076],{},"Processor: 2.3 GHz Intel Core i5",[37,144078,144079],{},"Memory: 16 GB",[37,144081,144082],{},"Startup Disk: Macintosh HD",[37,144084,144085],{},"Graphics: Intel Iris Plus Graphics 640 1536 MB",[37,144087,144088],{},"Storage: 500 GB",[22,144090,144091],{},"When you power on your MacBook you are going to run through the setup. I usually configure anything I can here like Wi-fi & Apple ID. The rest of the instructions will assume you made it through the setup and are on the desktop.",[22,144093,144094],{},[4534,144095,144096],{},"If you don't want to have to wait for disk encryption (error in macOS updates section) don't select that during setup and perform that at a later time",[26,144098,144100],{"id":144099},"macos-updates","macOS Updates",[22,144102,144103],{},"The first thing I do is run any updates that are available. In my case the laptop came preloaded with macOS Sierra and macOS Mojave has been released.",[22,144105,144106],{},"So my first step is going to be to update to macOS Mojave. You can download this and run this from the App Store.",[22,144108,144109],{},[4534,144110,144111],{},"Error: You may not install to this volume because it is currently being encrypted",[22,144113,144114],{},"If you get the error above than you chose the option of encrypting your hard drive during setup. Make sure you have the power plugged in and go to System Preferences > Security & Privacy > File Vault. From there you can see the progress of encryption and when its complete you can launch the macOS Mojave installer.",[26,144116,144118],{"id":144117},"app-store","App Store",[22,144120,144121],{},"If you have signed in with your Apple Id you will be taken directly to the Apple Store. This is a good time to run any updates that are available.",[22,144123,144124],{},[4534,144125,144126],{},"You might run into issues here because the apple id that downloaded them is not you. In this case I just removed those apps I am not using (GarageBand,Numbers,Pages,iMovie…)",[22,144128,144129,144130,144135],{},"To remove applications I like installing ",[677,144131,144134],{"href":144132,"rel":144133},"https://freemacsoft.net/appcleaner/",[681],"AppCleaner",". This will make sure that the application and any related files will be removed.",[22,144137,144138,144139],{},"Another application I really love for this and so many other things is ",[677,144140,144143],{"href":144141,"rel":144142},"https://macpaw.com/cleanmymac",[681],"Clean My Mac",[636,144145,144147],{"id":144146},"garage-band","Garage Band",[22,144149,144150],{},"Unfortunately AppCleaner only works for 3rd party installed applications and wont remove GarageBand. You need to remove this manually and while there are a few more locations these are the main 3 you should remove. If you're going to use apple sound affects in other programs please read up on this before deleting.",[915,144152,144153,144156,144159],{},[37,144154,144155],{},"/Applications/GarageBand.app",[37,144157,144158],{},"/Library/Application Support/GarageBand/",[37,144160,144161],{},"/Library/Audio/Apple Loops/Apple/",[22,144163,144164],{},"Empty Trash",[26,144166,93066],{"id":93065},[22,144168,144169,144170,144175],{},"Anything I can install using ",[677,144171,144174],{"href":144172,"rel":144173},"https://brew.sh/",[681],"HomeBrew"," I will. Before you install HomeBrew though you need to install the xcode command line utilities. Open up a new terminal and type the following command. Even if you plan on installing xcode I believe you still need to install these now as they moved them out of the standard installation.",[52,144177,144179],{"className":1663,"code":144178,"language":1665,"meta":57,"style":57},"xcode-select --install\n",[59,144180,144181],{"__ignoreMap":57},[62,144182,144183,144186],{"class":64,"line":65},[62,144184,144185],{"class":122},"xcode-select",[62,144187,144188],{"class":149}," --install\n",[22,144190,144191],{},"The Command Line Tools Package is a small self-contained package available for download separately from Xcode and that allows you to do command line development in OS X. It consists of two components: OS X SDK and command-line tools such as Clang, which are installed in /usr/bin.",[636,144193,144174],{"id":144194},"homebrew",[22,144196,144197],{},"As I said earlier I use HomeBrew to install anything that it can install. I recently found out about cask which makes my life so much easier. If you normally use brew to install something like google-chrome you know that you have to then drag it into the applications folder. If you you use cask it will not only download the package but also move it into the applications folder.",[22,144199,144200],{},[677,144201,144203],{"href":144172,"rel":144202},[681],"HomeBrew Website",[22,144205,144206],{},"Installation:",[52,144208,144210],{"className":1663,"code":144209,"language":1665,"meta":57,"style":57},"/usr/bin/ruby -e \"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)\"\n",[59,144211,144212],{"__ignoreMap":57},[62,144213,144214,144217,144220,144223,144225,144227],{"class":64,"line":65},[62,144215,144216],{"class":122},"/usr/bin/ruby",[62,144218,144219],{"class":149}," -e",[62,144221,144222],{"class":1675}," \"$(",[62,144224,18163],{"class":122},[62,144226,111139],{"class":149},[62,144228,144229],{"class":1675}," https://raw.githubusercontent.com/Homebrew/install/master/install)\"\n",[22,144231,144232],{},"Post Installation",[915,144234,144235,144238,144241],{},[37,144236,144237],{},"If you need help with brew you can run brew help.",[37,144239,144240],{},"brew update - You shouldn't have anything to update but its good to check.",[37,144242,144243],{},"brew search 'term' to search for brews",[26,144245,144247],{"id":144246},"terminal-bash-iterm","Terminal / Bash / iTerm",[22,144249,144250,144251,144256],{},"Now that we have HomeBrew installed it's time to start installing some software. The default version of bash is v3.2 and I want to go ahead and upgrade this to 4.x. There is a ",[677,144252,144255],{"href":144253,"rel":144254},"http://clubmate.fi/upgrade-to-bash-4-in-mac-os-x/",[681],"really good guide"," here that you can follow to upgrade bash.",[915,144258,144259,144262,144265,144268],{},[37,144260,144261],{},"bash -v (bash-3.2)",[37,144263,144264],{},"brew install bash",[37,144266,144267],{},"if you close terminal or open a new tab it will show 4.4 but this still isn't the default version.",[37,144269,144270,144273],{},[646,144271,144272],{},"which bash"," will show you what bash you're using.",[22,144275,144276],{},"Now that we have bash updated we need to make that our default shell. To do so you need to edit /etc/shells",[52,144278,144280],{"className":1663,"code":144279,"language":1665,"meta":57,"style":57},"sudo vi /etc/shells\nadd the path /usr/local/bin/bash\ncomment out the others\n\nChange to the new shell\nchsh -s /usr/local/bin/bash \n",[59,144281,144282,144293,144304,144315,144319,144333],{"__ignoreMap":57},[62,144283,144284,144287,144290],{"class":64,"line":65},[62,144285,144286],{"class":122},"sudo",[62,144288,144289],{"class":1675}," vi",[62,144291,144292],{"class":1675}," /etc/shells\n",[62,144294,144295,144297,144299,144301],{"class":64,"line":76},[62,144296,5806],{"class":122},[62,144298,53632],{"class":1675},[62,144300,60383],{"class":1675},[62,144302,144303],{"class":1675}," /usr/local/bin/bash\n",[62,144305,144306,144308,144310,144312],{"class":64,"line":82},[62,144307,82999],{"class":122},[62,144309,112907],{"class":1675},[62,144311,53632],{"class":1675},[62,144313,144314],{"class":1675}," others\n",[62,144316,144317],{"class":64,"line":89},[62,144318,79],{"emptyLinePlaceholder":13},[62,144320,144321,144324,144326,144328,144330],{"class":64,"line":95},[62,144322,144323],{"class":122},"Change",[62,144325,57317],{"class":1675},[62,144327,53632],{"class":1675},[62,144329,466],{"class":1675},[62,144331,144332],{"class":1675}," shell\n",[62,144334,144335,144338,144340],{"class":64,"line":101},[62,144336,144337],{"class":122},"chsh",[62,144339,2684],{"class":149},[62,144341,144303],{"class":1675},[636,144343,144345],{"id":144344},"bash-profile","Bash Profile",[22,144347,144348],{},"Now that we have bash updated I need to customize my shell. Look in your home directory and see if you have a .bash_profile and if you don't you can create one using the following command.",[52,144350,144352],{"className":1663,"code":144351,"language":1665,"meta":57,"style":57},"touch .bash_profile\n",[59,144353,144354],{"__ignoreMap":57},[62,144355,144356,144359],{"class":64,"line":65},[62,144357,144358],{"class":122},"touch",[62,144360,144361],{"class":1675}," .bash_profile\n",[22,144363,144364,144365,144369],{},"This is where you can add all kinds of customization's to bash. I have included ",[677,144366,144368],{"href":144367},".bash_profile","my .bash_profile"," in this repository if you want to check it out. As always if you have questions about what's in there please let me know.",[636,144371,144373],{"id":144372},"iterm2","iTerm2",[22,144375,144376],{},"For the longest time I used the terminal and even had a couple of people call me out on it 😂",[22,144378,144379,144380,2755],{},"I am now using iTerm2 full time and I absolutely love it. If you want to find out about some of the features & configurations it gives you please ",[677,144381,144384],{"href":144382,"rel":144383},"https://www.iterm2.com/features.html",[681],"check out their website",[52,144386,144388],{"className":1663,"code":144387,"language":1665,"meta":57,"style":57},"brew cask install iterm2\n",[59,144389,144390],{"__ignoreMap":57},[62,144391,144392,144395,144398,144400],{"class":64,"line":65},[62,144393,144394],{"class":122},"brew",[62,144396,144397],{"class":1675}," cask",[62,144399,32750],{"class":1675},[62,144401,144402],{"class":1675}," iterm2\n",[22,144404,144405,144406],{},"One thing I like to do is customize the colors and a great resource for that is ",[677,144407,144408],{"href":144408,"rel":144409},"https://iterm2colorschemes.com/",[681],[22,144411,144412],{},"This is a list of my favorite color schemes.",[915,144414,144415,144418,144421,144423,144426,144429,144432,144435,144438,144441,144444,144447],{},[37,144416,144417],{},"Dracula",[37,144419,144420],{},"FirefoxDev",[37,144422,50830],{},[37,144424,144425],{},"Grape",[37,144427,144428],{},"Grass",[37,144430,144431],{},"Hipster Green",[37,144433,144434],{},"Homebrew",[37,144436,144437],{},"Man Page",[37,144439,144440],{},"Material",[37,144442,144443],{},"MaterialDark",[37,144445,144446],{},"Novel",[37,144448,144449],{},"OceanicMaterial",[26,144451,144453],{"id":144452},"development-setup","Development Setup",[22,144455,144456],{},"Now that I have a nice looking command line full of features its time to start installing all of the different applications I will use. If you have any questions about any of these or why I install them please see the contact me section below.",[915,144458,144459,144462,144465,144468,144471,144474,144477,144480,144483,144486,144489,144492,144495,144498],{},[37,144460,144461],{},"brew install git",[37,144463,144464],{},"brew cask install google-chrome",[37,144466,144467],{},"brew cask install google-chrome-canary",[37,144469,144470],{},"brew cask install firefox",[37,144472,144473],{},"brew cask install firefox-developer-edition",[37,144475,144476],{},"brew cask install visual-studio-code",[37,144478,144479],{},"brew cask install visual-studio-code-insiders",[37,144481,144482],{},"brew cask install intellij-idea",[37,144484,144485],{},"brew cask install eclipse-java",[37,144487,144488],{},"brew cask install postman",[37,144490,144491],{},"brew cask install docker",[37,144493,144494],{},"brew cask install spectacle",[37,144496,144497],{},"brew install tree",[37,144499,144500],{},[677,144501,144502],{"href":144502,"rel":144503},"https://github.com/sindresorhus/quick-look-plugins",[681],[636,144505,125822],{"id":125821},[22,144507,144508],{},"If you aren't using Visual Studio Code, what the heck are you waiting for? All jokes aside I am sure you have probably heard of code by now and here is a few ways that I customize it.",[28831,144510,144512],{"id":144511},"extensions","Extensions",[22,144514,144515],{},"I used to hate having to install Visual Studio Code on a new machine. The reason for that is I have grown to love the editor so much that I have an extension for everything. I use to install them one by one and when you have to do that 35 times it gets really old.",[22,144517,144518],{},"If you want to get a list of extensions currently installed on your machine you can use the following command.",[52,144520,144522],{"className":1663,"code":144521,"language":1665,"meta":57,"style":57},"code --list-extensions\n",[59,144523,144524],{"__ignoreMap":57},[62,144525,144526,144528],{"class":64,"line":65},[62,144527,59],{"class":122},[62,144529,144530],{"class":149}," --list-extensions\n",[22,144532,144533],{},"The nice thing about that is you can install visual studio code extensions using the command line.",[52,144535,144537],{"className":1663,"code":144536,"language":1665,"meta":57,"style":57},"code --install-extension ${extension-name}\n",[59,144538,144539],{"__ignoreMap":57},[62,144540,144541,144543,144546],{"class":64,"line":65},[62,144542,59],{"class":122},[62,144544,144545],{"class":149}," --install-extension",[62,144547,144548],{"class":72}," ${extension-name}\n",[22,144550,144551,144552,2755],{},"You can pipe the results of your list into a file or if you wish you can grab mine from this repository. Once you have that file you can run the following to install all of your extensions. if you're interested in what extensions I am using I have ",[677,144553,144556],{"href":144554,"rel":144555},"https://github.com/cfaddict/new-macbook-setup/blob/master/vscode-extensions.txt",[681],"included my list of extensions",[52,144558,144560],{"className":1663,"code":144559,"language":1665,"meta":57,"style":57},"cat extensions.txt | xargs -L1 code --install-extension\n",[59,144561,144562],{"__ignoreMap":57},[62,144563,144564,144567,144570,144572,144575,144578,144580],{"class":64,"line":65},[62,144565,144566],{"class":122},"cat",[62,144568,144569],{"class":1675}," extensions.txt",[62,144571,46036],{"class":68},[62,144573,144574],{"class":122}," xargs",[62,144576,144577],{"class":149}," -L1",[62,144579,111495],{"class":1675},[62,144581,144582],{"class":149}," --install-extension\n",[22,144584,144585],{},"After I did this and tweeted about it a lot of people let me know about another extension called sync settings. This will sync all of your visual studio extensions, settings, key bindings, etc across machines.",[22,144587,144588],{},[677,144589,144590],{"href":144590,"rel":144591},"https://marketplace.visualstudio.com/items?itemName=Shan.code-settings-sync",[681],[28831,144593,144595],{"id":144594},"fonts","Fonts",[22,144597,144598],{},"I am a huge fan of the Dank Mono font. I install this and use it for most of my development. If I am writing documentation I will sometimes use a different font, it just depends.",[22,144600,144601],{},[677,144602,144605],{"href":144603,"rel":144604},"https://dank.sh/",[681],"Dank Mono",[28831,144607,144609],{"id":144608},"user-settings","User Settings",[22,144611,144612],{},"I have included my user settings in case you want to see what I use. I don't have much here but will try and update these soon.",[636,144614,141434],{"id":144615},"node-npm",[22,144617,144618,144619,144624],{},"If you're going to install Node I think the best way to do so is by using ",[677,144620,144623],{"href":144621,"rel":144622},"https://github.com/creationix/nvm",[681],"Node Version Manager (NVM)",". This to me has a few advantages over installing it from brew or even directly downloading it from their website.",[915,144626,144627,144638],{},[37,144628,144629,144630],{},"You can install multiple versions of Node\n",[915,144631,144632,144635],{},[37,144633,144634],{},"You can set a default version",[37,144636,144637],{},"You can switch between these versions easily",[37,144639,144640,144641],{},"Installs in your home directory\n",[915,144642,144643,144646],{},[37,144644,144645],{},"You don't need special privileges",[37,144647,144648],{},"No more sudo when installing packages globally",[22,144650,144651],{},"Once you have NVM you can install the latest stable realease (v10.12.0 at the time of this writing) using the following command.",[52,144653,144655],{"className":1663,"code":144654,"language":1665,"meta":57,"style":57},"nvm install stable\n",[59,144656,144657],{"__ignoreMap":57},[62,144658,144659,144662,144664],{"class":64,"line":65},[62,144660,144661],{"class":122},"nvm",[62,144663,32750],{"class":1675},[62,144665,144666],{"class":1675}," stable\n",[28831,144668,144670],{"id":144669},"global-packages-to-install","Global Packages to Install",[52,144672,144674],{"className":1663,"code":144673,"language":1665,"meta":57,"style":57},"npm install -g tldr\nnpm install -g typescript\nnpm install -g @vue/cli\nnpm install -g vuepress\nnpm install -g @angular/cli\nnpm install -g eslint\nnpm install -g gitbook-cli\nnpm install -g lodash\n",[59,144675,144676,144687,144698,144708,144719,144729,144740,144751],{"__ignoreMap":57},[62,144677,144678,144680,144682,144684],{"class":64,"line":65},[62,144679,32645],{"class":122},[62,144681,32750],{"class":1675},[62,144683,51606],{"class":149},[62,144685,144686],{"class":1675}," tldr\n",[62,144688,144689,144691,144693,144695],{"class":64,"line":76},[62,144690,32645],{"class":122},[62,144692,32750],{"class":1675},[62,144694,51606],{"class":149},[62,144696,144697],{"class":1675}," typescript\n",[62,144699,144700,144702,144704,144706],{"class":64,"line":82},[62,144701,32645],{"class":122},[62,144703,32750],{"class":1675},[62,144705,51606],{"class":149},[62,144707,129739],{"class":1675},[62,144709,144710,144712,144714,144716],{"class":64,"line":89},[62,144711,32645],{"class":122},[62,144713,32750],{"class":1675},[62,144715,51606],{"class":149},[62,144717,144718],{"class":1675}," vuepress\n",[62,144720,144721,144723,144725,144727],{"class":64,"line":95},[62,144722,32645],{"class":122},[62,144724,32750],{"class":1675},[62,144726,51606],{"class":149},[62,144728,51609],{"class":1675},[62,144730,144731,144733,144735,144737],{"class":64,"line":101},[62,144732,32645],{"class":122},[62,144734,32750],{"class":1675},[62,144736,51606],{"class":149},[62,144738,144739],{"class":1675}," eslint\n",[62,144741,144742,144744,144746,144748],{"class":64,"line":107},[62,144743,32645],{"class":122},[62,144745,32750],{"class":1675},[62,144747,51606],{"class":149},[62,144749,144750],{"class":1675}," gitbook-cli\n",[62,144752,144753,144755,144757,144759],{"class":64,"line":113},[62,144754,32645],{"class":122},[62,144756,32750],{"class":1675},[62,144758,51606],{"class":149},[62,144760,144761],{"class":1675}," lodash\n",[636,144763,144765],{"id":144764},"git-config","Git Config",[22,144767,144768],{},"There is usually a default install of git but we used brew to install the latest earlier. Now that we are on the latest version of git we need to do a little configuration.",[22,144770,144771],{},".gitconfig",[915,144773,144774,144781,144784],{},[37,144775,144776,144777,4498],{},"git config --global user.email \"",[677,144778,144780],{"href":144779},"mailto:dan@techelevator.com","dan@techelevator.com",[37,144782,144783],{},"git config --global user.name \"Dan Vega\"",[37,144785,144786,144787],{},"Aliases\n",[915,144788,144789],{},[37,144790,144791],{},"git config --global alias.add-commit '!git add -A && git commit'",[636,144793,144795],{"id":144794},"databases","Databases",[915,144797,144798,144805,144812],{},[37,144799,144800],{},[677,144801,144804],{"href":144802,"rel":144803},"https://www.postgresql.org/",[681],"PostgreSQL",[37,144806,144807],{},[677,144808,144811],{"href":144809,"rel":144810},"https://www.mongodb.com/",[681],"Mongodb",[37,144813,144814],{},[677,144815,144818],{"href":144816,"rel":144817},"https://www.mysql.com/",[681],"MySQL",[28831,144820,144804],{"id":144821},"postgresql",[22,144823,144824],{},"The easiest way to install PostgreSQL is by using HomeBrew.",[52,144826,144828],{"className":1663,"code":144827,"language":1665,"meta":57,"style":57},"brew install postgresql\n",[59,144829,144830],{"__ignoreMap":57},[62,144831,144832,144834,144836],{"class":64,"line":65},[62,144833,144394],{"class":122},[62,144835,32750],{"class":1675},[62,144837,144838],{"class":1675}," postgresql\n",[22,144840,144841],{},"When this is done installing you can have it start up automatically using the following command.",[52,144843,144845],{"className":1663,"code":144844,"language":1665,"meta":57,"style":57},"brew postgresql-upgrade-database\n",[59,144846,144847],{"__ignoreMap":57},[62,144848,144849,144851],{"class":64,"line":65},[62,144850,144394],{"class":122},[62,144852,144853],{"class":1675}," postgresql-upgrade-database\n",[22,144855,144856],{},"I don't need it that often so when I want to run it I can run the following command:",[52,144858,144860],{"className":1663,"code":144859,"language":1665,"meta":57,"style":57},"pg_ctl -D /usr/local/var/postgres start\n",[59,144861,144862],{"__ignoreMap":57},[62,144863,144864,144867,144869,144872],{"class":64,"line":65},[62,144865,144866],{"class":122},"pg_ctl",[62,144868,32753],{"class":149},[62,144870,144871],{"class":1675}," /usr/local/var/postgres",[62,144873,144874],{"class":1675}," start\n",[22,144876,144877],{},"Better yet I can add a few of aliases to my bash profile to make this even easier.",[52,144879,144881],{"className":1663,"code":144880,"language":1665,"meta":57,"style":57},"alias start_postgres=\"pg_ctl -D /usr/local/var/postgres start\"\nalias stop_postgres=\"pg_ctl -D /usr/local/var/postgres stop -s -m fast\"\nalias pgup=\"start_postgres\"\nalias pgdown=\"stop_postgres\"\n",[59,144882,144883,144895,144907,144919],{"__ignoreMap":57},[62,144884,144885,144887,144890,144892],{"class":64,"line":65},[62,144886,87416],{"class":68},[62,144888,144889],{"class":72}," start_postgres",[62,144891,146],{"class":68},[62,144893,144894],{"class":1675},"\"pg_ctl -D /usr/local/var/postgres start\"\n",[62,144896,144897,144899,144902,144904],{"class":64,"line":76},[62,144898,87416],{"class":68},[62,144900,144901],{"class":72}," stop_postgres",[62,144903,146],{"class":68},[62,144905,144906],{"class":1675},"\"pg_ctl -D /usr/local/var/postgres stop -s -m fast\"\n",[62,144908,144909,144911,144914,144916],{"class":64,"line":82},[62,144910,87416],{"class":68},[62,144912,144913],{"class":72}," pgup",[62,144915,146],{"class":68},[62,144917,144918],{"class":1675},"\"start_postgres\"\n",[62,144920,144921,144923,144926,144928],{"class":64,"line":89},[62,144922,87416],{"class":68},[62,144924,144925],{"class":72}," pgdown",[62,144927,146],{"class":68},[62,144929,144930],{"class":1675},"\"stop_postgres\"\n",[22,144932,144933],{},"Our students also use DBVisualizer so I like to have that installed as well.",[52,144935,144937],{"className":1663,"code":144936,"language":1665,"meta":57,"style":57},"brew cask install dbvisualizer\n",[59,144938,144939],{"__ignoreMap":57},[62,144940,144941,144943,144945,144947],{"class":64,"line":65},[62,144942,144394],{"class":122},[62,144944,144397],{"class":1675},[62,144946,32750],{"class":1675},[62,144948,144949],{"class":1675}," dbvisualizer\n",[28831,144951,144818],{"id":144952},"mysql",[22,144954,144955],{},"To get started with MySQL run the following command:",[52,144957,144959],{"className":1663,"code":144958,"language":1665,"meta":57,"style":57},"brew install mysql\n",[59,144960,144961],{"__ignoreMap":57},[62,144962,144963,144965,144967],{"class":64,"line":65},[62,144964,144394],{"class":122},[62,144966,32750],{"class":1675},[62,144968,144969],{"class":1675}," mysql\n",[22,144971,144972],{},"To have MySQL automatically run when you computer starts:",[52,144974,144976],{"className":1663,"code":144975,"language":1665,"meta":57,"style":57},"brew services start mysql\n",[59,144977,144978],{"__ignoreMap":57},[62,144979,144980,144982,144985,144987],{"class":64,"line":65},[62,144981,144394],{"class":122},[62,144983,144984],{"class":1675}," services",[62,144986,111445],{"class":1675},[62,144988,144969],{"class":1675},[22,144990,144991],{},"OR you can start / stop it manually",[52,144993,144995],{"className":1663,"code":144994,"language":1665,"meta":57,"style":57},"mysql.server start\n",[59,144996,144997],{"__ignoreMap":57},[62,144998,144999,145002],{"class":64,"line":65},[62,145000,145001],{"class":122},"mysql.server",[62,145003,144874],{"class":1675},[22,145005,145006],{},"To be consistent with our PostgreSQL we can create a few aliases.",[52,145008,145010],{"className":1663,"code":145009,"language":1665,"meta":57,"style":57},"alias start_mysql=\"mysql.server start\"\nalias stop_mysql=\"mysql.server stop\"\nalias mysqlup=\"start_mysql\"\nalias mysqldown=\"stop_mysql\"\n",[59,145011,145012,145024,145036,145048],{"__ignoreMap":57},[62,145013,145014,145016,145019,145021],{"class":64,"line":65},[62,145015,87416],{"class":68},[62,145017,145018],{"class":72}," start_mysql",[62,145020,146],{"class":68},[62,145022,145023],{"class":1675},"\"mysql.server start\"\n",[62,145025,145026,145028,145031,145033],{"class":64,"line":76},[62,145027,87416],{"class":68},[62,145029,145030],{"class":72}," stop_mysql",[62,145032,146],{"class":68},[62,145034,145035],{"class":1675},"\"mysql.server stop\"\n",[62,145037,145038,145040,145043,145045],{"class":64,"line":82},[62,145039,87416],{"class":68},[62,145041,145042],{"class":72}," mysqlup",[62,145044,146],{"class":68},[62,145046,145047],{"class":1675},"\"start_mysql\"\n",[62,145049,145050,145052,145055,145057],{"class":64,"line":89},[62,145051,87416],{"class":68},[62,145053,145054],{"class":72}," mysqldown",[62,145056,146],{"class":68},[62,145058,145059],{"class":1675},"\"stop_mysql\"\n",[636,145061,61356],{"id":36257},[22,145063,145064,145065,145070],{},"This is one of my favorite version managers because I use a lot of the Software Development Kits (SDKs) it manages. If you haven't heard of ",[677,145066,145069],{"href":145067,"rel":145068},"https://sdkman.io/install",[681],"SDKMan check them out here",". This is a list of SDKs I manage using SDKMan.",[915,145072,145073,145075,145077,145079,145081,145083,145086,145088],{},[37,145074,15],{},[37,145076,53590],{},[37,145078,69903],{},[37,145080,54946],{},[37,145082,94504],{},[37,145084,145085],{},"Micronaut",[37,145087,2925],{},[37,145089,135984],{},[22,145091,145092,145093],{},"Here is a full list of SDKs ",[677,145094,145095],{"href":145095,"rel":145096},"https://sdkman.io/sdks",[681],[22,145098,145099,145101,145102,145106],{},[646,145100,144206],{}," curl -s \"",[677,145103,145104],{"href":145104,"rel":145105},"https://get.sdkman.io",[681],"\" | bash",[22,145108,145109],{},"If you just type sdk install candidate it will install the latest stable version or you can install a specific version",[52,145111,145113],{"className":1663,"code":145112,"language":1665,"meta":57,"style":57},"sdk install java 8.0.191-oracle\n",[59,145114,145115],{"__ignoreMap":57},[62,145116,145117,145119,145121,145123],{"class":64,"line":65},[62,145118,36267],{"class":122},[62,145120,32750],{"class":1675},[62,145122,36272],{"class":1675},[62,145124,145125],{"class":1675}," 8.0.191-oracle\n",[22,145127,145128],{},"If you need to get a list of versions you can ask for it:",[52,145130,145132],{"className":1663,"code":145131,"language":1665,"meta":57,"style":57},"sdk list java\n",[59,145133,145134],{"__ignoreMap":57},[62,145135,145136,145138,145141],{"class":64,"line":65},[62,145137,36267],{"class":122},[62,145139,145140],{"class":1675}," list",[62,145142,145143],{"class":1675}," java\n",[636,145145,145147],{"id":145146},"browser-configuration","Browser Configuration",[22,145149,145150],{},"Turn on sync and sign into chrome, this brings all of my bookmarks and extensions. If you have any questions about any of these or why I install them please see the contact me section below.",[22,145152,145153],{},[646,145154,144512],{},[915,145156,145157,145160,145162,145165,145168,145171,145174,145177,145180],{},[37,145158,145159],{},"LastPass",[37,145161,60111],{},[37,145163,145164],{},"Color Picker",[37,145166,145167],{},"LiveReload",[37,145169,145170],{},"uBlock Origin",[37,145172,145173],{},"privacy badger",[37,145175,145176],{},"oneTab",[37,145178,145179],{},"JSONViewer",[37,145181,145182],{},"Vue devtools",[26,145184,145186],{"id":145185},"system-preferences","System Preferences",[22,145188,145189],{},"I have a few things that I customize in preferences:",[22,145191,145192,145195,145197,145198,145200],{},[646,145193,145194],{},"Trackpad:",[36006,145196],{},"\nSystem Preferences > Trackpad > Scroll & Zoom:",[36006,145199],{},"\nI uncheck scroll direction: Natural (It doesn’t feel natural for me)",[22,145202,145203,145206,145208,145209,145211,145212,145214],{},[646,145204,145205],{},"Dock:",[36006,145207],{},"\nSystem Preferences > Dock",[36006,145210],{},"\nChange the size to small and turn on magnification",[36006,145213],{},"\nI also remove all icons from the dock that i don't use",[22,145216,145217,145220,145222],{},[646,145218,145219],{},"Avatar:",[36006,145221],{},"\nSystem Preferences > Users & Groups > Edit Avatar",[22,145224,145225,145228,145230],{},[646,145226,145227],{},"Theme:",[36006,145229],{},"\nmacOS Mojave introduced light & dark themes. I still stick with the light theme even though I use dark themes in almost every editor or program that I use. If you want to change this later simply go to System Preferences > General > Appearance",[26,145232,145234],{"id":145233},"finder","Finder",[22,145236,145237],{},"I like to customize Finder so I can get to all of my most used places quickly.",[22,145239,145240],{},[646,145241,145242],{},"Locations",[915,145244,145245,145248,145251],{},[37,145246,145247],{},"Add Macintosh HD to locations so I can always get to the root hard drive",[37,145249,145250],{},"Home /Users/vega",[37,145252,145253,145254],{},"screenshots (configure screenshot utility to save here)\n",[915,145255,145256],{},[37,145257,145258],{},"open screenshot > options > other location",[22,145260,145261],{},[646,145262,145263],{},"A few tips in finder",[915,145265,145266,145269],{},[37,145267,145268],{},"cmd+shift+h (takes you home)",[37,145270,145271],{},"cmd . (show hidden files and folders)",[26,145273,145275],{"id":145274},"work-setup","Work Setup",[22,145277,145278],{},"These are a list of applications and configuration I need for work. You may not need some of these tools but I thought I would share them here anyways.",[22,145280,145281,145282,145284,145285,145287,145288,145290],{},"Last Pass",[36006,145283],{},"\nSlack",[36006,145286],{},"\nTwitter (App Store)",[36006,145289],{},"\nScreenflow",[636,145292,145294],{"id":145293},"adobe-creative-suite","Adobe Creative Suite",[22,145296,145297],{},"I am huge fan of Adobe and all of their products!",[915,145299,145300,145303,145306,145309,145312,145315],{},[37,145301,145302],{},"Photoshop CC",[37,145304,145305],{},"Illustrator CC",[37,145307,145308],{},"Premiere Pro CC",[37,145310,145311],{},"After Effects CC",[37,145313,145314],{},"Premiere Rush CC",[37,145316,145317],{},"XD",[636,145319,145321],{"id":145320},"windows-10","Windows 10",[22,145323,145324],{},"At work I need to dual boot with macOS and Windows 10. We have a .NET track where the students use Windows (Visual Studio, SQL Server, etc…) and I need to be able to support them. The great thing about this is Apple has made this stupid simple using BootCamp Assistant. If you want to read more about this check out the document below.",[22,145326,145327],{},[677,145328,145329],{"href":145329,"rel":145330},"https://github.com/cfaddict/new-macbook-setup/blob/master/windws10.md",[681],[26,145332,1499],{"id":1498},[22,145334,145335],{},"I had a lot of fun putting this together and I hope you picked up a new trick or 2. Whenever I find something new or a way to improve the installation process I will update the link below if you would like to follow along. Please let me know if I missed something that I should look into or if you have any tips and tricks.",[22,145337,145338],{},[677,145339,145340],{"href":145340,"rel":145341},"https://github.com/cfaddict/new-macbook-setup",[681],[1527,145343,145344],{},"html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}",{"title":57,"searchDepth":76,"depth":76,"links":145346},[145347,145348,145351,145354,145358,145366,145367,145368,145372],{"id":144099,"depth":76,"text":144100},{"id":144117,"depth":76,"text":144118,"children":145349},[145350],{"id":144146,"depth":82,"text":144147},{"id":93065,"depth":76,"text":93066,"children":145352},[145353],{"id":144194,"depth":82,"text":144174},{"id":144246,"depth":76,"text":144247,"children":145355},[145356,145357],{"id":144344,"depth":82,"text":144345},{"id":144372,"depth":82,"text":144373},{"id":144452,"depth":76,"text":144453,"children":145359},[145360,145361,145362,145363,145364,145365],{"id":125821,"depth":82,"text":125822},{"id":144615,"depth":82,"text":141434},{"id":144764,"depth":82,"text":144765},{"id":144794,"depth":82,"text":144795},{"id":36257,"depth":82,"text":61356},{"id":145146,"depth":82,"text":145147},{"id":145185,"depth":76,"text":145186},{"id":145233,"depth":76,"text":145234},{"id":145274,"depth":76,"text":145275,"children":145369},[145370,145371],{"id":145293,"depth":82,"text":145294},{"id":145320,"depth":82,"text":145321},{"id":1498,"depth":76,"text":1499},{"_id":145374,"path":145375,"title":145376,"description":145376,"meta":145377,"body":145381},"content/blog/2018/11/20/new-course-getting-started-with-spring-boot-2.md","/blog/2018/11/20/new-course-getting-started-with-spring-boot-2","New Course: Getting Started with Spring Boot 2",{"slug":145378,"date":145379,"published":13,"tags":57582,"author":-1,"cover":145380,"excerpt":-1},"new-course-getting-started-with-spring-boot-2","2018-11-20T12:39:53-05:00","./teachable_course_graphic-760x428.png",{"type":19,"value":145382,"toc":145464},[145383,145392,145396,145399,145415,145418,145425,145429,145438,145441,145446,145450,145453,145455,145458],[22,145384,145385,145386,145391],{},"I am so excited to announce that ",[677,145387,145390],{"href":145388,"rel":145389},"https://www.udemy.com/spring-boot-2/?couponCode=TRDV_BLOG_10",[681],"my new course is live","! This course was a long time in the making and I am just so thrilled that I get to share it with all of you because I believe it is my best one yet!",[26,145393,145395],{"id":145394},"whats-included-in-the-course","What's Included in the course?",[22,145397,145398],{},"The biggest feedback I received from you was that you wanted a more project focused course instead of some I'm examples. In this course, we are going to build a real application together from start to finish. We are going to use the latest technologies to build out our application including:",[915,145400,145401,145404,145407,145409,145412],{},[37,145402,145403],{},"Spring Framework 5",[37,145405,145406],{},"Spring Boot 2",[37,145408,101222],{},[37,145410,145411],{},"Spring Security ",[37,145413,145414],{},"So Much More",[22,145416,145417],{},"This course is packed with over 10 hours of content and I really hope you enjoy it. I created a short trailer to give you a better idea of what to expect. ",[22,145419,145420],{},[677,145421,145424],{"href":145422,"rel":145423},"https://www.youtube.com/watch?v=cz%5C_7KhbfaNE",[681],"https://www.youtube.com/watch?v=cz\\_7KhbfaNE",[26,145426,145428],{"id":145427},"the-road-to-this-course-launch","The Road to this course launch",[22,145430,145431,145432,145437],{},"If you had a chance to read my ",[677,145433,145436],{"href":145434,"rel":145435},"https://therealdanvega.com/blog/2018/09/24/content-announcements",[681],"content announcements"," in September I gave you a pretty good update as to what has been going on in my life. Trying to find dedicated time to work on this course is probably the biggest reason for the delay of this course. I also wanted to make sure that it wasn't rushed and that I was able to produce the course that you asked for.",[22,145439,145440],{},"I can also tell you that I learned some valuable lesson producing this course. I am in the process of trying to gather my thoughts and put something out there that might help other developers who want to create their own courses. I might not be the best course creator on the planet but I have definitely learned a few things and can hopefully show you what not to do. ",[22,145442,145443],{},[653,145444],{"alt":57,"src":145445},"./wahid-khene-978156-unsplash-1024x594.jpg",[636,145447,145449],{"id":145448},"video-editing","Video Editing",[22,145451,145452],{},"Launching a new course can be extremely gratifying and very stressful at the same time. I don't have this feeling when I write a new blog post or launch a new video on YouTube and it's probably due to the exchange of money. Even under $10 for 10 hours of content, I still feel a sense of Imposter Syndrome. Who am I to ask for money in exchange for knowledge? What if everyone hates my course? What if they don't learn anything? These are all very real feelings that I continue to battle. Thank you to all of you who continue to support my efforts, I appreciate you more than you will ever know. ",[26,145454,1499],{"id":1498},[22,145456,145457],{},"I am really happy to finally get this course into your hands. This is not a final product and I will be working on a few things to improve this course over the next month. If you have feedback (good or bad) please let me hear it so I can make this the best Spring Boot 2 course around. ",[22,145459,145460],{},[677,145461,145463],{"href":145388,"rel":145462},[681],"Get this course for $9.99",{"title":57,"searchDepth":76,"depth":76,"links":145465},[145466,145467,145470],{"id":145394,"depth":76,"text":145395},{"id":145427,"depth":76,"text":145428,"children":145468},[145469],{"id":145448,"depth":82,"text":145449},{"id":1498,"depth":76,"text":1499},{"_id":145472,"path":145473,"title":145474,"description":145474,"meta":145475,"body":145481},"content/blog/2018/11/06/cleveland-women-in-tech-conference-recap.md","/blog/2018/11/06/cleveland-women-in-tech-conference-recap","Cleveland Women in Tech Conference Recap",{"slug":145476,"date":145477,"published":13,"tags":145478,"author":-1,"cover":145480,"excerpt":-1},"cleveland-women-in-tech-conference-recap","2018-11-06T11:59:51-05:00",[105334,145479],"presentation","./IMG_4467.jpg",{"type":19,"value":145482,"toc":145662},[145483,145492,145496,145499,145507,145510,145524,145529,145533,145536,145539,145543,145546,145550,145553,145556,145565,145569,145572,145575,145578,145581,145584,145587,145614,145617,145625,145627,145630,145635,145639,145642,145647,145651,145654,145657,145659],[22,145484,145485,145486,145491],{},"This is going to be just a quick recap of the inaugural ",[677,145487,145490],{"href":145488,"rel":145489},"https://getwitit.org/agenda-cle/",[681],"Cleveland Women in Tech Conference",". I think I will have a little bit of a unique perspective to share on this as I am a presenter at this conference and one of a handful of men in attendance. ",[26,145493,145495],{"id":145494},"opening-keynote","Opening Keynote",[22,145497,145498],{},"The conference was held at Corporate College in Warrensville Heights and last I heard there were about 230+ attendees. The conference in Columbus doubled in year 2 and I really hope we see the same in Cleveland next year. The title of the keynote was \"Making HERstory stick\" and I thought it was an amazing panel discussion with some CIOs from the Cleveland area, each who had some unique perspectives on each of the questions. ",[29685,145500,145501,145504],{},[22,145502,145503],{},"Various industry leading trailblazers from prominent organizations all speak about their journey to leadership and their views on developing and nurturing a welcoming supportive culture of diversity. Discussion will include how innovation in technology changed their organization, delivery to the business and improved results.",[22,145505,145506],{},"Making HERstory stick",[22,145508,145509],{},"The panel was made up of:",[915,145511,145512,145515,145518,145521],{},[37,145513,145514],{},"Sandy Rapp - VP & CIO, The Timken Company",[37,145516,145517],{},"Tracey Petkovic - CIO, Westfield Insurance",[37,145519,145520],{},"Jane Alexander - CIO, The Cleveland Museum of Art",[37,145522,145523],{},"Lisa Ward - VP R&D and Strategy, STERIS Corporation",[22,145525,145526],{},[653,145527],{"alt":57,"src":145528},"./IMG_4469.jpg",[636,145530,145532],{"id":145531},"cio-panel","CIO Panel",[22,145534,145535],{},"I wish I would have had my laptop in front of me to take some notes but I didn't. There were some really great questions asked and some excellent responses.",[22,145537,145538],{},"One thing that really stood out to me that everyone in tech has an obligation to take the businesses requirements and then return to them a solution. You shouldn't be taking solution requests from the business and I agree with this 1000%.",[26,145540,145542],{"id":145541},"sessions-i-attended","Sessions I attended",[22,145544,145545],{},"I'll be honest and say it was pretty tough being one of only a few guys sitting in on each of these sessions today. It's that perspective that gave me a greater appreciation for the women who make it out to these conferences who are mostly filled with men. Not that I didn't already appreciate you but I think I will make a greater effort next time I see someone in that position to make them feel more welcome.",[636,145547,145549],{"id":145548},"the-blockchain-disruption-lisa-turner","The Blockchain Disruption - Lisa Turner",[22,145551,145552],{},"This was a good introduction to Blockchain and cryptocurrency. This is another one of those technologies on my list that I am interested in but at the same time not sure why I am interested in it. I get that the idea of decentralized networks are important for privacy.",[22,145554,145555],{},"What I don't get is where all of this currency actually comes from. I mean I understand the role of miners but It still makes not sense to me how we can just invent currency. As much as we hate the big bad banks I at least know that my money is backed by the federal reserve. ",[22,145557,145558,145559,145564],{},"I did find a stand at the conference for an upcoming conference in ",[677,145560,145563],{"href":145561,"rel":145562},"https://blocklandcleveland.com/solutions",[681],"Cleveland put on by Blockland",". I don't know if I can make it to this but this seems like a really good conference that I would love to attend. ",[636,145566,145568],{"id":145567},"imposter-syndrome-and-how-mentoring-helped-me-combat-it-joanna-hughes","Imposter Syndrome and How mentoring helped me combat it - Joanna Hughes",[22,145570,145571],{},"Joanna is a front-end developer for Vitamix and started the presentation off detailing what Imposter Syndrome is. She started out describing how this affects junior developers and in particular females. ",[22,145573,145574],{},"While both of these are true I want to point out that 20 years into my career I still feel like an imposter at times. I didn't go to some fancy school and I haven't worked for any of the big tech companies that only hire the best of the best. I often get the feeling of who am I and why should anyone listen to me. You just need to keep reassuring yourself that you know what you're doing and to move forward.",[22,145576,145577],{},"She went into a conversation about how mentoring other developers really helped her combat Imposter Syndrome. When you start to see that other developers face the same problems as you do it really helps take down a wall. I thought Joanna did a great job and couldn't believe that it was her first conference talk. You certainly couldn't tell! ",[26,145579,104727],{"id":145580},"my-presentation",[22,145582,145583],{},"My presentation was titled \"The rest of your life starts today. A guide to continued learning and personal growth.\" and I had the session right after lunch. Now, being a conference attendee pro, I know the sessions right after lunch are tough to stay awake in so I was going to need to be on my game today. ",[22,145585,145586],{},"While I was setting up my presentation the room started to fill up quickly. It filled up so much that everyone started lining the walls and sitting on the floor. Luckily the event staff was on it and wheeled in a ton of chairs. Thanks to everyone for making a guy feel special. ",[915,145588,145589,145594,145599,145604,145609],{},[37,145590,145591],{},[653,145592],{"alt":57,"src":145593},"./IMG_4482.jpg",[37,145595,145596],{},[653,145597],{"alt":57,"src":145598},"./IMG_4481.jpg",[37,145600,145601],{},[653,145602],{"alt":57,"src":145603},"./IMG_4480.jpg",[37,145605,145606],{},[653,145607],{"alt":57,"src":145608},"./IMG_4479.jpg",[37,145610,145611],{},[653,145612],{"alt":57,"src":145613},"./IMG_4478.jpg",[22,145615,145616],{},"I thought my presentation went really well. I practiced enough to where I knew what I was going to talk about in each slide without it feeling rehearsed. I had some great pictures of my daughter in there so even if it wasn't going well I could always fall back on the cute baby.",[22,145618,145619,145620,2755],{},"I thought it was a good pace and my hope is that everyone learned something new yesterday. Thanks again to everyone who attended the presentation, asked questions and laughed at my jokes. If you want to grab the slide deck or the resources I discussed you can ",[677,145621,145624],{"href":145622,"rel":145623},"https://therealdanvega.com/blog/2018/11/05/cleveland-women-in-tech-presentation",[681],"get them here",[636,145626,104491],{"id":104490},[22,145628,145629],{},"A special thanks to my Tech Elevator family for not only helping me with the presentation but also supporting it. They were all there bright and early to volunteer for the conference. You ladies rock! ",[22,145631,145632],{},[653,145633],{"alt":57,"src":145634},"./IMG_4471.jpg",[26,145636,145638],{"id":145637},"finding-mentors","Finding Mentors",[22,145640,145641],{},"I got an interesting question after the presentation. The question is where did my mentors come from and where can we find our mentors at. I feel like I did an ok job answering this in person but there is so much more I want to say on the subject. ",[22,145643,145644],{},[653,145645],{"alt":57,"src":145646},"./neonbrand-618320-unsplash.jpg",[636,145648,145650],{"id":145649},"mentor","Mentor",[22,145652,145653],{},"I am going to try and put together a whole post on this but here is the short answer. The best place to find mentors are in the places where your types of people are. If you're a junior developer and work for a bigger company with lots of developers, this is a great place to start. If you work for a smaller company try to attend local meetup groups where like minded developers might be. ",[22,145655,145656],{},"Another great approach to this is to get involved in an online community. If you are really into the VueJS framework for instance, be where other VueJS developers are. Try getting involved in discussions in the places where they hang out, comment on blog articles and if you can try and contribute back to the community. I have made some amazing friends and developers I look up to just by being involved in a community and attending meetups & conferences. ",[26,145658,1499],{"id":1498},[22,145660,145661],{},"I had such an amazing time at this conference. Weather I'm asked back next year or not my hope is that this conference really grows. I think it's a great place for women in tech to have their voices heard and inspire the next generation of movers and shakers. Round of applause Cleveland, you did really well yesterday :)",{"title":57,"searchDepth":76,"depth":76,"links":145663},[145664,145667,145671,145674,145677],{"id":145494,"depth":76,"text":145495,"children":145665},[145666],{"id":145531,"depth":82,"text":145532},{"id":145541,"depth":76,"text":145542,"children":145668},[145669,145670],{"id":145548,"depth":82,"text":145549},{"id":145567,"depth":82,"text":145568},{"id":145580,"depth":76,"text":104727,"children":145672},[145673],{"id":104490,"depth":82,"text":104491},{"id":145637,"depth":76,"text":145638,"children":145675},[145676],{"id":145649,"depth":82,"text":145650},{"id":1498,"depth":76,"text":1499},{"_id":145679,"path":145680,"title":145681,"description":145681,"meta":145682,"body":145687},"content/blog/2018/11/05/cleveland-women-in-tech-presentation.md","/blog/2018/11/05/cleveland-women-in-tech-presentation","Cleveland Women in Tech Presentation",{"slug":145683,"date":145684,"published":13,"tags":145685,"author":-1,"cover":145686,"excerpt":-1},"cleveland-women-in-tech-presentation","2018-11-05T06:50:29-05:00",[105334,145479],"./merherstory-cover.png",{"type":19,"value":145688,"toc":145857},[145689,145701,145704,145707,145711,145714,145719,145722,145725,145729,145732,145737,145739,145742,145746,145769,145774,145809,145813,145847,145849],[22,145690,145691,145692,145695,145696,145700],{},"Today I have the honor of representing ",[677,145693,104491],{"href":61080,"rel":145694},[681]," and presenting at the very 1st ",[677,145697,145699],{"href":145488,"rel":145698},[681],"getWITit conference in Cleveland",". This conference has been a huge success in Columbus and it is just getting started in Cleveland. ",[22,145702,145703],{},"When I first started my new position at Tech Elevator I was approached about speaking at this conference. I jumped all over this opportunity to represent us at this inaugural conference. ",[22,145705,145706],{},"When we started working on an idea for the presentation I wasn't really sure what we were going to do. A coworker suggested that I talk about something I am passionate about, learning. I thought it was a great idea and ran with it. ",[26,145708,145710],{"id":145709},"presentation-details","Presentation Details",[22,145712,145713],{},"The title of my presentation is \"The rest of your life starts today. A guide to continued learning and personal growth.\"",[22,145715,145716],{},[653,145717],{"alt":57,"src":145718},"./Screen-Shot-2018-11-04-at-9.25.10-PM.png",[22,145720,145721],{},"This is the first time I have ever given a presentation on something that wasn't code related. I mean I found a way to work in some code related slides but that isn't the theme of the presentation.",[22,145723,145724],{},"This presentation is about something I love, learning new skills. It also comes at a great time in my life. I have a newborn at home so time comes at a premium these days. In this presentation I share some of my best tips on how to learn a new skill. After 20 years of \"loving to learn\" I believe that I have a lot to share around this subject. ",[636,145726,145728],{"id":145727},"slide-deck","Slide Deck",[22,145730,145731],{},"I had a lot of fun putting this slide deck together. While it isn't perfect and there are some slides I wish weren't as boring I think for the most part I am happy. They are fun slides and my hope is they will keep the audience engaged. Did I mention there were 52 of them? ",[22,145733,145734],{},[677,145735,145490],{"href":145736},"./Cleveland-Women-in-Tech-Conference.pdf",[636,145738,4098],{"id":4097},[22,145740,145741],{},"Throughout the presentation, I mentioned a number of resources. I wanted this to be a place where I can mention all of those resources so here they are. ",[22,145743,145744],{},[646,145745,37756],{},[915,145747,145748,145755,145762],{},[37,145749,145750],{},[677,145751,145754],{"href":145752,"rel":145753},"https://amzn.to/2OqS87U",[681],"Outliers: The Story of success",[37,145756,145757],{},[677,145758,145761],{"href":145759,"rel":145760},"https://amzn.to/2FoMx2J",[681],"The first 20 hours. How to learn anything fast.",[37,145763,145764],{},[677,145765,145768],{"href":145766,"rel":145767},"https://amzn.to/2OnA7at",[681],"Your best year ever: A 5-stop plan for achieving your most important goals",[22,145770,145771],{},[646,145772,145773],{},"Tools",[915,145775,145776,145783,145790,145797,145802],{},[37,145777,145778],{},[677,145779,145782],{"href":145780,"rel":145781},"https://asana.com/",[681],"Asana",[37,145784,145785],{},[677,145786,145789],{"href":145787,"rel":145788},"https://trello.com/",[681],"Trello",[37,145791,145792],{},[677,145793,145796],{"href":145794,"rel":145795},"https://evernote.com/",[681],"Evernote",[37,145798,145799],{},[677,145800,125822],{"href":141426,"rel":145801},[681],[37,145803,145804],{},[677,145805,145808],{"href":145806,"rel":145807},"https://www.google.com/chrome/",[681],"Google Chrome",[22,145810,145811],{},[646,145812,37816],{},[915,145814,145815,145822,145827,145834,145840],{},[37,145816,145817],{},[677,145818,145821],{"href":145819,"rel":145820},"https://michaelhyatt.com",[681],"Michael Hyatt",[37,145823,145824],{},[677,145825,104491],{"href":107914,"rel":145826},[681],[37,145828,145829],{},[677,145830,145833],{"href":145831,"rel":145832},"https://therealdanvega.com",[681],"The Real Dan Vega",[37,145835,145836],{},[677,145837,104942],{"href":145838,"rel":145839},"http://www.toysforshots.com",[681],[37,145841,145842],{},[677,145843,145846],{"href":145844,"rel":145845},"https://www.audible.com",[681],"Audible",[636,145848,1499],{"id":1498},[22,145850,145851,145852,2755],{},"I really wanted to thank the conference organizers for allowing me to speak. I also want to thank everyone who came out to my presentation. I know my coworkers will be in full force so I also want to thank them for supporting me. If you're interested in having me give this presentation at your next conference or meetup, ",[677,145853,145856],{"href":145854,"rel":145855},"https://therealdanvega.com/contact",[681],"contact me",{"title":57,"searchDepth":76,"depth":76,"links":145858},[145859],{"id":145709,"depth":76,"text":145710,"children":145860},[145861,145862,145863],{"id":145727,"depth":82,"text":145728},{"id":4097,"depth":82,"text":4098},{"id":1498,"depth":82,"text":1499},{"_id":145865,"path":145866,"title":145867,"description":145867,"meta":145868,"body":145873},"content/blog/2018/09/07/i-am-joining-tech-elevator.md","/blog/2018/09/07/i-am-joining-tech-elevator","I am joining Tech Elevator",{"slug":145869,"date":145870,"published":13,"tags":145871,"author":-1,"cover":145872,"excerpt":-1},"i-am-joining-tech-elevator","2018-09-07T01:00:58-04:00",[104491],"./Stacked-Logo-760x202.jpg",{"type":19,"value":145874,"toc":146048},[145875,145882,145886,145895,145903,145906,145910,145916,145921,145924,145928,145931,145934,145939,145943,145946,145949,145958,145963,145967,145970,145998,146002,146005,146008,146013,146016,146020,146027,146032,146035,146037,146040,146045],[22,145876,145877,145878,145881],{},"I am happy to announce that I am joining ",[677,145879,104491],{"href":107914,"rel":145880},[681],". I am beyond excited to join this amazing company and I can't wait to tell you a little bit more about them and what they do.",[26,145883,145885],{"id":145884},"what-i-am-leaving-behind","What I am leaving behind",[22,145887,145888,145889,145894],{},"Before we get to the good news and what I have to look forward to, I want to spend a few minutes talking about what I am leaving behind. For the past 6 1/2 years, I have spent my days working for ",[677,145890,145893],{"href":145891,"rel":145892},"http://www.markelcorp.com/",[681],"Markel",". This is an amazing company that has done some amazing things, and I just want to thank them for the opportunities they gave me.",[22,145896,145897,145898,145902],{},"I was brought into Markel by my good friend ",[677,145899,104640],{"href":145900,"rel":145901},"https://www.linkedin.com/in/jasondelmore",[681]," and I can't thank him enough for everything he has done for me over the years. I had a chance to work with some really great people and work on some really fun projects.",[22,145904,145905],{},"This was a really hard decision for me because I really enjoyed the people I worked with and my commute to work was a short one (I work from home). I gave up some things in this move to do something I am really passionate about. Stepping outside of your comfort zone is never easy, but this is usually where the biggest rewards in life come from.",[26,145907,145909],{"id":145908},"hello-tech-elevator","Hello, Tech Elevator!",[22,145911,145912,145915],{},[677,145913,104491],{"href":107914,"rel":145914},[681]," is a Coding Bootcamp that started right here in Cleveland, Ohio and now has 4 locations. In just 14 weeks they can teach you the skills that you will need to land a job as a software developer. What's great about Tech Elevator is they have a 92% graduation rate and an 89% placement rate. That placement rate is the top in the country, how awesome is that!",[22,145917,145918],{},[653,145919],{"alt":57,"src":145920},"./ClassroomJosh.jpg",[22,145922,145923],{},"Josh teaching students at our Cleveland Campus",[636,145925,145927],{"id":145926},"what-is-a-coding-bootcamp","What is a Coding Bootcamp?",[22,145929,145930],{},"Traditionally, if you wanted to learn a specific skill you had to go to college to get it. You could attend a 4-year university or a more targeted trade school that typically takes 2 years. Even in a 2-year program, you're typically taking other classes that don't really have anything to do with the skill you're trying to learn.",[22,145932,145933],{},"This is where a coding boot camp or specialized trade school comes in. In a coding boot camp, you will spend a shorter amount of time in school but you're going to learn about software development the whole time. This allows the boot camp to focus in on the skills you need to land a job. The need for these boot camps is due to the overwhelming amount of available software development positions across the country.",[22,145935,145936],{},[653,145937],{"alt":57,"src":145938},"./brooke-cagle-609873-unsplash.jpg",[636,145940,145942],{"id":145941},"why-tech-elevator","Why Tech Elevator",[22,145944,145945],{},"Tech Elevator limits the class sizes so that instructors have time to focus on each student and it's one of the things that I really admire about them. They don't just spend 14 weeks in a textbook, they teach you concepts and give you practical examples to work on.",[22,145947,145948],{},"The last 2 weeks is used splitting students up into teams, teaching them agile principles and having them go off and build a real-world project together. I was able to see some of the student's projects and for only having 2 weeks to complete and having just learned to program, I was really impressed! ",[22,145950,145951,145952,145957],{},"They also have a ",[677,145953,145956],{"href":145954,"rel":145955},"https://www.techelevator.com/pathway-program/",[681],"pathway program"," that helps students with interview prep and the placement of graduates. I think this is as important as the computer skills they are learning. When I graduated I was sent out on my own with no help at all and just expected to find my own way. They take a real interest in student placement and it shows by being #1 in the country.",[22,145959,145960],{},[653,145961],{"alt":57,"src":145962},"./Tech-Elevator-small-11-0.jpg",[28831,145964,145966],{"id":145965},"check-us-out","Check us out",[22,145968,145969],{},"If this sounds like something you're interested in, we have 4 locations in the following cities.",[915,145971,145972,145979,145986,145992],{},[37,145973,145974],{},[677,145975,145978],{"href":145976,"rel":145977},"https://www.techelevator.com/cleveland/",[681],"Cleveland",[37,145980,145981],{},[677,145982,145985],{"href":145983,"rel":145984},"https://www.techelevator.com/columbus/",[681],"Columbus",[37,145987,145988],{},[677,145989,38168],{"href":145990,"rel":145991},"https://www.techelevator.com/cincinnati/",[681],[37,145993,145994],{},[677,145995,38180],{"href":145996,"rel":145997},"https://www.techelevator.com/pittsburgh/",[681],[636,145999,146001],{"id":146000},"what-is-my-role","What is my Role?",[22,146003,146004],{},"Now that you know a little bit about coding boot camps and Tech Elevator, I want to share with you what I will be doing. I am coming in as a Product Developer and my main product is our curriculum which has 2 tracks and is taught at all 4 of our locations.",[22,146006,146007],{},"It is my responsibility to take a look at the curriculum and make sure that the students are learning the skills that they will need and that employers are looking for. This will be done by taking feedback from instructors, listening to students and staying up to date in a fast-paced industry. We only get 14 weeks with these students and we need to make sure that every single day is an important one.",[22,146009,146010],{},[653,146011],{"alt":57,"src":146012},"./TEAnnouncement_Social.png",[22,146014,146015],{},"I will also be working on supplemental material for the students to help in making their day to day learning experience an even better one. We also have corporate training classes and we will be developing new classes for a variety of topics.",[636,146017,146019],{"id":146018},"we-are-hiring","We are hiring!",[22,146021,146022,146023],{},"Join our growing, passionate team and contribute your talents to our mission of elevating people, companies, and communities. Browse all openings: ",[677,146024,146025],{"href":146025,"rel":146026},"https://lnkd.in/e3Yuf45",[681],[22,146028,146029],{},[653,146030],{"alt":57,"src":146031},"./fab20838-f363-4400-b0f3-4a535192f1de-original.png",[22,146033,146034],{},"We are hiring! ",[26,146036,1499],{"id":1498},[22,146038,146039],{},"I consider myself extremely lucky to be a part of something special. I get to wake up and contribute to the next generation of software developers and for that, I am very grateful.",[22,146041,146042],{},[653,146043],{"alt":57,"src":146044},"./2018-09-07_08-28-36.png",[22,146046,146047],{},"This is a picture of the most recent graduates in Cleveland along with the Cleveland team.",{"title":57,"searchDepth":76,"depth":76,"links":146049},[146050,146051,146057],{"id":145884,"depth":76,"text":145885},{"id":145908,"depth":76,"text":145909,"children":146052},[146053,146054,146055,146056],{"id":145926,"depth":82,"text":145927},{"id":145941,"depth":82,"text":145942},{"id":146000,"depth":82,"text":146001},{"id":146018,"depth":82,"text":146019},{"id":1498,"depth":76,"text":1499},{"_id":146059,"path":146060,"title":146061,"description":146061,"meta":146062,"body":146067},"content/blog/2018/04/25/java-development-2018.md","/blog/2018/04/25/java-development-2018","What you should learn as a Java Developer in 2018",{"slug":146063,"date":146064,"published":13,"tags":146065,"author":-1,"cover":146066,"excerpt":-1},"java-development-2018","2018-04-25T06:39:22-04:00",[56],"./java_development_in_2018-760x428",{"type":19,"value":146068,"toc":146179},[146069,146072,146075,146079,146082,146085,146089,146102,146111,146115,146118,146124,146130,146136,146142,146148,146153,146159,146165,146170,146172,146175],[22,146070,146071],{},"In this tutorial, we are going to look at what you should learn as a Java Developer in 2018.",[22,146073,146074],{},"When you're trying to keep up to date or learn a new language it can often be confusing on what you should be learning in that space. New projects pop up and often go away and its hard to tell what's still relevant. In this article (and the video above), we will try and make sense of what you should be learning today as a Java Developer.",[26,146076,146078],{"id":146077},"java-development-in-2018","Java Development in 2018",[22,146080,146081],{},"This project started out as a really simple fun exercise for me. I wanted to get a list of what I thought Java developers should be learning in 2018 depending on their career path. That simple project I started has grown into the large list that you will see today. I just kept finding a reason to create another node or drill down even further.",[22,146083,146084],{},"When looking at this list I really want you to keep a couple of things in mind. When you see this large list of technologies please don’t let it scare you off. This is not a “you should know every single one of these” lists. It is just what I believe to be relevant in 2018. That last part is important, what I BELIEVE. I don’t know everything and I want this to be a fluid list, if you think I should add something to this list please let me know in the comments below and why and I will add it the mind map.",[26,146086,146088],{"id":146087},"java-development-in-2018-mind-map","Java Development in 2018 Mind Map",[22,146090,146091,146092,146096,146097,2755],{},"I really love using Mind Mapping software to organize my thoughts. It's actually really great for teaching and learning something new. It might make more sense if you watch the screencast below but if you want to jump right in you can check out the ",[677,146093,146095],{"href":130787,"rel":146094},[681],"Java Development in 2018 mind map here",". Also if you're new to mind mapping, in general, you can check out another short presentation I did on ",[677,146098,146101],{"href":146099,"rel":146100},"https://www.youtube.com/watch?v=FINqHqYjTlE",[681],"mind mapping for programmers",[22,146103,146104],{},[677,146105,146108],{"href":146106,"rel":146107},"https://therealdanvega.com/wp-content/uploads/2018/04/2018-04-25_06-01-13.png",[681],[653,146109],{"alt":146078,"src":146110},"./2018-04-25_06-01-13-1024x846.png",[636,146112,146114],{"id":146113},"java-development-mind-map-sections","Java Development Mind Map Sections",[22,146116,146117],{},"This is a quick breakdown of what we cover in the mind mapping presentation. I will also give you a time next to the title in case you want to jump directly to that section.",[22,146119,146120,146123],{},[646,146121,146122],{},"Core Fundamentals"," (3:10) These are core fundamentals that we should be learning during our development journey.",[22,146125,146126,146129],{},[646,146127,146128],{},"Basic Web Development"," (6:30) Even if you don't want to become a full-stack developer you should know the basics of front-end development. I would argue that no matter what language you're learning that having a foundation in HTML, CSS & JavaScript is important.",[22,146131,146132,146135],{},[646,146133,146134],{},"Advanced Front-end development"," (9:20) As you start to move through web development you might find out that you really like it. If you want to become a Java Developer and still want to learn some advanced front-end development skills you could become a full-stack developer.",[22,146137,146138,146141],{},[646,146139,146140],{},"Java Language"," (16:00) This is going to be the largest area of learning on your Java Developer path. In this section, I lay out what I think are the fundamentals to learn as well as what are the things you should be learning from the new releases.",[22,146143,146144,146147],{},[646,146145,146146],{},"JVM Languages"," (22:50) This is a collection of other languages that run on the JVM.",[22,146149,146150,146152],{},[646,146151,108112],{}," (23:55) A quick overview If you want to build applications for different form factors like phones and tablets.",[22,146154,146155,146158],{},[646,146156,146157],{},"Framework, Libraries & Tools"," (25:00) This is probably the largest section of the mind map. Here we are going to look at different frameworks, libraries or tools you might come across on your development journey.",[22,146160,146161,146164],{},[646,146162,146163],{},"DevOps"," (35:00) “DevOps is about moving from having a single skill, such as being a developer or network engineer, to having multiple skill sets that adapt to the changing needs of the business and market.”",[22,146166,146167,146169],{},[646,146168,4098],{}," (40:55) A collection of resources for learning a lot of the things we discussed in this tutorial.",[26,146171,1499],{"id":1498},[22,146173,146174],{},"I really hope you enjoyed that tutorial friend. This started out as a small little side project and ballooned into what it is today. If you think something is missing from this list please let me know. I hope you have a wonderful day and as always...",[22,146176,36004,146177,82545],{},[36006,146178],{},{"title":57,"searchDepth":76,"depth":76,"links":146180},[146181,146182,146185],{"id":146077,"depth":76,"text":146078},{"id":146087,"depth":76,"text":146088,"children":146183},[146184],{"id":146113,"depth":82,"text":146114},{"id":1498,"depth":76,"text":1499},{"_id":146187,"path":146188,"title":146189,"description":146189,"meta":146190,"body":146195},"content/blog/2018/03/01/what-is-new-spring-boot-2.md","/blog/2018/03/01/what-is-new-spring-boot-2","What's new in Spring Boot 2",{"slug":146191,"date":146192,"published":13,"tags":146193,"author":-1,"cover":146194,"excerpt":-1},"what-is-new-spring-boot-2","2018-03-01T06:47:52-05:00",[56,11002],"./pexels-photo-273238-2-760x507.jpeg",{"type":19,"value":146196,"toc":147564},[146197,146200,146213,146217,146225,146230,146233,146349,146353,146356,146360,146369,146372,146375,146379,146382,146386,146394,146398,146401,146408,146412,146424,146427,146435,146438,146447,146451,146454,146474,146477,146481,146484,146487,146490,146525,146534,146558,146561,146564,146567,146576,146580,146583,146651,146655,146658,146662,146665,146679,146687,146691,146699,146708,146711,146875,146902,146907,146910,146918,146922,146925,146928,146943,146945,146948,146954,146968,146970,146973,146982,147080,147094,147098,147101,147123,147128,147132,147135,147147,147150,147153,147167,147170,147178,147180,147183,147377,147380,147389,147403,147425,147428,147437,147439,147441,147444,147485,147487,147490,147499,147511,147513,147516,147520,147528,147537,147541,147553,147555,147561],[22,146198,146199],{},"Spring Boot 2 was just released and I am so pumped to see it in action in the coming months. This release is the culmination of 17 months of work and over 6800 commits by 215 different individuals. There are some really great features to talk about so let's talk about What's new in Spring Boot 2. In this article we are going to look at:",[915,146201,146202,146205,146208,146211],{},[37,146203,146204],{},"The history of Spring Boot",[37,146206,146207],{},"What's new in Spring Boot",[37,146209,146210],{},"Spring Boot 2 Migration Guide",[37,146212,4098],{},[26,146214,146216],{"id":146215},"spring-boot-history","Spring Boot History",[22,146218,146219,146220,2755],{},"Before we dive into what’s new I want to take this chance to take a quick look at the history of Spring Boot. In a blog post published in August 2013, ",[677,146221,146224],{"href":146222,"rel":146223},"https://spring.io/blog/2013/08/06/spring-boot-simplifying-spring-for-everyone/",[681],"Phil Webb announced the first milestone release of a new project called Spring Boot",[29685,146226,146227],{},[22,146228,146229],{},"Spring Boot aims to make it easy to create Spring-powered, production-grade applications and services with minimum fuss. It takes an opinionated view of the Spring platform so that new and existing users can quickly get to the bits they need. You can use it to create stand-alone Java applications that can be started using ‘java -jar’ or more traditional WAR deployments. ",[22,146231,146232],{},"About 9 months later in April 2014 Spring Boot 1.0 was released. Since then there have been numerous minor releases that have brought us some really great features. ",[915,146234,146235,146256,146277,146301,146325],{},[37,146236,146237,146242],{},[677,146238,146241],{"href":146239,"rel":146240},"https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-1.1-Release-Notes",[681],"Spring Boot 1.1 (June 2014) ",[915,146243,146244,146247,146250,146253],{},[37,146245,146246],{},"spring-boot-starter-test",[37,146248,146249],{},"Metrics & Health Endpoints",[37,146251,146252],{},"Elastic Search, Apache Solr, Spring Social & Spring Integration Auto-configuration ",[37,146254,146255],{},"Additional Templating support (Adding Freemaker, Groov, and Velocity)",[37,146257,146258,146263],{},[677,146259,146262],{"href":146260,"rel":146261},"https://github.com/spring-projects/spring-boot/wiki/spring-boot-1.2-release-notes",[681],"Spring Boot 1.2 (March 2015) ",[915,146264,146265,146268,146271,146274],{},[37,146266,146267],{},"Servlet 3.1, Tomcat 8 & Jetty 9",[37,146269,146270],{},"Spring 4.1",[37,146272,146273],{},"@SpringBootApplication Annotation",[37,146275,146276],{},"Email Support",[37,146278,146279,146284],{},[677,146280,146283],{"href":146281,"rel":146282},"https://github.com/spring-projects/spring-boot/wiki/spring-boot-1.3-release-notes",[681],"Spring Boot 1.3 (December 2016)",[915,146285,146286,146289,146292,146295,146298],{},[37,146287,146288],{},"Upgrade to Spring Framework 4.2",[37,146290,146291],{},"Upgrade to Spring Security 4.0",[37,146293,146294],{},"Developer Tools",[37,146296,146297],{},"Caching Auto-configuration ",[37,146299,146300],{},"Fully executable JARs and service support",[37,146302,146303,146308],{},[677,146304,146307],{"href":146305,"rel":146306},"https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-1.4-Release-Notes",[681],"Spring Boot 1.4 (January 2017)",[915,146309,146310,146313,146316,146319,146322],{},[37,146311,146312],{},"Spring 4.3",[37,146314,146315],{},"Hibernate 5",[37,146317,146318],{},"Testing Improvements",[37,146320,146321],{},"Integration Starter",[37,146323,146324],{},"Couchbase and Neo4J Support",[37,146326,146327,146332],{},[677,146328,146331],{"href":146329,"rel":146330},"https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-1.5-Release-Notes",[681],"Spring Boot 1.5 (February 2017)",[915,146333,146334,146337,146340,146343,146346],{},[37,146335,146336],{},"Loggers endpoint",[37,146338,146339],{},"Apache Kafka support",[37,146341,146342],{},"Cloud Foundry actuator extensions",[37,146344,146345],{},"LDAP support",[37,146347,146348],{},"Testing updates",[26,146350,146352],{"id":146351},"whats-new-in-spring","What's new in Spring ",[22,146354,146355],{},"What's new in Spring Boot 2? If we are going to talk about the biggest feature we need to start with the upgrade to Spring Framework 5. While Spring Framework 5 has been out since September 2017, most developers (like me) have been waiting for the release of Spring Boot 2. Spring Framework 5 introduces a long list of new features but I just want to touch on a few of the major ones here.",[636,146357,146359],{"id":146358},"whats-new-in-spring-framework-5","What’s new in Spring Framework 5",[22,146361,146362],{},[677,146363,146366],{"href":146364,"rel":146365},"https://therealdanvega.com/wp-content/uploads/2018/02/mark-solarski-209233-unsplash.jpg",[681],[653,146367],{"alt":57,"src":146368},"./mark-solarski-209233-unsplash-1024x682.jpg",[28831,146370,146371],{"id":54770},"Java 8+ Baseline",[22,146373,146374],{},"If you wish to build Spring Framework applications going forward you must be on a minimum of Java 8. Your first thought might be this is a great change for all of us but in fact its an even bigger one for the Spring team. This gave them a chance to make some major changes to update the code base to all the new features in Java 8 like Lambdas and streams. This not only ads readability throughout the code but also offers some performance improvements to the core of the framework. ",[28831,146376,146378],{"id":146377},"java-9-support","Java 9 Support",[22,146380,146381],{},"If you want to use Java 9 you will need to update to Spring Framework 5 and therefore need to update to Spring Boot 2. I know many of us might not be on the latest and greatest version of Java in production but this is a great chance for you to play around with all the cool new toys. This works pretty seamlessly out of the box using the standard classpath but I have read a few people having issues moving to Java 9 modules. ",[28831,146383,146385],{"id":146384},"spring-mvc","Spring MVC",[22,146387,146388,146389,2755],{},"While Spring MVC isn't the main character in this story there were some nice upgrades. I won't go through them all here so head over to ",[677,146390,146393],{"href":146391,"rel":146392},"https://github.com/spring-projects/spring-framework/wiki/What%27s-New-in-Spring-Framework-5.x#spring-web-mvc",[681],"documentation for Spring Framework 5",[28831,146395,146397],{"id":146396},"spring-webflux","Spring Webflux",[22,146399,146400],{},"The reactive stack is our main character in the story of Spring Framework 5. This is a different way of thinking but fortunately for us, we don't have to learn a whole new way of writing applications. Spring WebFlux is a fully asynchronous and non-blocking web framework built from the ground up allowing us to handle a massive number of concurrent connections. While this is a complete paradigm shift it is really easy to get started. ",[22,146402,146403],{},[677,146404,146406],{"href":146405},"./reactive_stack.png",[653,146407],{"alt":57,"src":146405},[28831,146409,146411],{"id":146410},"kotlin-support","Kotlin Support",[22,146413,146414,146415,146418,146419,2755],{},"Kotlin support was added to ",[677,146416,40207],{"href":40207,"rel":146417},[681]," but in Spring Framework 5 there is dedicated support for the language. With the dedicated support came some nice features that you can ",[677,146420,146423],{"href":146421,"rel":146422},"https://github.com/spring-projects/spring-framework/wiki/What%27s-New-in-Spring-Framework-5.x#kotlin-support",[681],"read about here",[28831,146425,146318],{"id":146426},"testing-improvements",[22,146428,146429,146430,146434],{},"The biggest change in the testing landscape is the complete support for ",[677,146431,60932],{"href":146432,"rel":146433},"http://junit.org/junit5/",[681],"'s Jupiter programming and extension model. I will mention this again later but when you fire up a new Spring Boot 2 application you are still using JUnit 4 by default however it is a simple change if you want to start using JUnit 5. ",[636,146436,146189],{"id":146437},"whats-new-in-spring-boot-2",[22,146439,146440],{},[677,146441,146444],{"href":146442,"rel":146443},"https://therealdanvega.com/wp-content/uploads/2018/02/jon-tyson-520864-unsplash.jpg",[681],[653,146445],{"alt":57,"src":146446},"./jon-tyson-520864-unsplash-1024x768.jpg",[28831,146448,146450],{"id":146449},"third-party-library-upgrades","Third-party Library Upgrades",[22,146452,146453],{},"With any new release of Spring Boot, the Spring team has an opportunity to update various dependencies. ",[915,146455,146456,146459,146462,146465,146468,146471],{},[37,146457,146458],{},"Thymeleaf 3 * ",[37,146460,146461],{},"Jetty 9.4",[37,146463,146464],{},"Tomcat 8.5",[37,146466,146467],{},"Hibernate 5.2",[37,146469,146470],{},"Flyway 5",[37,146472,146473],{},"Gradle 4",[22,146475,146476],{},"* The Thymeleaf starter now includes thymeleaf-extras-java8time out of the box.",[28831,146478,146480],{"id":146479},"reactive-spring-data-spring-security","Reactive Spring Data & Spring Security ",[22,146482,146483],{},"With the move to Spring WebFlux, Spring Data also provides support for reactive applications. Currently, Cassandra, MongoDB, Couchbase and Redis all have reactive API support. Spring Boot includes starter-POMs for all of these which should make getting started really easy. We also gain the ability to use Spring Security 5.0 in our reactive applications. When Spring Security is on the classpath auto-configuration is provided for WebFlux applications.",[28831,146485,146486],{"id":56318},"Actuator",[22,146488,146489],{},"Spring Boot Actuator is nothing new but it has been rewritten from the ground up. If you're not familiar with the Actuator project it automatically exposes endpoints to get information about that status of your application. The Actuator in Spring Boot 1.x was written against the servlet API and with the new reactive stack, the Spring team needed a solution that would work with both. Along with that, there were many changes to Actuator: ",[915,146491,146492,146495,146498,146501,146508,146511],{},[37,146493,146494],{},"Redesign for both servlet & reactive",[37,146496,146497],{},"Status & health (all the details) were separated out",[37,146499,146500],{},"Simplified Security model",[37,146502,146503,146504],{},"Move to micrometer (think SLF4J ",[62,146505,146507],{"style":146506},"font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Ubuntu, Cantarell, 'Helvetica Neue', sans-serif;","but for metrics)",[37,146509,146510],{},"Improved JSON Structures",[37,146512,146513,146514],{},"Simplified process for creating User-Defined Endpoints.\n",[915,146515,146516,146519,146522],{},[37,146517,146518],{},"@Endpoint",[37,146520,146521],{},"@WebEndpoint",[37,146523,146524],{},"@JmxEndpoint",[22,146526,146527],{},[677,146528,146531],{"href":146529,"rel":146530},"https://therealdanvega.com/wp-content/uploads/2018/02/pexels-photo-241544.jpeg",[681],[653,146532],{"alt":57,"src":146533},"./pexels-photo-241544-1024x683.jpeg",[22,146535,146536,146537,146540,146541,146544,146545,146547,146548,146551,146552,146557],{},"This might be an area that folks have some trouble upgrading because of the security model changes which we will talk about those in a few. By default, all web endpoints are available beneath the path ",[59,146538,146539],{},"/actuator"," with URLs of the form ",[59,146542,146543],{},"/actuator/{id}"," . The ",[59,146546,146539],{}," base path can be configured by using the ",[59,146549,146550],{},"management.endpoints.web.base-path"," property. There is a ",[677,146553,146556],{"href":146554,"rel":146555},"https://docs.spring.io/spring-boot/docs/2.0.x/actuator-api/html/",[681],"dedicated detailed document for Spring Boot Actuator Web API Endpoints"," and this is a great place to start. ",[28831,146559,146560],{"id":55236},"Gradle Plugin",[22,146562,146563],{},"I have always been a big fan of Gradle and I am really excited to see that the team took this opportunity to rewrite the Gradle plugin. ",[22,146565,146566],{},"he Spring Boot Gradle Plugin provides Spring Boot support in Gradle, allowing you to package executable jar or war archives, run Spring Boot applications, and use the dependency management provided by spring-boot-dependencies. Spring Boot’s Gradle plugin requires Gradle 4.0 or later.",[22,146568,146569],{},[677,146570,146573],{"href":146571,"rel":146572},"https://therealdanvega.com/wp-content/uploads/2018/02/pexels-photo-209728.jpeg",[681],[653,146574],{"alt":57,"src":146575},"./pexels-photo-209728-1024x687.jpeg",[22,146577,146578],{},[646,146579,93066],{},[22,146581,146582],{},"To get started with the plugin it needs to be applied to your project.",[52,146584,146586],{"className":54,"code":146585,"language":56,"meta":57,"style":57},"buildscript {\n repositories {\n maven { url 'https://repo.spring.io/libs-milestone' }\n }\n\n dependencies {\n classpath 'org.springframework.boot:spring-boot-gradle-plugin:2.0.0.RC1'\n }\n}\n\napply plugin: 'org.springframework.boot'\n",[59,146587,146588,146593,146598,146608,146612,146616,146621,146629,146633,146637,146641],{"__ignoreMap":57},[62,146589,146590],{"class":64,"line":65},[62,146591,146592],{"class":72},"buildscript {\n",[62,146594,146595],{"class":64,"line":76},[62,146596,146597],{"class":72}," repositories {\n",[62,146599,146600,146603,146606],{"class":64,"line":82},[62,146601,146602],{"class":72}," maven { url ",[62,146604,146605],{"class":1675},"'https://repo.spring.io/libs-milestone'",[62,146607,122649],{"class":72},[62,146609,146610],{"class":64,"line":89},[62,146611,3731],{"class":72},[62,146613,146614],{"class":64,"line":95},[62,146615,79],{"emptyLinePlaceholder":13},[62,146617,146618],{"class":64,"line":101},[62,146619,146620],{"class":72}," dependencies {\n",[62,146622,146623,146626],{"class":64,"line":107},[62,146624,146625],{"class":72}," classpath ",[62,146627,146628],{"class":1675},"'org.springframework.boot:spring-boot-gradle-plugin:2.0.0.RC1'\n",[62,146630,146631],{"class":64,"line":113},[62,146632,3731],{"class":72},[62,146634,146635],{"class":64,"line":129},[62,146636,379],{"class":72},[62,146638,146639],{"class":64,"line":134},[62,146640,79],{"emptyLinePlaceholder":13},[62,146642,146643,146646,146648],{"class":64,"line":156},[62,146644,146645],{"class":72},"apply plugin",[62,146647,1266],{"class":68},[62,146649,146650],{"class":1675}," 'org.springframework.boot'\n",[22,146652,146653],{},[646,146654,55269],{},[22,146656,146657],{},"The bootRepackage task has been replaced with bootJar and bootWar tasks for building executable jars and wars respectively. Both tasks extend their equivalent standard Gradle jar or war task, giving you access to all of the usual configuration options and behavior.",[22,146659,146660],{},[646,146661,55290],{},[22,146663,146664],{},"Spring Boot’s Gradle plugin no longer automatically applies the dependency management plugin. Instead, Spring Boot’s plugin now reacts to the dependency management plugin being applied by importing the correct version of the spring-boot-dependencies BOM. This gives you more control over how and when dependency management is configured. For most applications applying the dependency management plugin will be sufficient:",[52,146666,146668],{"className":54,"code":146667,"language":56,"meta":57,"style":57},"apply plugin: 'io.spring.dependency-management'\n",[59,146669,146670],{"__ignoreMap":57},[62,146671,146672,146674,146676],{"class":64,"line":65},[62,146673,146645],{"class":72},[62,146675,1266],{"class":68},[62,146677,146678],{"class":1675}," 'io.spring.dependency-management'\n",[22,146680,3521,146681,146686],{},[677,146682,146685],{"href":146683,"rel":146684},"https://docs.spring.io/spring-boot/docs/2.0.x/gradle-plugin/reference/html/",[681],"Gradle Plugin also has its own documentation"," and there is some really great information in there. Anyone that wants to get started using Gradle should start by checking out the link below. ",[28831,146688,146690],{"id":146689},"simplified-security","Simplified Security ",[22,146692,146693,146694,146696,146697,2755],{},"In Spring Boot 2.x, one of the main goals was to simplify security configuration and make adding custom security easy. By default, everything is secured, including static resources and Actuator endpoints. If Spring Security is on the classpath, Spring Boot will add ",[646,146695,80934],{}," and rely on Spring Security's content-negoation to decide which authentication mechanism to use. Once users decide that they want to add custom security, the default security configuration provided by Spring Boot will back off completely. At this point, users need to explicitly define all the bits they want to secure. This means security configuration is now in one place and avoids any kind of ordering issues with existing ",[646,146698,91440],{},[22,146700,146701],{},[677,146702,146705],{"href":146703,"rel":146704},"https://therealdanvega.com/wp-content/uploads/2018/02/pexels-photo-113726.jpeg",[681],[653,146706],{"alt":57,"src":146707},"./pexels-photo-113726-1024x683.jpeg",[22,146709,146710],{},"Here is an example of a custom security:",[52,146712,146714],{"className":54,"code":146713,"language":56,"meta":57,"style":57},"http\n .authorizeRequests()\n // 1\n .requestMatchers(EndpointRequest.to(\"status\", \"info\"))\n .permitAll()\n // 2\n .requestMatchers(EndpointRequest.toAnyEndpoint())\n .hasRole(\"ACTUATOR\")\n // 3 \n .requestMatchers(StaticResourceRequest.toCommonLocations())\n .permitAll()\n // 4\n .antMatchers(\"/\\*\\*\")\n .hasRole(\"USER\")\n .and()\n ... // additional configuration\n",[59,146715,146716,146721,146729,146734,146756,146764,146769,146782,146795,146800,146814,146822,146827,146846,146859,146867],{"__ignoreMap":57},[62,146717,146718],{"class":64,"line":65},[62,146719,146720],{"class":72},"http\n",[62,146722,146723,146725,146727],{"class":64,"line":76},[62,146724,4252],{"class":72},[62,146726,84626],{"class":122},[62,146728,2223],{"class":72},[62,146730,146731],{"class":64,"line":82},[62,146732,146733],{"class":85}," // 1\n",[62,146735,146736,146738,146740,146743,146745,146747,146749,146751,146754],{"class":64,"line":89},[62,146737,3862],{"class":72},[62,146739,15490],{"class":122},[62,146741,146742],{"class":72},"(EndpointRequest.",[62,146744,84640],{"class":122},[62,146746,2109],{"class":72},[62,146748,123496],{"class":1675},[62,146750,976],{"class":72},[62,146752,146753],{"class":1675},"\"info\"",[62,146755,5047],{"class":72},[62,146757,146758,146760,146762],{"class":64,"line":95},[62,146759,2610],{"class":72},[62,146761,11549],{"class":122},[62,146763,2223],{"class":72},[62,146765,146766],{"class":64,"line":101},[62,146767,146768],{"class":85}," // 2\n",[62,146770,146771,146773,146775,146777,146780],{"class":64,"line":107},[62,146772,3862],{"class":72},[62,146774,15490],{"class":122},[62,146776,146742],{"class":72},[62,146778,146779],{"class":122},"toAnyEndpoint",[62,146781,4460],{"class":72},[62,146783,146784,146786,146788,146790,146793],{"class":64,"line":113},[62,146785,2610],{"class":72},[62,146787,84669],{"class":122},[62,146789,2109],{"class":72},[62,146791,146792],{"class":1675},"\"ACTUATOR\"",[62,146794,2212],{"class":72},[62,146796,146797],{"class":64,"line":129},[62,146798,146799],{"class":85}," // 3 \n",[62,146801,146802,146804,146806,146809,146812],{"class":64,"line":134},[62,146803,3862],{"class":72},[62,146805,15490],{"class":122},[62,146807,146808],{"class":72},"(StaticResourceRequest.",[62,146810,146811],{"class":122},"toCommonLocations",[62,146813,4460],{"class":72},[62,146815,146816,146818,146820],{"class":64,"line":156},[62,146817,2610],{"class":72},[62,146819,11549],{"class":122},[62,146821,2223],{"class":72},[62,146823,146824],{"class":64,"line":161},[62,146825,146826],{"class":85}," // 4\n",[62,146828,146829,146831,146834,146836,146839,146842,146844],{"class":64,"line":167},[62,146830,3862],{"class":72},[62,146832,146833],{"class":122},"antMatchers",[62,146835,2109],{"class":72},[62,146837,146838],{"class":1675},"\"/",[62,146840,146841],{"class":149},"\\*\\*",[62,146843,4498],{"class":1675},[62,146845,2212],{"class":72},[62,146847,146848,146850,146852,146854,146857],{"class":64,"line":173},[62,146849,2610],{"class":72},[62,146851,84669],{"class":122},[62,146853,2109],{"class":72},[62,146855,146856],{"class":1675},"\"USER\"",[62,146858,2212],{"class":72},[62,146860,146861,146863,146865],{"class":64,"line":179},[62,146862,4252],{"class":72},[62,146864,84694],{"class":122},[62,146866,2223],{"class":72},[62,146868,146869,146872],{"class":64,"line":185},[62,146870,146871],{"class":72}," ... ",[62,146873,146874],{"class":85},"// additional configuration\n",[34,146876,146877,146886,146893,146896],{},[37,146878,146879,49236,146882,146885],{},[59,146880,146881],{},"/status",[59,146883,146884],{},"/info"," endpoints do not require authentication.",[37,146887,146888,146889,146892],{},"All other actuator endpoints are protected by the ",[59,146890,146891],{},"ACTUATOR"," role.",[37,146894,146895],{},"Common static resource locations are open to all.",[37,146897,146898,146899,146892],{},"All other application endpoints are protected by the ",[59,146900,146901],{},"USER",[22,146903,146904,146906],{},[646,146905,56598],{}," With the actuator endpoints locked down, you will need to include or exclude the endpoints you want to see. These are the properties that now control that and have changed since 1.x ",[22,146908,146909],{},"# ENDPOINTS WEB CONFIGURATION (WebEndpointProperties)\nmanagement.endpoints.web.exposure.include=info,health # Endpoint IDs that should be included or '*' for all.\nmanagement.endpoints.web.exposure.exclude= # Endpoint IDs that should be excluded.\nmanagement.endpoints.web.base-path=/actuator # Base path for Web endpoints. Relative to server.servlet.context-path or management.server.servlet.context-path if management.server.port is configured.\nmanagement.endpoints.web.path-mapping= # Mapping between endpoint IDs and the path that should expose them.",[22,146911,146912,146913,57647],{},"If you're ever unsure what properties to use bookmark the ",[677,146914,146917],{"href":146915,"rel":146916},"https://docs.spring.io/spring-boot/docs/2.0.x/reference/html/common-application-properties.html",[681],"common application properties documentation",[28831,146919,146921],{"id":146920},"http2-support","HTTP/2 Support",[22,146923,146924],{},"It's hard to believe but the HTTP 1.1 specification was released back in 1996. I think this comes without saying but the web is very different today. If you want to enable HTTP/2 in your Spring MVC or WebFlux applications you can use the following property. ",[22,146926,146927],{},"server.http2.enabled=true",[22,146929,146930,146931,146935,146936],{},"This support depends on the chosen web server and the application environment since that protocol is not supported out-of-the-box by JDK8. Please ",[677,146932,131217],{"href":146933,"rel":146934},"https://docs.spring.io/spring-boot/docs/2.0.x/reference/html/howto-embedded-web-servers.html#howto-configure-http2",[681]," for more details. ",[677,146937,146940],{"href":146938,"rel":146939},"https://therealdanvega.com/wp-content/uploads/2018/02/mac-freelancer-macintosh-macbook-40185.jpeg",[681],[653,146941],{"alt":57,"src":146942},"./mac-freelancer-macintosh-macbook-40185-1024x634.jpeg",[28831,146944,104236],{"id":104235},[22,146946,146947],{},"In Spring Boot 1.x this notion of relaxed binding was supported and all that meant is there were multiple ways you could create a property name (camel case, underscore, hyphen) and that property would all bind to the same property. ",[22,146949,146950,146951,146953],{},"This still works the same but what they did tighten up was the way that you read variables in your own code. The ",[59,146952,14603],{}," annotation is a core container feature, and it does not provide the same features as type-safe configuration properties.",[22,146955,146956,60103,146964],{},[677,146957,146960],{"href":146958,"rel":146959},"https://therealdanvega.com/wp-content/uploads/2018/02/2018-02-28_07-12-58.png",[681],[653,146961],{"alt":146962,"src":146963},"What's new in Spring Boot 2 - Configuration Properties","./2018-02-28_07-12-58-1024x180.png",[677,146965,146966],{"href":146966,"rel":146967},"https://docs.spring.io/spring-boot/docs/2.0.x/reference/html/boot-features-external-config.html#boot-features-external-config-relaxed-binding",[681],[28831,146969,98873],{"id":56466},[22,146971,146972],{},"Spring Boot’s own metrics have been replaced by Micrometer. This is being developed by Pivotal and quickly being adopted across projects within Pivotal. ",[22,146974,146975,146976,146981],{},"Spring Boot Actuator provides dependency management and auto-configuration for ",[677,146977,146980],{"href":146978,"rel":146979},"https://micrometer.io/",[681],"Micrometer",", an application metrics facade that supports numerous monitoring systems, including:",[915,146983,146984,146992,147000,147008,147016,147024,147032,147040,147048,147056,147064,147072],{},[37,146985,146986],{},[677,146987,146991],{"href":146988,"rel":146989,"title":146990},"https://docs.spring.io/spring-boot/docs/2.0.x/reference/html/production-ready-metrics.html#production-ready-metrics-export-atlas",[681],"54.2.1 Atlas","Atlas",[37,146993,146994],{},[677,146995,146999],{"href":146996,"rel":146997,"title":146998},"https://docs.spring.io/spring-boot/docs/2.0.x/reference/html/production-ready-metrics.html#production-ready-metrics-export-datadog",[681],"54.2.2 Datadog","Datadog",[37,147001,147002],{},[677,147003,147007],{"href":147004,"rel":147005,"title":147006},"https://docs.spring.io/spring-boot/docs/2.0.x/reference/html/production-ready-metrics.html#production-ready-metrics-export-ganglia",[681],"54.2.3 Ganglia","Ganglia",[37,147009,147010],{},[677,147011,147015],{"href":147012,"rel":147013,"title":147014},"https://docs.spring.io/spring-boot/docs/2.0.x/reference/html/production-ready-metrics.html#production-ready-metrics-export-graphite",[681],"54.2.4 Graphite","Graphite",[37,147017,147018],{},[677,147019,147023],{"href":147020,"rel":147021,"title":147022},"https://docs.spring.io/spring-boot/docs/2.0.x/reference/html/production-ready-metrics.html#production-ready-metrics-export-influx",[681],"54.2.5 Influx","Influx",[37,147025,147026],{},[677,147027,147031],{"href":147028,"rel":147029,"title":147030},"https://docs.spring.io/spring-boot/docs/2.0.x/reference/html/production-ready-metrics.html#production-ready-metrics-export-jmx",[681],"54.2.6 JMX","JMX",[37,147033,147034],{},[677,147035,147039],{"href":147036,"rel":147037,"title":147038},"https://docs.spring.io/spring-boot/docs/2.0.x/reference/html/production-ready-metrics.html#production-ready-metrics-export-newrelic",[681],"54.2.7 New Relic","New Relic",[37,147041,147042],{},[677,147043,147047],{"href":147044,"rel":147045,"title":147046},"https://docs.spring.io/spring-boot/docs/2.0.x/reference/html/production-ready-metrics.html#production-ready-metrics-export-prometheus",[681],"54.2.8 Prometheus","Prometheus",[37,147049,147050],{},[677,147051,147055],{"href":147052,"rel":147053,"title":147054},"https://docs.spring.io/spring-boot/docs/2.0.x/reference/html/production-ready-metrics.html#production-ready-metrics-export-signalfx",[681],"54.2.9 SignalFx","SignalFx",[37,147057,147058],{},[677,147059,147063],{"href":147060,"rel":147061,"title":147062},"https://docs.spring.io/spring-boot/docs/2.0.x/reference/html/production-ready-metrics.html#production-ready-metrics-export-simple",[681],"54.2.10 Simple","Simple (in-memory)",[37,147065,147066],{},[677,147067,147071],{"href":147068,"rel":147069,"title":147070},"https://docs.spring.io/spring-boot/docs/2.0.x/reference/html/production-ready-metrics.html#production-ready-metrics-export-statsd",[681],"54.2.11 StatsD","StatsD",[37,147073,147074],{},[677,147075,147079],{"href":147076,"rel":147077,"title":147078},"https://docs.spring.io/spring-boot/docs/2.0.x/reference/html/production-ready-metrics.html#production-ready-metrics-export-wavefront",[681],"54.2.12 Wavefront","Wavefront",[22,147081,147082,147089,147090,147093],{},[677,147083,147086],{"href":147084,"rel":147085},"https://therealdanvega.com/wp-content/uploads/2018/02/2018-02-28_06-59-52.png",[681],[653,147087],{"alt":57,"src":147088},"./2018-02-28_06-59-52-1024x579.png"," To learn more about Micrometer visit ",[677,147091,146978],{"href":146978,"rel":147092},[681]," This might cause a little bit of an upgrade pain for those doing a ton of logging of custom metrics.",[28831,147095,147097],{"id":147096},"quartz-scheduler","Quartz Scheduler",[22,147099,147100],{},"Spring Boot 2 provides support for the Quartz scheduler that can be used via the spring-boot-starter-quartz dedicated starter. Both in-memory and JDBC stores can be configured.",[52,147102,147104],{"className":1769,"code":147103,"language":1771,"meta":57,"style":57},"\u003Cdependency>\n \u003CgroupId>org.springframework.boot\u003C/groupId>\n \u003CartifactId>spring-boot-starter-quartz\u003C/artifactId>\n\u003C/dependency>\n",[59,147105,147106,147110,147114,147119],{"__ignoreMap":57},[62,147107,147108],{"class":64,"line":65},[62,147109,46425],{},[62,147111,147112],{"class":64,"line":76},[62,147113,54736],{},[62,147115,147116],{"class":64,"line":82},[62,147117,147118],{}," \u003CartifactId>spring-boot-starter-quartz\u003C/artifactId>\n",[62,147120,147121],{"class":64,"line":89},[62,147122,46445],{},[22,147124,147125],{},[653,147126],{"alt":57,"src":147127},"./pexels-photo-768472-1024x768.jpeg",[28831,147129,147131],{"id":147130},"hikaricp-connection-pool","HikariCP Connection Pool",[22,147133,147134],{},"The default connection pool has switched from Tomcat to HikariCP. If you used spring.datasource.type to force the use of Hikari in a Tomcat-based application, you can now remove that override. Similarly, if you want to stay with the Tomcat connection pool, simply add the following to your configuration:",[52,147136,147138],{"className":1663,"code":147137,"language":1665,"meta":57,"style":57},"spring.datasource.type=org.apache.tomcat.jdbc.pool.DataSource\n",[59,147139,147140],{"__ignoreMap":57},[62,147141,147142,147144],{"class":64,"line":65},[62,147143,54849],{"class":122},[62,147145,147146],{"class":1675},"=org.apache.tomcat.jdbc.pool.DataSource\n",[28831,147148,146294],{"id":147149},"developer-tools",[22,147151,147152],{},"By default, each time your application restarts, a report showing the condition evaluation delta is logged. The report shows the changes to your application’s auto-configuration as you make changes such as adding or removing beans and setting configuration properties. To disable the logging of the report, set the following property:",[52,147154,147156],{"className":1663,"code":147155,"language":1665,"meta":57,"style":57},"spring.devtools.restart.log-condition-evaluation-delta=false\n",[59,147157,147158],{"__ignoreMap":57},[62,147159,147160,147163,147165],{"class":64,"line":65},[62,147161,147162],{"class":122},"spring.devtools.restart.log-condition-evaluation-delta",[62,147164,146],{"class":1675},[62,147166,40782],{"class":149},[28831,147168,146411],{"id":147169},"kotlin-support-1",[22,147171,147172,147173,57647],{},"We talked earlier in this article about the official support for Kotlin. There is also a ",[677,147174,147177],{"href":147175,"rel":147176},"https://docs.spring.io/spring-boot/docs/2.0.x/reference/html/boot-features-kotlin.html",[681],"dedicated portion of the documentation to Kotlin",[28831,147179,60932],{"id":60931},[22,147181,147182],{},"As I said earlier the default for a Spring Boot application is to still use JUnit 4. If you want to switch to JUnit 5 you will need to exclude JUnit 4 from the spring boot starter test and add the required dependencies. You will also need to add the plugin listed below. ",[52,147184,147186],{"className":1769,"code":147185,"language":1771,"meta":57,"style":57},"\u003Cdependencies>\n \u003Cdependency>\n \u003CgroupId>org.springframework.boot\u003C/groupId>\n \u003CartifactId>spring-boot-starter-test\u003C/artifactId>\n \u003Cscope>test\u003C/scope>\n \u003Cexclusions>\n \u003Cexclusion>\n \u003CgroupId>junit\u003C/groupId>\n \u003CartifactId>junit\u003C/artifactId>\n \u003C/exclusion>\n \u003C/exclusions>\n \u003C/dependency>\n \u003Cdependency>\n \u003CgroupId>org.junit.jupiter\u003C/groupId>\n \u003CartifactId>junit-jupiter-api\u003C/artifactId>\n \u003Cscope>test\u003C/scope>\n \u003C/dependency>\n \u003Cdependency>\n \u003CgroupId>org.junit.jupiter\u003C/groupId>\n \u003CartifactId>junit-jupiter-engine\u003C/artifactId>\n \u003Cscope>test\u003C/scope>\n \u003C/dependency>\n\u003C/dependencies>\n\u003Cbuild>\n\u003Cplugins>\n \u003Cplugin>\n \u003CgroupId>org.springframework.boot\u003C/groupId>\n \u003CartifactId>spring-boot-maven-plugin\u003C/artifactId>\n \u003C/plugin>\n \u003Cplugin>\n \u003CgroupId>org.apache.maven.plugins\u003C/groupId>\n \u003CartifactId>maven-surefire-plugin\u003C/artifactId>\n \u003Cdependencies>\n \u003Cdependency>\n \u003CgroupId>org.junit.platform\u003C/groupId>\n \u003CartifactId>junit-platform-surefire-provider\u003C/artifactId>\n \u003Cversion>${junit-platform.version}\u003C/version>\n \u003C/dependency>\n \u003C/dependencies>\n \u003C/plugin>\n\u003C/plugins>\n\u003C/build>\n",[59,147187,147188,147192,147196,147200,147204,147208,147213,147218,147223,147228,147233,147238,147242,147246,147251,147256,147260,147264,147268,147272,147277,147281,147285,147289,147293,147298,147302,147306,147311,147315,147319,147324,147329,147334,147339,147344,147349,147354,147359,147364,147368,147373],{"__ignoreMap":57},[62,147189,147190],{"class":64,"line":65},[62,147191,56803],{},[62,147193,147194],{"class":64,"line":76},[62,147195,56808],{},[62,147197,147198],{"class":64,"line":82},[62,147199,56813],{},[62,147201,147202],{"class":64,"line":89},[62,147203,58658],{},[62,147205,147206],{"class":64,"line":95},[62,147207,58663],{},[62,147209,147210],{"class":64,"line":101},[62,147211,147212],{}," \u003Cexclusions>\n",[62,147214,147215],{"class":64,"line":107},[62,147216,147217],{}," \u003Cexclusion>\n",[62,147219,147220],{"class":64,"line":113},[62,147221,147222],{}," \u003CgroupId>junit\u003C/groupId>\n",[62,147224,147225],{"class":64,"line":129},[62,147226,147227],{}," \u003CartifactId>junit\u003C/artifactId>\n",[62,147229,147230],{"class":64,"line":134},[62,147231,147232],{}," \u003C/exclusion>\n",[62,147234,147235],{"class":64,"line":156},[62,147236,147237],{}," \u003C/exclusions>\n",[62,147239,147240],{"class":64,"line":161},[62,147241,56823],{},[62,147243,147244],{"class":64,"line":167},[62,147245,56808],{},[62,147247,147248],{"class":64,"line":173},[62,147249,147250],{}," \u003CgroupId>org.junit.jupiter\u003C/groupId>\n",[62,147252,147253],{"class":64,"line":179},[62,147254,147255],{}," \u003CartifactId>junit-jupiter-api\u003C/artifactId>\n",[62,147257,147258],{"class":64,"line":185},[62,147259,58663],{},[62,147261,147262],{"class":64,"line":191},[62,147263,56823],{},[62,147265,147266],{"class":64,"line":209},[62,147267,56808],{},[62,147269,147270],{"class":64,"line":220},[62,147271,147250],{},[62,147273,147274],{"class":64,"line":226},[62,147275,147276],{}," \u003CartifactId>junit-jupiter-engine\u003C/artifactId>\n",[62,147278,147279],{"class":64,"line":231},[62,147280,58663],{},[62,147282,147283],{"class":64,"line":236},[62,147284,56823],{},[62,147286,147287],{"class":64,"line":242},[62,147288,56916],{},[62,147290,147291],{"class":64,"line":247},[62,147292,78341],{},[62,147294,147295],{"class":64,"line":252},[62,147296,147297],{},"\u003Cplugins>\n",[62,147299,147300],{"class":64,"line":257},[62,147301,78351],{},[62,147303,147304],{"class":64,"line":271},[62,147305,56813],{},[62,147307,147308],{"class":64,"line":281},[62,147309,147310],{}," \u003CartifactId>spring-boot-maven-plugin\u003C/artifactId>\n",[62,147312,147313],{"class":64,"line":286},[62,147314,78426],{},[62,147316,147317],{"class":64,"line":291},[62,147318,78351],{},[62,147320,147321],{"class":64,"line":296},[62,147322,147323],{}," \u003CgroupId>org.apache.maven.plugins\u003C/groupId>\n",[62,147325,147326],{"class":64,"line":302},[62,147327,147328],{}," \u003CartifactId>maven-surefire-plugin\u003C/artifactId>\n",[62,147330,147331],{"class":64,"line":308},[62,147332,147333],{}," \u003Cdependencies>\n",[62,147335,147336],{"class":64,"line":314},[62,147337,147338],{}," \u003Cdependency>\n",[62,147340,147341],{"class":64,"line":320},[62,147342,147343],{}," \u003CgroupId>org.junit.platform\u003C/groupId>\n",[62,147345,147346],{"class":64,"line":326},[62,147347,147348],{}," \u003CartifactId>junit-platform-surefire-provider\u003C/artifactId>\n",[62,147350,147351],{"class":64,"line":338},[62,147352,147353],{}," \u003Cversion>${junit-platform.version}\u003C/version>\n",[62,147355,147356],{"class":64,"line":343},[62,147357,147358],{}," \u003C/dependency>\n",[62,147360,147361],{"class":64,"line":357},[62,147362,147363],{}," \u003C/dependencies>\n",[62,147365,147366],{"class":64,"line":366},[62,147367,78426],{},[62,147369,147370],{"class":64,"line":371},[62,147371,147372],{},"\u003C/plugins>\n",[62,147374,147375],{"class":64,"line":376},[62,147376,78436],{},[26,147378,146210],{"id":147379},"spring-boot-2-migration-guide",[22,147381,147382,147383,147388],{},"It probably goes without saying but with any major release like this simply flipping the version number in production isn't your best upgrade path. The first thing I would do is read through the ",[677,147384,147387],{"href":147385,"rel":147386},"https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0-Migration-Guide",[681],"Spring Boot 2.0 Migration Guide",". For me I found most of my issues revolved around the simplified security model changes and property changes. The migration guide includes an awesome tip for migrating your property files. ",[29685,147390,147391],{},[22,147392,147393,147394,119066,147396,147398,147399,147402],{},"With Spring Boot 2.0, many configuration properties were renamed/removed and developers need to update their ",[59,147395,1265],{},[59,147397,17005],{}," accordingly. To help you with that, Spring Boot ships a new ",[59,147400,147401],{},"spring-boot-properties-migrator"," module. Once added as a dependency to your project, this will not only analyze your application’s environment and print diagnostics at startup, but also temporarily migrate properties at runtime for you. This is a must have during your application migration:",[52,147404,147406],{"className":1769,"code":147405,"language":1771,"meta":57,"style":57},"\u003Cdependency>\n \u003CgroupId>org.springframework.boot\u003C/groupId>\n \u003CartifactId>spring-boot-properties-migrator\u003C/artifactId>\n\u003C/dependency>\n",[59,147407,147408,147412,147416,147421],{"__ignoreMap":57},[62,147409,147410],{"class":64,"line":65},[62,147411,46425],{},[62,147413,147414],{"class":64,"line":76},[62,147415,78970],{},[62,147417,147418],{"class":64,"line":82},[62,147419,147420],{}," \u003CartifactId>spring-boot-properties-migrator\u003C/artifactId>\n",[62,147422,147423],{"class":64,"line":89},[62,147424,46445],{},[22,147426,147427],{},"I am not sure what your thoughts are on this but I would certainly start playing with Spring Boot 2 and working on migrating your code over to it but with any major release, I usually wait until the next point release. This isn't just for Spring, I do this with pretty much everything from Apple to Pivotal to Angry Birds! ",[22,147429,147430],{},[677,147431,147434],{"href":147432,"rel":147433},"https://therealdanvega.com/wp-content/uploads/2018/02/pexels-photo-860379.jpeg",[681],[653,147435],{"alt":57,"src":147436},"./pexels-photo-860379-1024x659.jpeg",[26,147438,4098],{"id":4097},[636,147440,37639],{"id":86173},[22,147442,147443],{},"There are a ton of great resources out there but I want to start with the documentation. The documentation for Spring Framework and Spring Boot has really improved over the years. I love some of the dedicated guides we got this time around with Actuator and the Gradle Plugin. ",[915,147445,147446,147453,147460,147466,147472,147479],{},[37,147447,147448,147449],{},"Spring Boot Reference Guide: ",[677,147450,147451],{"href":147451,"rel":147452},"https://docs.spring.io/spring-boot/docs/2.0.x/reference/html/",[681],[37,147454,147455,147456],{},"Spring Boot API: ",[677,147457,147458],{"href":147458,"rel":147459},"https://docs.spring.io/spring-boot/docs/2.0.x/api/",[681],[37,147461,147462,147463],{},"Spring Boot Actuator Web API: ",[677,147464,146554],{"href":146554,"rel":147465},[681],[37,147467,147468,147469],{},"Spring Boot Gradle Plugin Reference: ",[677,147470,146683],{"href":146683,"rel":147471},[681],[37,147473,147474,147475],{},"Spring Boot Maven Plugin Reference: ",[677,147476,147477],{"href":147477,"rel":147478},"https://docs.spring.io/spring-boot/docs/2.0.x/maven-plugin/",[681],[37,147480,147481,147482],{},"Common Application Properties: ",[677,147483,146915],{"href":146915,"rel":147484},[681],[636,147486,37756],{"id":86219},[22,147488,147489],{},"I have both of these books and think they both have some really good value. If you're looking for more of a reactive path check out Learning Spring Boot 2.0. ",[22,147491,147492],{},[677,147493,147496],{"href":147494,"rel":147495},"https://www.amazon.com/Learning-Spring-Boot-2-0-microservices/dp/1786463784/ref=as_li_ss_il?ie=UTF8&qid=1519826464&sr=8-1-spons&keywords=spring+boot+2&psc=1&linkCode=li3&tag=therealdanveg-20&linkId=02c093ca835465896029afb5ff59d6ce",[681],[653,147497],{"alt":57,"src":147498},"//ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&ASIN=1786463784&Format=_SL250_&ID=AsinImage&MarketPlace=US&ServiceVersion=20070822&WS=1&tag=therealdanveg-20",[22,147500,147501,147508],{},[677,147502,147505],{"href":147503,"rel":147504},"https://www.amazon.com/Beginning-Spring-Boot-Applications-Microservices/dp/1484229304/ref=as_li_ss_il?ie=UTF8&qid=1519826464&sr=8-3&keywords=spring+boot+2&linkCode=li3&tag=therealdanveg-20&linkId=431754c7b1270cf3b194761f5df51b34",[681],[653,147506],{"alt":57,"src":147507},"//ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&ASIN=1484229304&Format=_SL250_&ID=AsinImage&MarketPlace=US&ServiceVersion=20070822&WS=1&tag=therealdanveg-20",[653,147509],{"alt":57,"src":147510},"https://ir-na.amazon-adsystem.com/e/ir?t=therealdanveg-20&l=li3&o=1&a=1484229304",[636,147512,53436],{"id":57582},[22,147514,147515],{},"I am working on wrapping up my next course titled \"Getting Started with Spring Boot 2\".",[28831,147517,147519],{"id":147518},"course-overview","Course Overview",[22,147521,147522,147523,57647],{},"This course offers hands-on experience building Spring Framework applications using Spring Boot. The first thing that is going to stand out is that we are going to move away from the boring, non-useful demos. In the new course, we are going to build a practical application from start to finish. This will allow you to see the process I use for building applications from idea to launch. One of the big thing I heard from my students in my first course is that they wanted more information on how to go into production. A great application isn’t that useful if we can deploy it into a production environment. We will take a look at using one of the most popular cloud services around, Amazon Web Services (AWS). On completion, students will have the foundation they need to build enterprise-ready Java applications. If you're interested in signing up for the waitlist and receive an introductory discount ",[677,147524,147527],{"href":147525,"rel":147526},"https://therealdanvega.com/spring-boot-2",[681],"please head over to the course page",[22,147529,147530],{},[677,147531,147534],{"href":147532,"rel":147533},"https://therealdanvega.com/wp-content/uploads/2018/02/getting_started_spring_boot_2.png",[681],[653,147535],{"alt":57,"src":147536},"./getting_started_spring_boot_2-1024x304.png",[636,147538,147540],{"id":147539},"me-me-me","Me, Me, Me",[22,147542,147543,147544,147548,147549],{},"Please consider signing up for my mailing list (below) to be notified when I publish new content. I am making a large push towards more video and less long blog posts (like this one) so ",[677,147545,147547],{"href":57189,"rel":147546},[681],"please consider subscribing to my YouTube Channel"," as well. A video of me talking about this blog post. ",[677,147550,147551],{"href":147551,"rel":147552},"https://www.youtube.com/watch?v=91n6y-T-PC8",[681],[26,147554,1499],{"id":1498},[22,147556,147557,147558,147560],{},"I have been looking forward to this release for a long time now and If you can't tell, I am super excited. _",[646,147559,49733],{}," What are your favorite features in Spring Boot 2? _",[1527,147562,147563],{},"html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .s4-Ip, html code.shiki .s4-Ip{--shiki-default:#6A737D;--shiki-dark:#6A737D;--shiki-sepia:#6A737D}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}",{"title":57,"searchDepth":76,"depth":76,"links":147565},[147566,147567,147571,147572,147578],{"id":146215,"depth":76,"text":146216},{"id":146351,"depth":76,"text":146352,"children":147568},[147569,147570],{"id":146358,"depth":82,"text":146359},{"id":146437,"depth":82,"text":146189},{"id":147379,"depth":76,"text":146210},{"id":4097,"depth":76,"text":4098,"children":147573},[147574,147575,147576,147577],{"id":86173,"depth":82,"text":37639},{"id":86219,"depth":82,"text":37756},{"id":57582,"depth":82,"text":53436},{"id":147539,"depth":82,"text":147540},{"id":1498,"depth":76,"text":1499},{"_id":147580,"path":147581,"title":147582,"description":147582,"meta":147583,"body":147587},"content/blog/2018/01/01/my-2018-goals.md","/blog/2018/01/01/my-2018-goals","Happy New Year!!! My 2018 Goals",{"slug":147584,"date":147585,"published":13,"tags":147586,"author":-1,"cover":143822,"excerpt":-1},"my-2018-goals","2018-01-01T08:50:40-05:00",[33475],{"type":19,"value":147588,"toc":147962},[147589,147598,147601,147604,147607,147611,147614,147624,147627,147654,147656,147659,147669,147673,147676,147684,147688,147691,147699,147701,147704,147711,147713,147716,147724,147727,147732,147752,147759,147761,147764,147772,147774,147777,147787,147791,147794,147796,147799,147809,147813,147822,147832,147851,147853,147856,147864,147873,147877,147895,147899,147906,147920,147930,147934,147943,147951,147954,147956],[22,147590,147591,147592,147597],{},"Another year has come and gone and after ",[677,147593,147596],{"href":147594,"rel":147595},"https://therealdanvega.com/blog/2017/12/29/my-2017-year-in-review",[681],"spending some time reflecting on 2017",", it's time to start looking forward to 2018.",[22,147599,147600],{},"Just so we are clear on where this is going, this isn't a \"new year, new me\" post. If you want to accomplish goals, you have to define them first. I sat down this week and wrote down some things I wanted to accomplish in 2018.",[26,147602,147603],{"id":147584},"My 2018 Goals",[22,147605,147606],{},"These goals are not in any specific order but I did try to group them into buckets.",[636,147608,147610],{"id":147609},"personal","Personal",[22,147612,147613],{},"Personally, 2018 is all about focusing on my health. With a baby on the way and the big 40 arriving this year, I need to make this more of a priority. Exercise has never been an issue for me, but I need to make clean eating a must and not just something I focus on when I want to lose some weight. I have already given up diet soda (which I was literally addicted to) for the past 2 months of the year.",[22,147615,147616],{},[677,147617,147620],{"href":147618,"rel":147619},"https://therealdanvega.com/wp-content/uploads/2017/12/pexels-photo-315737.jpeg",[681],[653,147621],{"alt":147622,"src":147623},"2018 Goals - Eating clean","./pexels-photo-315737-1024x663.jpeg",[22,147625,147626],{},"I have also been reading a lot more about the importance of a morning routine. A lot of successful people I look up to swear by it and I think this is going to help me with so many things. I am usually up around 6 AM and don't start work (I work from home) until 8:00 AM. This is going to be a good time to meditate, move for 5 minutes ( squats, pushups, situps, burpees) and spend 30 -60 min reading or listening to podcasts.",[915,147628,147629,147631,147633,147635,147647,147649,147652],{},[37,147630,143865],{},[37,147632,143868],{},[37,147634,143871],{},[37,147636,143874,147637],{},[915,147638,147639,147641,147643,147645],{},[37,147640,143879],{},[37,147642,143888],{},[37,147644,143882],{},[37,147646,143885],{},[37,147648,143891],{},[37,147650,147651],{},"Complete the nursery (Baby on the way)",[37,147653,143897],{},[636,147655,61058],{"id":61057},[22,147657,147658],{},"I am not sure if you know this about me, but I am a serial learner. There are some things that I want to make sure I focus on learning this year.",[22,147660,147661],{},[677,147662,147665],{"href":147663,"rel":147664},"https://therealdanvega.com/wp-content/uploads/2017/12/open-book-library-education-read-159621.jpeg",[681],[653,147666],{"alt":147667,"src":147668},"Education in 2018","./open-book-library-education-read-159621-1024x769.jpeg",[28831,147670,147672],{"id":147671},"build-an-ios-app","Build an iOS app",[22,147674,147675],{},"I really want to create an iOS application and launch it in the app store this year. Money is not my primary focus here, and I get it.. the app market is a really hard one to get into. It is just one of those things that I have always wanted to do (along with writing a book) so I am making it a priority this year. I don't know what my app is going to do, but I will start brainstorming this soon.",[22,147677,147678],{},[677,147679,147682],{"href":147680,"rel":147681},"https://therealdanvega.com/wp-content/uploads/2017/12/twitter-facebook-together-exchange-of-information-147413.jpeg",[681],[653,147683],{"alt":143916,"src":143917},[28831,147685,147687],{"id":147686},"cryptocurrency","Cryptocurrency ",[22,147689,147690],{},"I have heard the cryptocurrency rumblings for a couple of years now, and like most people, I just ignored it. It seems like 2017 was a big year for Cryptocurrency and it doesn't seem to be going anywhere. Between some books and courses, I would like to inform myself more about a few things. The blockchain, which is the technology behind the emergence of cryptocurrency, is very interesting. This technology could very well shape the internet as we know it going forward and I don't want to be left behind. I also have a little bit of money invested in some coins, but this is something I would like to expand in the new year as well.",[22,147692,147693],{},[677,147694,147697],{"href":147695,"rel":147696},"https://therealdanvega.com/wp-content/uploads/2017/12/pexels-photo-730569.jpeg",[681],[653,147698],{"alt":57,"src":143929},[28831,147700,143933],{"id":143932},[22,147702,147703],{},"This Christmas I got an Amazon echo and I am absolutely loving it. I'm intrigued by the idea of voice-activated applications, and with Echo's popularity, I believe this is a great platform to be on. The programmer in me wants to dive right in and start teaching Alexa some new skills. I will admit I know nothing about writing applications for the echo, so I will be starting from scratch here.",[22,147705,147706],{},[677,147707,147709],{"href":147708},"./61yI7vWa83L._SX522_.jpg",[653,147710],{"alt":57,"src":147708},[28831,147712,143940],{"id":143939},[22,147714,147715],{},"I have never been interested in marketing. I have always taken the stance that I will produce content and let someone else worry about the marketing. As an entrepreneur, and someone who puts out a ton of content, it really hit me this year that I was going about it the wrong way.",[22,147717,147718],{},[677,147719,147722],{"href":147720,"rel":147721},"https://therealdanvega.com/wp-content/uploads/2017/12/pexels-photo-266176.jpeg",[681],[653,147723],{"alt":143948,"src":143949},[22,147725,147726],{},"I produce content for 2 very simple reasons. First, I really enjoy helping people. I remember being a new software developer and learning from people just like the present day me. I also enjoy the extra income some of my work produces, but this should never be the motivating factor. If you can provide a product or service that truly transforms someone's life, then that is a good place to be. With that said, I want to find more ways to get my content out there, and I am not focused enough on this side of my business. I am working on a plan as we speak of books, courses, mentors and even hiring an agency to handle this to make this a bigger priority in 2018.",[22,147728,147729],{},[646,147730,147731],{},"Marketing Areas of focus",[915,147733,147734,147737,147740,147743,147746,147749],{},[37,147735,147736],{},"Facebook Ads",[37,147738,147739],{},"Google Ads",[37,147741,147742],{},"Google Analytics",[37,147744,147745],{},"Sales Funnels",[37,147747,147748],{},"Chat Bots",[37,147750,147751],{},"Digital Marketing in general",[22,147753,147754],{},[4534,147755,147756,147758],{},[646,147757,49733],{}," Do you have any recommendations for books, courses or agencies to improve in any of topics mentioned above?",[28831,147760,37756],{"id":86219},[22,147762,147763],{},"I mentioned this in my morning routine, but I want to become a more consistent reader. This isn't going to happen if I don't actually set aside time for it. I know that I am not going to be able to read 30 min every single day, but if I build this into my routine, I should be able to stick to a goal. My goal for is 2018 is to read about 30 books. This is going to be mostly software development and business related, but I am going to try and sneak in a few for fun. I am going to keep a list so we can come back and look at this later. If you're interested in me writing a quick review of each book I read this year please let me know in the comments below.",[22,147765,147766],{},[677,147767,147770],{"href":147768,"rel":147769},"https://therealdanvega.com/wp-content/uploads/2017/12/books-bookstore-book-reading-159711.jpeg",[681],[653,147771],{"alt":104900,"src":143581},[28831,147773,143953],{"id":143952},[22,147775,147776],{},"This is another technology that I keep hearing is going to take over the world. I don't know that I will get into any actual development in the space this year, but at the very least I want to get more informed on where it's current state is and where it's going.",[22,147778,147779],{},[677,147780,147783],{"href":147781,"rel":147782},"https://therealdanvega.com/wp-content/uploads/2017/12/pexels-photo-123335.jpeg",[681],[653,147784],{"alt":147785,"src":147786},"2108 Goals - Augmented Reality","./pexels-photo-123335-1024x682.jpeg",[636,147788,147790],{"id":147789},"professionally","Professionally",[22,147792,147793],{},"If you aren't aware, I have been with the same company for almost 6 years now and everything there is going great. I do have some goals there but they aren't really things I want to share here so, for now, I will just leave it at that.",[28831,147795,143969],{"id":143968},[22,147797,147798],{},"I know this might not seem like a huge goal, but I want to release 4 new courses this year. I already know what two of them are going to be (coming soon) and have some ideas for the other two. If you have some ideas for courses that you would like me to pursue, please leave me a comment below.",[22,147800,147801],{},[677,147802,147805],{"href":147803,"rel":147804},"https://therealdanvega.com/wp-content/uploads/2017/12/pexels-photo-450035.jpeg",[681],[653,147806],{"alt":147807,"src":147808},"2018 Goals - 4 new courses","./pexels-photo-450035-1024x682.jpeg",[28831,147810,147812],{"id":147811},"video-live-video-video-production","Video, Live Video & Video Production",[22,147814,147815,147816,147821],{},"When it comes to content on the web, video is king, and I have been preaching this for a couple years now. It's time I take my own advice and focus more on video. In 2017, I really focused on blogging, and while I am going to continue to blog, I am also going to start my content story with video. This means more live videos on ",[677,147817,147820],{"href":147818,"rel":147819},"http://www.facebook.com/therealdanvega",[681],"my Facebook page",", more YouTube videos and just being more comfortable in front of the camera.",[22,147823,147824],{},[677,147825,147828],{"href":147826,"rel":147827},"https://therealdanvega.com/wp-content/uploads/2017/12/pexels-photo-257904.jpeg",[681],[653,147829],{"alt":147830,"src":147831},"2018 Goals - Video Production","./pexels-photo-257904-1024x682.jpeg",[22,147833,147834,147835,147840,147841,147846,147847],{},"With a shift in focus to video, I need to get more creative with video editing. I know the basics of editing and know my way around Final Cut Pro and Adobe Premiere Pro. I know the basics, but would really like to improve the quality of my course promo videos, YouTube tutorials and some videos I will be working on for Facebook ads in the near future. ",[677,147836,147839],{"href":147837,"rel":147838},"https://videoschoolonline.com/",[681],"Phil Ebiner"," is a friend and someone I look up to when it comes to all things video. That is why I am taking his course \"",[677,147842,147845],{"href":147843,"rel":147844},"https://www.udemy.com/adobe-premiere-pro-video-editing",[681],"Adobe Premiere Pro CC for beginners","\". This is a promo he created for a new course of his and I absolutely fell in love with it. This is a great example of how to create a perfect course promo video. ",[677,147848,147849],{"href":147849,"rel":147850},"https://www.youtube.com/watch?v=Tu6vV3huI6E",[681],[636,147852,37576],{"id":86245},[22,147854,147855],{},"There is a big question mark next to this one so I am not sure what to do here. I love podcasts so much that it is probably to a fault. I listen to them in the morning, in the car, on a run and whenever I can find some extra time. The reason I say that this is probably to a fault is that I listen too much and don't take enough action. I have a voice and would love to share my thoughts on a variety of topics. I have been a software developer & serial entrepreneur for half of my life. These are things I would love to talk about, but I am not even sure where to start.",[22,147857,147858],{},[677,147859,147862],{"href":147860,"rel":147861},"https://therealdanvega.com/wp-content/uploads/2017/12/microphone-audio-computer-sound-recording-55800-1.jpeg",[681],[653,147863],{"alt":143673,"src":143674},[22,147865,147866,147867,147872],{},"I do know that If I come up with a good plan for a podcast that I would probably learn from the best before I dive right in. Pat Flynn has a course called ",[677,147868,147871],{"href":147869,"rel":147870},"https://courses.smartpassiveincome.com/p/power-up-podcasting",[681],"Power-Up Podcasting"," that I have had my eye on for a while now.",[28831,147874,147876],{"id":147875},"new-business-ventures","New Business Ventures",[22,147878,147879,147880,60103,147882,147888,147889,147894],{},"Towards the end of this year, I turned my side hustle into a legitimate business. The primary focus was to separate my personal and business finances, but I do have some ideas that need to get put into action in the new year. For now, I am just going to share the name and logo with you and fill you in on this in the coming months. ",[646,147881,143792],{},[677,147883,147886],{"href":147884,"rel":147885},"https://therealdanvega.com/wp-content/uploads/2017/12/badge-logo.png",[681],[653,147887],{"alt":143792,"src":143793}," Please ",[677,147890,147893],{"href":147891,"rel":147892},"https://www.facebook.com/codemonkeyu/",[681],"follow Code Monkey on Facebook"," for updates. I also have a very good idea for a physical space right here in Northeast Ohio that could be a game changer. I can guarantee someone is going to do this within the next 2 years and the only question is, will it be me?",[28831,147896,147898],{"id":147897},"non-profit","Non-Profit",[22,147900,147901,147902,147905],{},"I am the President of a non-profit called CLE Cares. For the past 10 years, we have put on an event called ",[677,147903,104942],{"href":145838,"rel":147904},[681],". Up until this point, we have strictly been focused on this one single event and this year that stops. We are much more than a single event and in 2018, I have a few goals that I would like us to accomplish.",[915,147907,147908,147911,147914,147917],{},[37,147909,147910],{},"CLE Cares Branding & Website",[37,147912,147913],{},"Summer Event",[37,147915,147916],{},"Sponsors for Toys for Shots",[37,147918,147919],{},"Increase attendance for Toys for Shots 2018",[22,147921,147922],{},[677,147923,147926],{"href":147924,"rel":147925},"https://therealdanvega.com/wp-content/uploads/2017/12/santa_sign.png",[681],[653,147927],{"alt":147928,"src":147929},"CLE Cares & Toys for Shots","./santa_sign-1024x1024.png",[26,147931,147933],{"id":147932},"productivity-and-focus","Productivity and Focus",[22,147935,147936,147937,147942],{},"This seems like a lot, and as I am writing this, I am already a little overwhelmed. We all have the same 24 hours in a day, so how is that some people change the world and others don't? I believe a lot of this has to do with productivity and focus. So how am I going to accomplish these goals? The first thing I did was pick up ",[677,147938,147941],{"href":147939,"rel":147940},"https://fullfocusplanner.com/",[681],"Michael Hyatt's Full Focus Planner",". I am a huge fan of Michael's, and he certainly knows his stuff when it comes to productivity and focus. This is going to become part of my morning journaling and will help me work towards daily, weekly and monthly goals.",[22,147944,147945],{},[677,147946,147948],{"href":147947},"./about-fullfocusplanner-cover.png",[653,147949],{"alt":147950,"src":147947},"2018 Goals - Full Focus Planner",[22,147952,147953],{},"On the 1st of every month, I go through a monthly checklist of items that need my attention like updating growth reports, earnings and expenses. I am also going to add a \"Goals check-in\" task to this day. When the 1st rolls around, I am going to read this post and ask myself \"what have I done to work towards any of these goals?\" and check off the ones I have completed.",[26,147955,1499],{"id":1498},[22,147957,147958,147959,147961],{},"Well, there you have it, my goals for 2018. I have always been taught that if you aren't setting goals that are a little scary, you're doing it wrong. I need to challenge myself and I think this list does exactly that. We will revisit this post at the end of 2018 and see where I ended up. _",[646,147960,49733],{}," What are your goals for 2108? What are some new things that you want to learn? _",{"title":57,"searchDepth":76,"depth":76,"links":147963},[147964,147970,147971],{"id":147584,"depth":76,"text":147603,"children":147965},[147966,147967,147968,147969],{"id":147609,"depth":82,"text":147610},{"id":61057,"depth":82,"text":61058},{"id":147789,"depth":82,"text":147790},{"id":86245,"depth":82,"text":37576},{"id":147932,"depth":76,"text":147933},{"id":1498,"depth":76,"text":1499},{"_id":147973,"path":147974,"title":147975,"description":147975,"meta":147976,"body":147981},"content/blog/2017/12/29/my-2017-year-in-review.md","/blog/2017/12/29/my-2017-year-in-review","My 2017 Year in Review",{"slug":147977,"date":147978,"published":13,"tags":147979,"author":-1,"cover":147980,"excerpt":-1},"my-2017-year-in-review","2017-12-29T09:14:08-05:00",[33475],"./pexels-photo-282919-760x571.jpeg",{"type":19,"value":147982,"toc":148102},[147983,147986,147990,147993,147997,148000,148006,148009,148012,148016,148019,148022,148026,148029,148032,148036,148039,148042,148046,148049,148052,148055,148058,148072,148076,148089,148094,148096],[22,147984,147985],{},"It seems like we say this every year, but can you believe the year is over already? 2017 is going to go down as one of the more memorable years of my life. I thought I would take this time to give you a little \"year in review\" before I share my 2018 goals. ",[26,147987,147989],{"id":147988},"_2017-year-in-review-highlights","2017 Year in Review Highlights",[22,147991,147992],{},"As I said before, this has been one of the craziest (in a good way) years of my life. I try and remind myself through the ups and downs just how lucky I am. As I sat down to write this post, it really hit me just how true that is. ",[636,147994,147996],{"id":147995},"being-a-new-homeowner","Being a new homeowner",[22,147998,147999],{},"This was the first year in our new house and as many of you know, being a first time homeowner has its ups and downs. ",[22,148001,148002],{},[653,148003],{"alt":148004,"src":148005},"My first time being a homeowner","./IMG_3572-1024x768.jpg",[22,148007,148008],{},"We worked on many projects this year, but none bigger than the kitchen remodel. My brother and I woke up on New Years Day (not feeling so great) and started ripping out the kitchen. We stripped the kitchen down to absolutely nothing and built it from scratch. I have to start by thanking my brother Joe because without him we would not have gotten this done. In fact, my whole family pitched in, so I would also like to thank them. Thanks to my wife for her vision of what turned out to be an amazing kitchen! ",[22,148010,148011],{},"homeowner gallery",[26,148013,148015],{"id":148014},"bachelor-bachelorette-party","Bachelor / Bachelorette Party ",[22,148017,148018],{},"We had a joint bachelor/bachelorette party in New Orleans this year and it was so much fun. We took a huge group of friends down to the Big Easy and had a weekend full of fun, laughter, and great memories. As a group, we spent a lot of time walking the city and enjoying the food and drinks. The food, the architecture and the music are what set this city apart from others. ",[22,148020,148021],{},"the-big-easy gallery",[26,148023,148025],{"id":148024},"wedding-day","Wedding Day",[22,148027,148028],{},"The main reason this year is going to go down as one of the best for me is I got married! I got to marry my best friend in the whole world and our wedding day was one for the books. My wife paid attention to every single detail and our big day was one to remember. We had perfect weather and all of our friends and family were there to share in our big day. ",[22,148030,148031],{},"wedding-day gallery",[26,148033,148035],{"id":148034},"honeymoon","Honeymoon",[22,148037,148038],{},"After the wedding, it was off to Jamaica for our honeymoon. We spent almost 2 weeks in one of the most magical places I have ever been to. It was just so great to be able to relax and not worry about anything and just enjoy some alone time with my wife. ",[22,148040,148041],{},"honeymoon gallery",[26,148043,148045],{"id":148044},"were-expecting","We're expecting! ",[22,148047,148048],{},"As if this year wasn't already the greatest ever, we were so excited to find out that we are expecting our first baby in May 2108! ",[22,148050,148051],{},"baby gallery",[26,148053,145833],{"id":148054},"the-real-dan-vega",[22,148056,148057],{},"I had a really good year growing my personal brand. There are going to be changes coming in the new year but for now, I want to focus on what we did this year.",[915,148059,148060,148063,148066,148069],{},[37,148061,148062],{},"Went from 10,000 page views a month to 60,000.",[37,148064,148065],{},"Went from 100 email subscribers to 1300.",[37,148067,148068],{},"Crossed 35,000 Students worldwide. ",[37,148070,148071],{},"Complete website redesign & makeover",[636,148073,148075],{"id":148074},"angular-4-java-developers","Angular 4 Java Developers",[22,148077,148078,148079,148084,148085,2755],{},"With the help of my friend ",[677,148080,148083],{"href":148081,"rel":148082},"http://springframeworkguru.com",[681],"John Thompson,"," we launched a new course called \"Angular 4 Java Developers\". We put some real thought and hard work into this course and it shows. I think this is the best course I have created and it's already helping so many people. If you want to find out more about this course visit ",[677,148086,148087],{"href":148087,"rel":148088},"https://courses.therealdanvega.com/p/jhipster",[681],[22,148090,148091,36672],{},[653,148092],{"alt":148075,"src":148093},"./UdemyJohnDan02-1024x576.png",[26,148095,1499],{"id":1498},[22,148097,148098,148099,148101],{},"Well, there it is, my 2017 year in review. I didn't post any goals for this year yet, so I have nothing to hold myself accountable to. That is going to change and in a few days, I am going to let you know what my personal and professional goals are for the year. I think it's so important to sit down and actually put your goals for the upcoming year on paper to make a solid plan for yourself and hold yourself accountable, so stay tuned for mine, and I encourage you to do this as well. Thanks for reading... until next time! _",[646,148100,49733],{}," What big things happened in 2017 for you? What goals do you have for 2018? _",{"title":57,"searchDepth":76,"depth":76,"links":148103},[148104,148107,148108,148109,148110,148111,148114],{"id":147988,"depth":76,"text":147989,"children":148105},[148106],{"id":147995,"depth":82,"text":147996},{"id":148014,"depth":76,"text":148015},{"id":148024,"depth":76,"text":148025},{"id":148034,"depth":76,"text":148035},{"id":148044,"depth":76,"text":148045},{"id":148054,"depth":76,"text":145833,"children":148112},[148113],{"id":148074,"depth":82,"text":148075},{"id":1498,"depth":76,"text":1499},{"_id":148116,"path":148117,"title":148118,"description":148118,"meta":148119,"body":148124},"content/blog/2017/12/08/getting-started-with-java.md","/blog/2017/12/08/getting-started-with-java","Getting Started with Java course",{"slug":148120,"date":148121,"published":13,"tags":148122,"author":-1,"cover":148123,"excerpt":-1},"getting-started-with-java","2017-12-08T09:00:23-05:00",[56],"./apple-computer-desk-18105.jpg",{"type":19,"value":148125,"toc":148428},[148126,148129,148138,148141,148144,148147,148150,148153,148158,148162,148165,148207,148211,148214,148217,148221,148225,148414,148416,148422],[22,148127,148128],{},"A few months ago I picked up a brand new 15\" MacBook Pro. I thought to myself this is a perfect time to show others how I set up my local development environment for Java. With a fresh machine, this would be a lot easier to take students from step 1 to writing Java applications.",[22,148130,148131,148132,148137],{},"The reason I am telling you this is because ",[677,148133,148136],{"href":148134,"rel":148135},"https://www.danvega.dev/courses/getting-started-with-java",[681],"I recently launched a new course around this",". In this article, I am going to give you my tips for getting started with Java as well as some details about this course.",[26,148139,148140],{"id":148120},"Getting Started with Java",[22,148142,148143],{},"When I first started out programming it was a little overwhelming to learn a new language. Part of that is because I am really really really old and we didn't have all the resources you have at your exposure today.",[22,148145,148146],{},"The other part of that is that it was really confusing to know what the easiest way was to set up a local development environment. If you were going to back a pie and didn't have a list of ingredients would you start baking? The answer is no, you need to have the required ingredients to bake and you need to have a solid development environment to write code. ",[22,148148,148149],{},"With this in mind, I set out to help people in 2 phases. First, I want you to quickly stand up a development environment. There are traditional ways of installing software packages and there better ways that make this step really easy to do. Once this is complete you can write your very first Java program.",[22,148151,148152],{},"The other phase is introducing you to concepts like build tools, other languages that run on the JVM and some tools and frameworks that you will come across in your journey to learning Java.",[22,148154,148155],{},[653,148156],{"alt":148140,"src":148157},"./pexels-photo-2-1024x682.jpg",[636,148159,148161],{"id":148160},"key-terms","Key Terms",[22,148163,148164],{},"When I was \"Getting Started with Java\", one thing that tripped me up was all of the acronyms the language used. What is a JVM, JDK, JRE and how do they differ from each other. Here are a few concepts to help you get started. ",[915,148166,148167,148172,148178,148184,148189,148195,148201],{},[37,148168,148169,148171],{},[646,148170,15],{}," - Java is the programming language that we are using to write our applications in.",[37,148173,148174,148177],{},[646,148175,148176],{},"Java SE"," (Java Standard Edition) - When people are talking about Java, 99% of the time they are talking about the standard edition. There are other editions out there but we won't get into that at this time. ",[37,148179,148180,148183],{},[646,148181,148182],{},"JRE"," (Java Runtime Environment) - This is what you need to run Java applications. This is what non-developers will have installed on their machines to run Java applications. ",[37,148185,148186,148188],{},[646,148187,61344],{}," (Java Development Kit) - JDK is what we as Java developers need to write Java applications. This contains the JRE along with tools like compilers and debuggers that help us create applications. ",[37,148190,148191,148194],{},[646,148192,148193],{},"JVM"," (Java Virtual Machine) - When we start writing Java applications we compile human-readable code into bytecode. That bytecode runs on the virtual machine and the JVM is what allows us to write our code once and run it anywhere. ",[37,148196,148197,148200],{},[646,148198,148199],{},"SDK","( Software Development Kit) - This is a collection of libraries that we can use to build out applications. Java itself comes with an SDK for building out Java applications. ",[37,148202,148203,148206],{},[646,148204,148205],{},"JAR"," (Java ARchive) - A JAR is nothing more than a zip file that contains 1 or more classes with some meta information about them. This is how we package up our applications. ",[26,148208,148210],{"id":148209},"getting-started-with-java-course","Getting Started with Java Course",[22,148212,148213],{},"Now that I have given you a little information about getting started with Java, I would like to give you some details about my course. I set out to record some content that would help beginners get Java installed on their machine and set up a local development environment. What I ended up with was so much more. We also walk through things like advanced tooling, build tools and other languages that run on the JVM.",[22,148215,148216],{},"I didn't set out to make you an expert in all of these but to get you familiar with them. This way when they come up in a conversation you will know what your friends or coworkers are talking about. I have been working on the JVM for most of 18-year career and I have learned more than a few tips and tricks along the way. This course is fairly short at 3-4 hours in total length and this is what we are going to cover. ",[22,148218,148219],{},[653,148220],{"alt":148140,"src":52844},[636,148222,148224],{"id":148223},"getting-started-with-java-course-curriculum","Getting Started with Java Course Curriculum ",[915,148226,148227,148240,148265,148283,148305,148328,148357,148375,148392,148402],{},[37,148228,117826,148229],{},[915,148230,148231,148234,148237],{},[37,148232,148233],{},"Goals of the course",[37,148235,148236],{},"Meet your instructor",[37,148238,148239],{},"Why you should learn Java (article)",[37,148241,148242,148243],{},"Getting started with Java\n",[915,148244,148245,148248,148251,148254,148256,148259,148262],{},[37,148246,148247],{},"Java Terminology",[37,148249,148250],{},"Quiz: Java Terms",[37,148252,148253],{},"Java version check",[37,148255,61356],{},[37,148257,148258],{},"Installing Java",[37,148260,148261],{},"Exercise: Java version check",[37,148263,148264],{},"Exercise Review",[37,148266,148267,148268],{},"Writing your first Java program\n",[915,148269,148270,148273,148276,148278,148281],{},[37,148271,148272],{},"Writing your first Java program",[37,148274,148275],{},"Java program execution",[37,148277,103760],{},[37,148279,148280],{},"Exercise: Writing your 1st program",[37,148282,148264],{},[37,148284,148285,148286],{},"Text Editors & IDEs\n",[915,148287,148288,148291,148294,148297,148300,148303],{},[37,148289,148290],{},"Text Editors & IDEs overview",[37,148292,148293],{},"Text Editor & IDE options (article) ",[37,148295,148296],{},"Installing IntelliJ ",[37,148298,148299],{},"Hello, World. IntelliJ Edition",[37,148301,148302],{},"Exercise: Writing your first application in IntelliJ",[37,148304,148264],{},[37,148306,148307,148308],{},"IntelliJ \n",[915,148309,148310,148313,148316,148319,148322,148325],{},[37,148311,148312],{},"IntelliJ tour",[37,148314,148315],{},"Themes",[37,148317,148318],{},"Plugins",[37,148320,148321],{},"Code formatting & organizing imports",[37,148323,148324],{},"Templates",[37,148326,148327],{},"Quiz",[37,148329,148330,148331],{},"Advanced Tooling\n",[915,148332,148333,148336,148339,148342,148345,148348,148351,148354],{},[37,148334,148335],{},"Advanced tooling overview",[37,148337,148338],{},"Code generation",[37,148340,148341],{},"Debugging ",[37,148343,148344],{},"Running IntelliJ from the command line",[37,148346,148347],{},"Refactoring",[37,148349,148350],{},"Github Source Control",[37,148352,148353],{},"Java Decompiler",[37,148355,148356],{},"Shortcut Keys",[37,148358,148359,148360],{},"Java Build Tools\n",[915,148361,148362,148365,148367,148370,148372],{},[37,148363,148364],{},"Build Tools Introduction",[37,148366,94504],{},[37,148368,148369],{},"Maven Dependencies ",[37,148371,54946],{},[37,148373,148374],{},"Quiz: Why Build Tools? ",[37,148376,148377,148378],{},"JVM Languages\n",[915,148379,148380,148383,148385,148387,148389],{},[37,148381,148382],{},"Languages Intro",[37,148384,146146],{},[37,148386,53590],{},[37,148388,135984],{},[37,148390,148391],{},"Scala",[37,148393,148394,148395],{},"Getting Help\n",[915,148396,148397,148400],{},[37,148398,148399],{},"Java Documentation",[37,148401,4098],{},[37,148403,148404,148405],{},"Bonus\n",[915,148406,148407,148410,148412],{},[37,148408,148409],{},"My Contact Information",[37,148411,145728],{},[37,148413,52950],{},[26,148415,1499],{"id":1498},[22,148417,148418,148419,148421],{},"I had a lot of fun putting this course together and I am pretty excited to share it with all of you. I hope you enjoy this course and if you have any questions about it please leave them below. _",[646,148420,65584],{},": What was the toughest part of learning Java for you? _",[22,148423,148424],{},[677,148425,148427],{"href":148134,"rel":148426},[681],"Enroll in my Getting Started with Java Course",{"title":57,"searchDepth":76,"depth":76,"links":148429},[148430,148433,148436],{"id":148120,"depth":76,"text":148140,"children":148431},[148432],{"id":148160,"depth":82,"text":148161},{"id":148209,"depth":76,"text":148210,"children":148434},[148435],{"id":148223,"depth":82,"text":148224},{"id":1498,"depth":76,"text":1499},{"_id":148438,"path":148439,"title":148440,"description":148440,"meta":148441,"body":148446},"content/blog/2017/11/08/angular-4-java-developers-course-now-live.md","/blog/2017/11/08/angular-4-java-developers-course-now-live","Angular 4 Java Developers Course is now Live!",{"slug":148442,"date":148443,"published":13,"tags":148444,"author":-1,"cover":148445,"excerpt":-1},"angular-4-java-developers-course-now-live","2017-11-08T16:15:32-05:00",[56,11002],"./greetings_java_hipsters-760x525.png",{"type":19,"value":148447,"toc":148651},[148448,148459,148462,148466,148473,148477,148481,148528,148532,148540,148545,148571,148575,148578,148619,148623],[22,148449,148450,148451,976,148456,148458],{},"I'm so excited to finally announce the release of my new course with ",[677,148452,148455],{"href":148453,"rel":148454},"https://springframework.guru",[681],"John Thompson",[646,148457,148075],{},"! In this course, you will learn how to build applications using some of the hottest technologies in the industry today, including Spring Boot and Angular. These are two of our favorite technologies to work with right now, it really makes developing applications a breeze.",[22,148460,148461],{},"In this introduction to the course, I am going to give you all of the details, as well as a special offer so please read the entire article. ",[26,148463,148465],{"id":148464},"about-angular-4-java-developers","About Angular 4 Java Developers",[22,148467,148468,148469,148472],{},"First off, I just want to say congrats to my partner in crime on this course, ",[677,148470,148455],{"href":148453,"rel":148471},[681],". We have put a lot of thought and hard work into this course and I think it's really going to show. We had a clear vision when we started this course and I think we ended up delivering. What we started out building is very different than what it looks like today, but you need to put yourself in your student's shoes and ask yourself \"Am I getting what I need out of this course?\" ",[22,148474,148475],{},[653,148476],{"alt":148075,"src":148093},[636,148478,148480],{"id":148479},"what-you-will-learn","What you will learn",[915,148482,148483,148492,148498,148504,148510,148513,148516,148522],{},[37,148484,148485,67835,148488,148491],{},[646,148486,148487],{},"Building Angular & Spring Boot apps",[646,148489,148490],{},"without JHipster -"," To start this course off, we are going to build a small tasks application that demonstrates just what goes into putting all of these technologies together.",[37,148493,148494,148497],{},[646,148495,148496],{},"What is JHipster?"," Next, we are going to learn about what JHipster is and why you should never start another Angular & Spring Boot project without it again.",[37,148499,148500,148503],{},[646,148501,148502],{},"How to setup your development environment -"," Any good cook knows that if you don't have all of your ingredients and tools, it is really hard to prepare a meal. The same goes for you as a developer and your environment. We will walk you through setting up your environment and showing you the tools that you will need.",[37,148505,148506,148509],{},[646,148507,148508],{},"Compare starting from scratch & JHipster -"," We will take that same application we built from scratch and show you how we build it in JHipster.",[37,148511,148512],{},"**JDL Studio -**A great way to build out your domain model.",[37,148514,148515],{},"**MongoDB -**Learn how to use MongoDB in your applications.",[37,148517,148518,148521],{},[646,148519,148520],{},"Microservices -"," Are all the buzz these days. We are going to learn about what they are and more importantly when to use them. After that, we will learn how to build out microservices in our JHipster application.",[37,148523,148524,148527],{},[646,148525,148526],{},"Build and deploy a real-world application -"," We won't just teach you about how to build real-world applications, we will actually help you do it. In this course, you are going to build a real-world application from requirements to production.",[26,148529,148531],{"id":148530},"running-for-brews","Running for Brews",[22,148533,148534,148535,148539],{},"I think the best part of this course is that we built a real-world application from conception to completion. We sat down with the ",[677,148536,148531],{"href":148537,"rel":148538},"http://runningforbrews.com/",[681]," organization and talked through the requirements with them. Running for Brews is a national running club that is all about bringing people together. In this course, we helped them build a loyalty program that is going to reward runners for consistently showing up. I had so much fun on this project that as soon as the weather gets warm again, I am going to organize my own RFB location! ",[22,148541,148542],{},[653,148543],{"alt":148531,"src":148544},"./2017-11-08_07-37-19-1024x647.png",[915,148546,148547,148553,148559,148565,148568],{},[37,148548,148549,148552],{},[646,148550,148551],{},"Starting with requirements -"," Before you can build an application, you need to listen to your client and get the requirements. John and I sit down in an interview with the project stakeholder to find out what he wants this application to do.",[37,148554,148555,148558],{},[646,148556,148557],{},"Generate project from scratch -"," We will create this project from scratch so that you can follow right along with us.",[37,148560,148561,148564],{},[646,148562,148563],{},"Customize the UI -"," A lot of students have questions on how to customize the default UI in JHipster. We are going to walk through a number of UI changes to give our application a custom look and feel.",[37,148566,148567],{},"**Helpful Exercises -**Even as we are building out this real-world application, we will stop to give you some helpful exercises to go through.",[37,148569,148570],{},"**Production -**Where we all want to be, right? After our application is completed, we will show you what it takes to get us into production.",[26,148572,148574],{"id":148573},"angular-4-java-developers-bonus-content","Angular 4 Java Developers Bonus Content",[22,148576,148577],{},"When you purchase this course, you will get access to exclusive bonus content!",[915,148579,148580,148586,148595,148601,148607,148613],{},[37,148581,148582,148585],{},[646,148583,148584],{},"Downloadable lessons -"," There are 10 modules (over 11 total hours!) that you can take with you anywhere.",[37,148587,148588,148591,148592,2755],{},[646,148589,148590],{},"IntelliJ Ultimate License -"," As a special bonus, you will have access to download a ",[646,148593,148594],{},"90 day free trial of IntelliJ Ultimate Edition",[37,148596,148597,148600],{},[646,148598,148599],{},"Bonuses to keep you on track -"," Including interviews with the \"Running for Brews\" President on how this application is working out for them.",[37,148602,148603,148606],{},[646,148604,148605],{},"Exclusive access to a Facebook community -"," To discuss coursework and trade ideas with your fellow classmates.",[37,148608,148609,148612],{},[646,148610,148611],{},"Live Videos"," - As part of our community, you will have access to exclusive Facebook live events and recorded videos.",[37,148614,148615,148618],{},[646,148616,148617],{},"T-Shirt giveaways -"," If you liked the JHipster shirt Dan was wearing in some of the lessons, we will be giving a few of these away to members only.",[26,148620,148622],{"id":148621},"what-are-you-waiting-for","What are you waiting for?",[22,148624,148625,148626,148631,148632,148637,148638,43654,148645,148650],{},"John and I have worked really hard on creating this course and we hope that you find it helpful in your software development journey. This course will normally sell for $200 but as part of the launch, I am giving my loyal blog readers a special gift. As a valued reader, I am going to offer you the course for ",[4534,148627,148628],{},[646,148629,148630],{},"85% off regular price!"," Yep, you can get access to over 11 hours of content for just ",[4534,148633,148634],{},[646,148635,148636],{},"$29.99!"," This special launch pricing is available until the end of ",[62,148639,148641],{"style":148640},"aBn",[62,148642,148644],{"style":148643},"aQJ","Sunday",[677,148646,148649],{"href":148647,"rel":148648},"https://therealdanvega.teachable.com/p/jhipster/?product_id=456739&coupon_code=TRDVLAUNCH_2999",[681],"Click here"," to buy the course for just $29.99!",{"title":57,"searchDepth":76,"depth":76,"links":148652},[148653,148656,148657,148658],{"id":148464,"depth":76,"text":148465,"children":148654},[148655],{"id":148479,"depth":82,"text":148480},{"id":148530,"depth":76,"text":148531},{"id":148573,"depth":76,"text":148574},{"id":148621,"depth":76,"text":148622},{"_id":148660,"path":148661,"title":148662,"description":148662,"meta":148663,"body":148668},"content/blog/2017/10/04/compile-groovy-java-gradle-build.md","/blog/2017/10/04/compile-groovy-java-gradle-build","How to compile Groovy before Java in a Gradle Build",{"slug":148664,"date":148665,"published":13,"tags":148666,"author":-1,"cover":148667,"excerpt":-1},"compile-groovy-java-gradle-build","2017-10-04T08:32:40-04:00",[53536],"./614px-Groovy-logo.svg_-e1459476180685.png",{"type":19,"value":148669,"toc":149325},[148670,148673,148676,148680,148683,148689,148695,148698,148704,148707,148712,148716,148719,148911,148914,149062,149065,149071,149075,149078,149227,149230,149234,149237,149311,149314,149316,149322],[22,148671,148672],{},"I had an interesting problem come up at work last week and I would like to share it with you today. A coworker was having an issue with a Gradle build. Whenever he tried to compile the project it threw an error saying it couldn't find the Groovy class he was trying to call from Java.",[22,148674,148675],{},"In this article, we are going to walk through setting up a project that will illustrate this problem. We will then examine what normal behavior is in a Gradle build and how we can make a change to fix this problem that we were facing. ",[26,148677,148679],{"id":148678},"create-a-new-gradle-project","Create a new Gradle Project",[22,148681,148682],{},"The first thing we are going to do is create a new project using Gradle. This is going to give us a standard Gradle & Java project layout. ",[22,148684,148685],{},[653,148686],{"alt":148687,"src":148688},"New Gradle Project","./2017-10-04_07-37-26-300x197.png",[22,148690,148691],{},[653,148692],{"alt":148693,"src":148694},"Gradle Wrapper","./2017-10-04_07-38-08-300x197.png",[22,148696,148697],{},"The next thing we need to do is add support to our IDE for Groovy.",[22,148699,148700],{},[653,148701],{"alt":148702,"src":148703},"Adding Groovy Support","./2017-10-04_07-43-08-300x263.png",[22,148705,148706],{},"With that in place, we are going to add a new groovy folder to our main directory. ",[22,148708,148709],{},[653,148710],{"alt":57,"src":148711},"./2017-10-04_07-44-38.png",[26,148713,148715],{"id":148714},"create-a-new-groovy-class","Create a new Groovy class",[22,148717,148718],{},"Now that a lot of our setup is done we get to finally write some code. I created a new package \"com.foo\" under my Groovy src folder and created a simple class called Developer.",[52,148720,148722],{"className":54,"code":148721,"language":56,"meta":57,"style":57},"package com.foo\n\nclass Developer {\n\n String firstName\n String lastName\n String email\n String github\n\n List\u003CString> languages = \\[\\]\n\n Developer(String firstName, String lastName, String email, String github, List\u003CString> languages) {\n this.firstName = firstName\n this.lastName = lastName\n this.email = email\n this.github = github\n this.languages = languages\n }\n}\n",[59,148723,148724,148731,148735,148745,148749,148762,148773,148780,148787,148791,148803,148807,148850,148866,148882,148889,148896,148903,148907],{"__ignoreMap":57},[62,148725,148726,148728],{"class":64,"line":65},[62,148727,69],{"class":68},[62,148729,148730],{"class":72}," com.foo\n",[62,148732,148733],{"class":64,"line":76},[62,148734,79],{"emptyLinePlaceholder":13},[62,148736,148737,148739,148742],{"class":64,"line":82},[62,148738,11671],{"class":23824},[62,148740,148741],{"class":23824}," D",[62,148743,148744],{"class":72},"eveloper {\n",[62,148746,148747],{"class":64,"line":89},[62,148748,79],{"emptyLinePlaceholder":13},[62,148750,148751,148753,148756,148759],{"class":64,"line":95},[62,148752,53871],{"class":23824},[62,148754,148755],{"class":72},"tring first",[62,148757,148758],{"class":23824},"N",[62,148760,148761],{"class":72},"ame\n",[62,148763,148764,148766,148769,148771],{"class":64,"line":101},[62,148765,53871],{"class":23824},[62,148767,148768],{"class":72},"tring last",[62,148770,148758],{"class":23824},[62,148772,148761],{"class":72},[62,148774,148775,148777],{"class":64,"line":107},[62,148776,53871],{"class":23824},[62,148778,148779],{"class":72},"tring email\n",[62,148781,148782,148784],{"class":64,"line":113},[62,148783,53871],{"class":23824},[62,148785,148786],{"class":72},"tring github\n",[62,148788,148789],{"class":64,"line":129},[62,148790,79],{"emptyLinePlaceholder":13},[62,148792,148793,148796,148798,148800],{"class":64,"line":134},[62,148794,148795],{"class":23824}," L",[62,148797,70221],{"class":72},[62,148799,37504],{"class":23824},[62,148801,148802],{"class":72},"tring> languages = \\[\\]\n",[62,148804,148805],{"class":64,"line":156},[62,148806,79],{"emptyLinePlaceholder":13},[62,148808,148809,148811,148814,148816,148818,148820,148823,148825,148827,148829,148831,148833,148836,148838,148841,148843,148845,148847],{"class":64,"line":161},[62,148810,70044],{"class":23824},[62,148812,148813],{"class":72},"eveloper(",[62,148815,37504],{"class":23824},[62,148817,148755],{"class":72},[62,148819,148758],{"class":23824},[62,148821,148822],{"class":72},"ame, ",[62,148824,37504],{"class":23824},[62,148826,148768],{"class":72},[62,148828,148758],{"class":23824},[62,148830,148822],{"class":72},[62,148832,37504],{"class":23824},[62,148834,148835],{"class":72},"tring email, ",[62,148837,37504],{"class":23824},[62,148839,148840],{"class":72},"tring github, ",[62,148842,70218],{"class":23824},[62,148844,70221],{"class":72},[62,148846,37504],{"class":23824},[62,148848,148849],{"class":72},"tring> languages) {\n",[62,148851,148852,148854,148857,148859,148862,148864],{"class":64,"line":167},[62,148853,2405],{"class":23824},[62,148855,148856],{"class":72},".first",[62,148858,148758],{"class":23824},[62,148860,148861],{"class":72},"ame = first",[62,148863,148758],{"class":23824},[62,148865,148761],{"class":72},[62,148867,148868,148870,148873,148875,148878,148880],{"class":64,"line":173},[62,148869,2405],{"class":23824},[62,148871,148872],{"class":72},".last",[62,148874,148758],{"class":23824},[62,148876,148877],{"class":72},"ame = last",[62,148879,148758],{"class":23824},[62,148881,148761],{"class":72},[62,148883,148884,148886],{"class":64,"line":179},[62,148885,2405],{"class":23824},[62,148887,148888],{"class":72},".email = email\n",[62,148890,148891,148893],{"class":64,"line":185},[62,148892,2405],{"class":23824},[62,148894,148895],{"class":72},".github = github\n",[62,148897,148898,148900],{"class":64,"line":191},[62,148899,2405],{"class":23824},[62,148901,148902],{"class":72},".languages = languages\n",[62,148904,148905],{"class":64,"line":209},[62,148906,223],{"class":72},[62,148908,148909],{"class":64,"line":220},[62,148910,379],{"class":72},[22,148912,148913],{},"Next, we will modify our Gradle build. We are going to add the Groovy dependency and a couple plugins. The groovy plugin gives us the ability to compile the Groovy code. ",[52,148915,148917],{"className":54,"code":148916,"language":56,"meta":57,"style":57},"group 'com.therealdanvega'\nversion '1.0-SNAPSHOT'\n\napply plugin: 'java'\napply plugin: 'groovy'\napply plugin: 'idea'\n\nsourceCompatibility = 1.8\n\nrepositories {\n mavenCentral()\n}\n\ndependencies {\n compile group: 'org.codehaus.groovy', name: 'groovy-all', version: '2.4.9'\n testCompile group: 'junit', name: 'junit', version: '4.12'\n}\n",[59,148918,148919,148927,148935,148939,148948,148957,148966,148970,148980,148984,148989,148996,149000,149004,149009,149035,149058],{"__ignoreMap":57},[62,148920,148921,148924],{"class":64,"line":65},[62,148922,148923],{"class":72},"group ",[62,148925,148926],{"class":1675},"'com.therealdanvega'\n",[62,148928,148929,148932],{"class":64,"line":76},[62,148930,148931],{"class":72},"version ",[62,148933,148934],{"class":1675},"'1.0-SNAPSHOT'\n",[62,148936,148937],{"class":64,"line":82},[62,148938,79],{"emptyLinePlaceholder":13},[62,148940,148941,148943,148945],{"class":64,"line":89},[62,148942,146645],{"class":72},[62,148944,1266],{"class":68},[62,148946,148947],{"class":1675}," 'java'\n",[62,148949,148950,148952,148954],{"class":64,"line":95},[62,148951,146645],{"class":72},[62,148953,1266],{"class":68},[62,148955,148956],{"class":1675}," 'groovy'\n",[62,148958,148959,148961,148963],{"class":64,"line":101},[62,148960,146645],{"class":72},[62,148962,1266],{"class":68},[62,148964,148965],{"class":1675}," 'idea'\n",[62,148967,148968],{"class":64,"line":107},[62,148969,79],{"emptyLinePlaceholder":13},[62,148971,148972,148975,148977],{"class":64,"line":113},[62,148973,148974],{"class":72},"sourceCompatibility ",[62,148976,146],{"class":68},[62,148978,148979],{"class":149}," 1.8\n",[62,148981,148982],{"class":64,"line":129},[62,148983,79],{"emptyLinePlaceholder":13},[62,148985,148986],{"class":64,"line":134},[62,148987,148988],{"class":72},"repositories {\n",[62,148990,148991,148994],{"class":64,"line":156},[62,148992,148993],{"class":122}," mavenCentral",[62,148995,2223],{"class":72},[62,148997,148998],{"class":64,"line":161},[62,148999,379],{"class":72},[62,149001,149002],{"class":64,"line":167},[62,149003,79],{"emptyLinePlaceholder":13},[62,149005,149006],{"class":64,"line":173},[62,149007,149008],{"class":72},"dependencies {\n",[62,149010,149011,149014,149016,149019,149022,149024,149027,149030,149032],{"class":64,"line":179},[62,149012,149013],{"class":72}," compile group",[62,149015,1266],{"class":68},[62,149017,149018],{"class":1675}," 'org.codehaus.groovy'",[62,149020,149021],{"class":72},", name",[62,149023,1266],{"class":68},[62,149025,149026],{"class":1675}," 'groovy-all'",[62,149028,149029],{"class":72},", version",[62,149031,1266],{"class":68},[62,149033,149034],{"class":1675}," '2.4.9'\n",[62,149036,149037,149040,149042,149045,149047,149049,149051,149053,149055],{"class":64,"line":185},[62,149038,149039],{"class":72}," testCompile group",[62,149041,1266],{"class":68},[62,149043,149044],{"class":1675}," 'junit'",[62,149046,149021],{"class":72},[62,149048,1266],{"class":68},[62,149050,149044],{"class":1675},[62,149052,149029],{"class":72},[62,149054,1266],{"class":68},[62,149056,149057],{"class":1675}," '4.12'\n",[62,149059,149060],{"class":64,"line":191},[62,149061,379],{"class":72},[22,149063,149064],{},"We should now be able to run the compileGroovy task from Gradle. ",[22,149066,149067],{},[653,149068],{"alt":149069,"src":149070},"Compile Groovy before Java","./2017-10-04_08-13-49.png",[26,149072,149074],{"id":149073},"creating-the-java-application","Creating the Java Application",[22,149076,149077],{},"It's time to focus on the Java side of this project and create our main application. I have created a very simple application that simply creates a new developer. ",[52,149079,149081],{"className":54,"code":149080,"language":56,"meta":57,"style":57},"package com.therealdanvega;\n\nimport com.foo.Developer;\n\nimport java.util.Arrays;\nimport java.util.List;\n\npublic class Application {\n\n public static void main(String\\[\\] args) {\n List\u003CString> languages = Arrays.asList(\"Java\",\"Groovy\");\n Developer developer = new Developer(\"Dan\",\"Vega\",\"danvega@gmail.com\",\"cfaddict\", languages);\n }\n\n}\n",[59,149082,149083,149089,149093,149100,149104,149111,149118,149122,149132,149136,149156,149183,149215,149219,149223],{"__ignoreMap":57},[62,149084,149085,149087],{"class":64,"line":65},[62,149086,69],{"class":68},[62,149088,52481],{"class":72},[62,149090,149091],{"class":64,"line":76},[62,149092,79],{"emptyLinePlaceholder":13},[62,149094,149095,149097],{"class":64,"line":82},[62,149096,27875],{"class":68},[62,149098,149099],{"class":72}," com.foo.Developer;\n",[62,149101,149102],{"class":64,"line":89},[62,149103,79],{"emptyLinePlaceholder":13},[62,149105,149106,149108],{"class":64,"line":95},[62,149107,27875],{"class":68},[62,149109,149110],{"class":72}," java.util.Arrays;\n",[62,149112,149113,149115],{"class":64,"line":101},[62,149114,27875],{"class":68},[62,149116,149117],{"class":72}," java.util.List;\n",[62,149119,149120],{"class":64,"line":107},[62,149121,79],{"emptyLinePlaceholder":13},[62,149123,149124,149126,149128,149130],{"class":64,"line":113},[62,149125,116],{"class":68},[62,149127,119],{"class":68},[62,149129,2088],{"class":122},[62,149131,126],{"class":72},[62,149133,149134],{"class":64,"line":129},[62,149135,79],{"emptyLinePlaceholder":13},[62,149137,149138,149140,149142,149144,149146,149148,149150,149152,149154],{"class":64,"line":134},[62,149139,194],{"class":68},[62,149141,2101],{"class":68},[62,149143,200],{"class":68},[62,149145,2106],{"class":122},[62,149147,2109],{"class":72},[62,149149,973],{"class":889},[62,149151,52569],{"class":72},[62,149153,2117],{"class":889},[62,149155,768],{"class":72},[62,149157,149158,149160,149162,149165,149167,149169,149171,149173,149176,149178,149181],{"class":64,"line":156},[62,149159,25147],{"class":72},[62,149161,973],{"class":68},[62,149163,149164],{"class":72},"> languages ",[62,149166,146],{"class":68},[62,149168,32033],{"class":72},[62,149170,32036],{"class":122},[62,149172,2109],{"class":72},[62,149174,149175],{"class":1675},"\"Java\"",[62,149177,32225],{"class":72},[62,149179,149180],{"class":1675},"\"Groovy\"",[62,149182,1133],{"class":72},[62,149184,149185,149188,149190,149192,149195,149197,149199,149201,149203,149205,149207,149209,149212],{"class":64,"line":161},[62,149186,149187],{"class":72}," Developer developer ",[62,149189,146],{"class":68},[62,149191,466],{"class":68},[62,149193,149194],{"class":122}," Developer",[62,149196,2109],{"class":72},[62,149198,25684],{"class":1675},[62,149200,32225],{"class":72},[62,149202,52643],{"class":1675},[62,149204,32225],{"class":72},[62,149206,52648],{"class":1675},[62,149208,32225],{"class":72},[62,149210,149211],{"class":1675},"\"cfaddict\"",[62,149213,149214],{"class":72},", languages);\n",[62,149216,149217],{"class":64,"line":167},[62,149218,223],{"class":72},[62,149220,149221],{"class":64,"line":173},[62,149222,79],{"emptyLinePlaceholder":13},[62,149224,149225],{"class":64,"line":179},[62,149226,379],{"class":72},[22,149228,149229],{},"If we try and run a Gradle build now it will fail and this is the problem we were facing at work. ",[26,149231,149233],{"id":149232},"compiling-groovy-before-java","Compiling Groovy before Java",[22,149235,149236],{},"The reason this fails is that the default action for our Gradle build is to compile the Java class before it compiles the Groovy class. When this takes our Java class will have no idea what our Developer class is because it hasn't been compiled yet. No worries though, we can change this behavior with a little bit of code in our Gradle build. The Groovy plugin actually extends the Java plugin so that we can compile Groovy along with Java. What we are doing here is just telling Groovy to compile everything for us. When you understand what is going on behind the scenes this makes a lot of sense. ",[52,149238,149240],{"className":54,"code":149239,"language":56,"meta":57,"style":57},"sourceSets {\n main {\n java {\n srcDirs = \\[\\] // don't compile Java code twice\n }\n groovy {\n srcDirs = \\[ 'src/main/groovy', 'src/main/java' \\]\n }\n }\n}\n",[59,149241,149242,149247,149252,149257,149270,149274,149279,149299,149303,149307],{"__ignoreMap":57},[62,149243,149244],{"class":64,"line":65},[62,149245,149246],{"class":72},"sourceSets {\n",[62,149248,149249],{"class":64,"line":76},[62,149250,149251],{"class":72}," main {\n",[62,149253,149254],{"class":64,"line":82},[62,149255,149256],{"class":72}," java {\n",[62,149258,149259,149262,149264,149267],{"class":64,"line":89},[62,149260,149261],{"class":72}," srcDirs ",[62,149263,146],{"class":68},[62,149265,149266],{"class":72}," \\[\\] ",[62,149268,149269],{"class":85},"// don't compile Java code twice\n",[62,149271,149272],{"class":64,"line":95},[62,149273,533],{"class":72},[62,149275,149276],{"class":64,"line":101},[62,149277,149278],{"class":72}," groovy {\n",[62,149280,149281,149283,149285,149288,149291,149293,149296],{"class":64,"line":107},[62,149282,149261],{"class":72},[62,149284,146],{"class":68},[62,149286,149287],{"class":72}," \\[ ",[62,149289,149290],{"class":1675},"'src/main/groovy'",[62,149292,976],{"class":72},[62,149294,149295],{"class":1675},"'src/main/java'",[62,149297,149298],{"class":72}," \\]\n",[62,149300,149301],{"class":64,"line":113},[62,149302,533],{"class":72},[62,149304,149305],{"class":64,"line":129},[62,149306,223],{"class":72},[62,149308,149309],{"class":64,"line":134},[62,149310,379],{"class":72},[22,149312,149313],{},"Now if you were trying to call a Java class from Groovy the normal behavior would work out just fine. ",[26,149315,1499],{"id":1498},[22,149317,149318,149319,149321],{},"This is a pretty common mistake when you try and mix both Java and Groovy. I loving mixing Java & Groovy in projects whenever I get the chance so hopefully, this taught you something and you will do the same in the near future. _",[646,149320,49733],{}," Are you using Java & Groovy in your projects today? _",[1527,149323,149324],{},"html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .shOWo, html code.shiki .shOWo{--shiki-default:#B31D28;--shiki-default-font-style:italic;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic;--shiki-sepia:#B31D28;--shiki-sepia-font-style:italic}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .spoOn, html code.shiki .spoOn{--shiki-default:#E36209;--shiki-dark:#FFAB70;--shiki-sepia:#E36209}html pre.shiki code .s4-Ip, html code.shiki .s4-Ip{--shiki-default:#6A737D;--shiki-dark:#6A737D;--shiki-sepia:#6A737D}",{"title":57,"searchDepth":76,"depth":76,"links":149326},[149327,149328,149329,149330,149331],{"id":148678,"depth":76,"text":148679},{"id":148714,"depth":76,"text":148715},{"id":149073,"depth":76,"text":149074},{"id":149232,"depth":76,"text":149233},{"id":1498,"depth":76,"text":1499},{"_id":149333,"path":149334,"title":149335,"description":149335,"meta":149336,"body":149341},"content/blog/2017/09/22/get-hot-java-9.md","/blog/2017/09/22/get-hot-java-9","Get it while it's hot. Java 9 is out!",{"slug":149337,"date":149338,"published":13,"tags":149339,"author":-1,"cover":149340,"excerpt":-1},"get-hot-java-9","2017-09-22T08:21:00-04:00",[56],"./2017-09-22_07-39-27-760x504.png",{"type":19,"value":149342,"toc":149486},[149343,149352,149354,149358,149361,149367,149371,149374,149378,149381,149385,149388,149392,149395,149399,149402,149406,149409,149413,149416,149420,149449,149458,149462,149469,149474,149476,149479],[22,149344,149345,149346,149351],{},"After a few delays, ",[677,149347,149350],{"href":149348,"rel":149349},"http://www.oracle.com/technetwork/java/javase/downloads/jdk9-downloads-3848520.html",[681],"Java 9 has finally been released",". This is the first major release of Java since March 2014. Java 9 is headlined by Jigsaw which is Java's new modularity system. This is a huge change to how we will write Java programs going forward but there are also a lot of other great features being released in 9. In this article, we will talk briefly about some of the highlights in Java 9.",[26,149353,57257],{"id":61313},[636,149355,149357],{"id":149356},"jigsaw-java-modularity","Jigsaw (Java Modularity) ",[22,149359,149360],{},"Remember when I said that there were a few delays in shipping Java 9? Well, this was mainly the feature that caused it. There was a lot of criticism about this new feature and I still don't think everyone is on the same page. Project Jigsaw aims to design and implement a standard module system for the Java SE Platform and to apply that system to the Platform itself, and to the JDK. Its primary goals are to make implementations of the Platform more easily scalable down to small devices, improve security and maintainability, enable improved application performance, and provide developers with better tools for programming in the large.",[22,149362,149363],{},[653,149364],{"alt":149365,"src":149366},"Java 9: Project Jigsaw","./pexels-photo-218443-1-1024x682.jpeg",[636,149368,149370],{"id":149369},"jshell","JShell",[22,149372,149373],{},"The JShell API and tool will provide a way to interactively evaluate declarations, statements, and expressions of the Java programming language within the JShell state. The JShell state includes an evolving code and execution state. To facilitate rapid investigation and coding, statements and expressions need not occur within a method, and variables and method need not occur within a class. The JShell tool will be a command-line tool with features to ease interaction including a history with editing, tab-completion, automatic addition of needed terminal semicolons, and configurable predefined imports and definitions.",[636,149375,149377],{"id":149376},"http2","Http/2",[22,149379,149380],{},"Java 9 defines a new HTTP client API that implements HTTP/2 and WebSocket. This will allow us to replace the legacy HttpURLConnection API. This is only going to help us improve performance and faster loading pages. ",[636,149382,149384],{"id":149383},"stream-api-enhancements","Stream API Enhancements",[22,149386,149387],{},"The streams API that was released in Java 8 was a really great start. Java 9 fills in some of the holes here and gives us the ability to conditionally take or drop items from the stream. Along with a few other updates, these are great enhancements. ",[636,149389,149391],{"id":149390},"process-api-updates","Process API Updates",[22,149393,149394],{},"Improve the API for controlling and managing operating-system processes.",[636,149396,149398],{"id":149397},"multi-release-jars","Multi-Release Jars",[22,149400,149401],{},"This is especially huge for any third party library creators. Currently, when a new version of Java is released they have to ship a new version of their libraries. With this update, they will be able to ship one JAR that contains multiple versions based on the JVM version being used. ",[636,149403,149405],{"id":149404},"conveniencefactory-methods-for-collections","Convenience Factory methods for Collections",[22,149407,149408],{},"I am always a huge fan API changes that allow me to write less code and this is one of them. Java 9 will Provide static factory methods on the collection interfaces that will create compact, unmodifiable collection instances. The API is deliberately kept minimal.",[636,149410,149412],{"id":149411},"private-interface-methods","Private Interface Methods",[22,149414,149415],{},"You now have the ability to create private methods in your interfaces.",[636,149417,149419],{"id":149418},"other-new-features","Other New Features",[915,149421,149422,149428,149435,149442],{},[37,149423,149424],{},[677,149425,61386],{"href":149426,"rel":149427},"http://openjdk.java.net/jeps/158",[681],[37,149429,149430],{},[677,149431,149434],{"href":149432,"rel":149433},"http://openjdk.java.net/jeps/236",[681],"Parser API for Nashorn",[37,149436,149437],{},[677,149438,149441],{"href":149439,"rel":149440},"http://openjdk.java.net/jeps/224",[681],"Javadoc Improvements",[37,149443,149444],{},[677,149445,149448],{"href":149446,"rel":149447},"http://openjdk.java.net/jeps/248",[681],"Garbage Collector Improvements",[22,149450,149451,149452,149457],{},"These were just some of the highlights of what was released in Java 9. If you're a geek like me (and I'm guessing you are) you can ",[677,149453,149456],{"href":149454,"rel":149455},"http://www.oracle.com/technetwork/java/javase/documentation/9u-relnotes-3704429.html",[681],"dig through the release notes here"," for all the goodies. ",[26,149459,149461],{"id":149460},"download-java-9","Download Java 9",[22,149463,149464,149465,57647],{},"There are so many new features and I will do my best to highlight them here on the blog. Java 9 is now publicly available and can be ",[677,149466,149468],{"href":149348,"rel":149467},[681],"downloaded from here",[22,149470,149471],{},[653,149472],{"alt":149461,"src":149473},"./luis-llerena-14779-1024x683.jpg",[26,149475,1499],{"id":1498},[22,149477,149478],{},"Java 9 is a really exciting release and I am excited to see what others think of it. ",[22,149480,149481],{},[4534,149482,149483,149485],{},[646,149484,49733],{}," What is your favorite new feature in Java 9?",{"title":57,"searchDepth":76,"depth":76,"links":149487},[149488,149499,149500],{"id":61313,"depth":76,"text":57257,"children":149489},[149490,149491,149492,149493,149494,149495,149496,149497,149498],{"id":149356,"depth":82,"text":149357},{"id":149369,"depth":82,"text":149370},{"id":149376,"depth":82,"text":149377},{"id":149383,"depth":82,"text":149384},{"id":149390,"depth":82,"text":149391},{"id":149397,"depth":82,"text":149398},{"id":149404,"depth":82,"text":149405},{"id":149411,"depth":82,"text":149412},{"id":149418,"depth":82,"text":149419},{"id":149460,"depth":76,"text":149461},{"id":1498,"depth":76,"text":1499},{"_id":149502,"path":149503,"title":149504,"description":149504,"meta":149505,"body":149510},"content/blog/2017/09/11/monolithic-vs-microservices.md","/blog/2017/09/11/monolithic-vs-microservices","When to use Microservices over Monolithic Architecture",{"slug":149506,"date":149507,"published":13,"tags":149508,"author":-1,"cover":149509,"excerpt":-1},"monolithic-vs-microservices","2017-09-11T08:30:19-04:00",[31398,11002],"./pexels-photo-325229-760x266.jpeg",{"type":19,"value":149511,"toc":149648},[149512,149521,149524,149527,149532,149535,149541,149544,149548,149551,149563,149569,149574,149577,149592,149595,149599,149608,149611,149633,149636,149638,149641],[22,149513,149514,149515,149520],{},"I am currently working on wrapping up a new section on MicroServices for ",[677,149516,149519],{"href":149517,"rel":149518},"https://www.danvega.dev/jhipster",[681],"my new course on JHipster",". We could easily create a whole course around MicroServices alone so I am trying to keep this to more of an introduction. I think one of the questions I hear most is \"When should I use a MicroServices architecture in place of a Monolithic application?\". In this article, we will review what both of these are as well as give some recommendations on when to use Monolithic and when to use MicrosServices. ",[26,149522,149523],{"id":105487},"Monolithic Architecture",[22,149525,149526],{},"A monolithic application is one that most of us are currently working on or have worked on in the past. Wikipedia defines a Monolithic application as:",[29685,149528,149529],{},[22,149530,149531],{},"In software engineering, a monolithic application describes a single-tiered software application in which the user interface and data access code are combined into a single program from a single platform.",[22,149533,149534],{},"I don't know about you but these are the types of applications I have been building for almost 20 years now. Before we get too far I want you to take a deep breath and I am here to say there is nothing wrong with these types of applications. Please don't run out and start refactoring your monolithic applications because you keep hearing about Microservices flying around at every conference you visit. ",[22,149536,149537],{},[653,149538],{"alt":149539,"src":149540},"Monolithic Application","./road-street-sign-way-1-1024x682.jpg",[22,149542,149543],{},"It is often a good idea to start with a Monolithic Architecture because it is way less complex for teams and less moving parts. ",[26,149545,149547],{"id":149546},"microservices-architecture","Microservices Architecture",[22,149549,149550],{},"Wikipedia defines Microservices as:",[29685,149552,149553],{},[22,149554,149555,60103,149559],{},[62,149556,149558],{"style":149557},"s1","Microservices",[62,149560,149562],{"style":149561},"s2","is a variant of the service-oriented architecture (SOA) architectural style that structures an application as a collection of loosely coupled services. In a microservices architecture, services should be fine-grained and the protocols should be lightweight. The benefit of decomposing an application into different smaller services is that it improves modularity and makes the application easier to understand, develop and test. It also parallelizes development by enabling small autonomous teams to develop, deploy and scale their respective services independently. It also allows the architecture of an individual service to emerge through continuous refactoring. Microservices-based architectures enable continuous delivery and deployment.",[22,149564,149565,149566],{},"What a Microservice boils down to a is something does one thing very well and can live independent of the overall application. In the course, I used the following example. ",[62,149567,149568],{"style":146506},"We run a conference and for years we have been on this monolithic application that manages everything. We have been growing like crazy and it’s time for some big changes.",[22,149570,149571],{},[653,149572],{"alt":149558,"src":149573},"./IMG_5429-1024x768.jpg",[22,149575,149576],{},"When we first started out we built out the functionality to list out speakers, sessions and from that data build an agenda. As we are growing we think about adding the following functionality. ",[915,149578,149579,149582,149584,149587,149590],{},[37,149580,149581],{},"Registration (We did this through 3rd party previously) ",[37,149583,104751],{},[37,149585,149586],{},"Store",[37,149588,149589],{},"Pre-Conference Training",[37,149591,149586],{},[22,149593,149594],{},"As you can see those are not only new functions of our application but they seem like components that do one thing well and can function independently in our larger conference application. This is a great way for our organization and our different teams to develop and deploy independently of each other quickly. ",[26,149596,149598],{"id":149597},"when-to-use-microservices","When to use MicroServices",[22,149600,149601,149602,149607],{},"So now that we know a little bit more about each of these architecture types we need to answer our original question. When should we use Microservices over Monolithic? When I was doing research on this very question I came across ",[677,149603,149606],{"href":149604,"rel":149605},"https://martinfowler.com/bliki/MonolithFirst.html",[681],"an article from Martin Fowler",". If you don't know who Martin is he is probably one of the most brilliant minds in the Software Development world and author of a book that helped me out a lot. ",[22,149609,149610],{},"In this article, Martin says the following: ",[29685,149612,149613,149622,149630],{},[22,149614,149615,149616,149621],{},"As I hear stories about teams using a ",[677,149617,149620],{"href":149618,"rel":149619},"https://martinfowler.com/articles/microservices.html",[681],"microservices architecture",", I've noticed a common pattern.",[34,149623,149624,149627],{},[37,149625,149626],{},"Almost all the successful microservice stories have started with a monolith that got too big and was broken up",[37,149628,149629],{},"Almost all the cases where I've heard of a system that was built as a microservice system from scratch, it has ended up in serious trouble.",[22,149631,149632],{},"This pattern has led many of my colleagues to argue that **you shouldn't start a new project with microservices, even if you're sure your application will be big enough to make it worthwhile. **.",[22,149634,149635],{},"The point of this is that just because Microservices are all the buzz these days it doesn't mean all new applications should start here. In fact as Martin states if you're starting with a Microservice out of the gate you're probably going to run into trouble. ",[26,149637,1499],{"id":1498},[22,149639,149640],{},"We all suffer from shiny object syndrome from time to time. We hear the term MicroServices and we want to jump right in. I hope this article makes you think about when to use Microservices.",[22,149642,149643],{},[4534,149644,149645,149647],{},[646,149646,65584],{},": Are you using MicroServices in your development today? If so what advice would you give to others?",{"title":57,"searchDepth":76,"depth":76,"links":149649},[149650,149651,149652,149653],{"id":105487,"depth":76,"text":149523},{"id":149546,"depth":76,"text":149547},{"id":149597,"depth":76,"text":149598},{"id":1498,"depth":76,"text":1499},{"_id":149655,"path":149656,"title":149657,"description":149657,"meta":149658,"body":149663},"content/blog/2017/09/06/upgrade-new-angular-command-line-interface-cli-1-3-release.md","/blog/2017/09/06/upgrade-new-angular-command-line-interface-cli-1-3-release","How to upgrade to the new Angular Command Line Interface (CLI) 1.3 release",{"slug":149659,"date":149660,"published":13,"tags":149661,"author":-1,"cover":149662,"excerpt":-1},"upgrade-new-angular-command-line-interface-cli-1-3-release","2017-09-06T09:07:30-04:00",[49752],"./pexels-photo-247791-1024x562.png",{"type":19,"value":149664,"toc":149981},[149665,149668,149672,149685,149691,149694,149698,149715,149753,149757,149760,149774,149780,149785,149789,149792,149796,149807,149814,149818,149821,149839,149843,149846,149871,149874,149889,149893,149896,149930,149933,149950,149953,149957,149960,149964,149967,149974,149976,149979],[22,149666,149667],{},"Angular CLI recently released its next major version, 1.3 (Hopper). There are some pretty great features in this release and I can't wait to talk about a few of them here. In this article, we are going to discuss how to check what version of the CLI you are running, how to upgrade and what new features 1.3 is giving us.",[26,149669,149671],{"id":149670},"upgrading-angular-cli","Upgrading Angular CLI",[22,149673,149674,149675,149680,149681,149684],{},"The first thing we need to talk about is installing or upgrading the Angular CLI. For those of you who have never actually installed it yet please ",[677,149676,149679],{"href":149677,"rel":149678},"https://www.danvega.dev/blog/2017/06/05/getting-started-angular-cli",[681],"check out this article I wrote",". If you are already running the Angular CLI you can check what version you are using by running the ",[59,149682,149683],{},"ng -v"," command.",[22,149686,149687],{},[653,149688],{"alt":149689,"src":149690},"Angular CLI version check","./2017-09-06_08-00-18.png",[22,149692,149693],{},"As we can see it has been a minute since I ran an update so this is a perfect time to do so.",[636,149695,149697],{"id":149696},"upgrading-100-beta28-or-earlier","Upgrading 1.0.0-beta.28 or earlier",[22,149699,149700,149701,149704,149705,149708,149709,149711,149712],{},"If you're using Angular CLI ",[59,149702,149703],{},"1.0.0-beta.28"," or less, you need to uninstall ",[59,149706,149707],{},"angular-cli"," package. It should be done due to changing of package's name and scope from ",[59,149710,149707],{}," to ",[59,149713,149714],{},"@angular/cli",[52,149716,149718],{"className":1663,"code":149717,"language":1665,"meta":57,"style":57},"npm uninstall -g angular-cli @angular/cli\nnpm cache clean\nnpm install -g @angular/cli\n",[59,149719,149720,149734,149743],{"__ignoreMap":57},[62,149721,149722,149724,149727,149729,149732],{"class":64,"line":65},[62,149723,32645],{"class":122},[62,149725,149726],{"class":1675}," uninstall",[62,149728,51606],{"class":149},[62,149730,149731],{"class":1675}," angular-cli",[62,149733,51609],{"class":1675},[62,149735,149736,149738,149740],{"class":64,"line":76},[62,149737,32645],{"class":122},[62,149739,111782],{"class":1675},[62,149741,149742],{"class":1675}," clean\n",[62,149744,149745,149747,149749,149751],{"class":64,"line":82},[62,149746,32645],{"class":122},[62,149748,32750],{"class":1675},[62,149750,51606],{"class":149},[62,149752,51609],{"class":1675},[636,149754,149756],{"id":149755},"update-angular-cli","Update Angular CLI",[22,149758,149759],{},"In my case, I was running a newer version so we can just run a simple command to update.",[52,149761,149762],{"className":1663,"code":51595,"language":1665,"meta":57,"style":57},[59,149763,149764],{"__ignoreMap":57},[62,149765,149766,149768,149770,149772],{"class":64,"line":65},[62,149767,32645],{"class":122},[62,149769,32750],{"class":1675},[62,149771,51606],{"class":149},[62,149773,51609],{"class":1675},[22,149775,149776,149777,149779],{},"Now if I run ",[59,149778,149683],{}," you can see that I have updated to the latest stable version which is 1.3.2",[22,149781,149782],{},[653,149783],{"alt":51538,"src":149784},"./2017-09-06_08-20-29.png",[26,149786,149788],{"id":149787},"whats-new-in-angular-cli-13","What's New in Angular CLI 1.3",[22,149790,149791],{},"Angular CLI now officially supports ES2017 and TypeScript 2.4 paving the way for Angular 5 which should be released very soon!",[636,149793,149795],{"id":149794},"universal-support-server-side-rendering","Universal Support (Server Side Rendering)",[22,149797,149798,149799,149802,149803,149806],{},"Angular Universal Support allows you to run your Angular code on the server side as opposed to in the browser. Angular CLI supports a generation of a Universal build for your application. This is a CommonJS-formatted bundle which can be ",[59,149800,149801],{},"require()"," into a Node application (for example, an Express server) and used with ",[59,149804,149805],{},"@angular/platform-server"," 's APIs to pre-render your application.",[22,149808,149809],{},[677,149810,149813],{"href":149811,"rel":149812},"https://github.com/angular/angular-cli/wiki/stories-universal-rendering",[681],"Follow this tutorial to check it out.",[636,149815,149817],{"id":149816},"build-optimizer","Build Optimizer",[22,149819,149820],{},"Angular CLI 1.3 upgrades us to Webpack 3 which brings us some new features a bug fixes. We also get a new optimizer that is meant to slim down our applications in production.",[52,149822,149824],{"className":1663,"code":149823,"language":1665,"meta":57,"style":57},"ng build --prod --build-optimizer\n",[59,149825,149826],{"__ignoreMap":57},[62,149827,149828,149830,149833,149836],{"class":64,"line":65},[62,149829,49784],{"class":122},[62,149831,149832],{"class":1675}," build",[62,149834,149835],{"class":149}," --prod",[62,149837,149838],{"class":149}," --build-optimizer\n",[636,149840,149842],{"id":149841},"named-chunks","Named Chunks",[22,149844,149845],{},"As long as we are talking about builds we should mention a new flag --named-chunks which will generate named chunks. Prior to Angular CLI 1.3 all of the lazily loaded chunks were named:",[52,149847,149849],{"className":1663,"code":149848,"language":1665,"meta":57,"style":57},"0.js\n1.js\n2.js\netc...\n",[59,149850,149851,149856,149861,149866],{"__ignoreMap":57},[62,149852,149853],{"class":64,"line":65},[62,149854,149855],{"class":122},"0.js\n",[62,149857,149858],{"class":64,"line":76},[62,149859,149860],{"class":122},"1.js\n",[62,149862,149863],{"class":64,"line":82},[62,149864,149865],{"class":122},"2.js\n",[62,149867,149868],{"class":64,"line":89},[62,149869,149870],{"class":122},"etc...\n",[22,149872,149873],{},"This setting can be enabled disabled with the namedChunks option in angular-cli.json and defaults to true. With that option in place, we will get some nicely named chunks.",[52,149875,149877],{"className":1663,"code":149876,"language":1665,"meta":57,"style":57},"products.module.chunk.js\norders.module.chunk.js\n",[59,149878,149879,149884],{"__ignoreMap":57},[62,149880,149881],{"class":64,"line":65},[62,149882,149883],{"class":122},"products.module.chunk.js\n",[62,149885,149886],{"class":64,"line":76},[62,149887,149888],{"class":122},"orders.module.chunk.js\n",[636,149890,149892],{"id":149891},"proxy-configuration","Proxy Configuration",[22,149894,149895],{},"It's been possible to define a proxy configuration for awhile now. This allows to match a pattern and forward all of those requests to our server side application. I do this a lot in my Spring Boot applications with something like this.",[52,149897,149899],{"className":32791,"code":149898,"language":32793,"meta":57,"style":57},"{\n \"/api\": {\n \"target\": \"http://localhost:8080\"\n }\n}\n",[59,149900,149901,149905,149912,149922,149926],{"__ignoreMap":57},[62,149902,149903],{"class":64,"line":65},[62,149904,3680],{"class":72},[62,149906,149907,149910],{"class":64,"line":76},[62,149908,149909],{"class":1675}," \"/api\"",[62,149911,3688],{"class":72},[62,149913,149914,149917,149919],{"class":64,"line":82},[62,149915,149916],{"class":1675}," \"target\"",[62,149918,3696],{"class":72},[62,149920,149921],{"class":1675},"\"http://localhost:8080\"\n",[62,149923,149924],{"class":64,"line":89},[62,149925,3731],{"class":72},[62,149927,149928],{"class":64,"line":95},[62,149929,379],{"class":72},[22,149931,149932],{},"The problem was you had to modify the ng serve command so that it took in your proxy config",[52,149934,149936],{"className":1663,"code":149935,"language":1665,"meta":57,"style":57},"ng serve --proxy-config proxy.conf.json\n",[59,149937,149938],{"__ignoreMap":57},[62,149939,149940,149942,149944,149947],{"class":64,"line":65},[62,149941,49784],{"class":122},[62,149943,112281],{"class":1675},[62,149945,149946],{"class":149}," --proxy-config",[62,149948,149949],{"class":1675}," proxy.conf.json\n",[22,149951,149952],{},"Now we can simply configure a proxyConfig in our angular-cli.json and the serve command will pick it up automatically.",[636,149954,149956],{"id":149955},"bug-fixes","Bug Fixes",[22,149958,149959],{},"As always a considerable amount of bug fixes!",[26,149961,149963],{"id":149962},"angular-cli-13-release-notes","Angular CLI 1.3 Release Notes",[22,149965,149966],{},"This is release 1.3.0 of the Angular CLI. As a minor release, it adds a lot of new features (as well as a considerable amount of bug fixes) from the 1.2 releases.",[22,149968,149969,2755],{},[677,149970,149973],{"href":149971,"rel":149972},"https://github.com/angular/angular-cli/releases/tag/v1.3.0",[681],"Read the full release notes here",[26,149975,1499],{"id":1498},[22,149977,149978],{},"The Angular team is constantly improving the CLI and it's exciting to see some of the new features and bug fixes.",[1527,149980,132749],{},{"title":57,"searchDepth":76,"depth":76,"links":149982},[149983,149987,149994,149995],{"id":149670,"depth":76,"text":149671,"children":149984},[149985,149986],{"id":149696,"depth":82,"text":149697},{"id":149755,"depth":82,"text":149756},{"id":149787,"depth":76,"text":149788,"children":149988},[149989,149990,149991,149992,149993],{"id":149794,"depth":82,"text":149795},{"id":149816,"depth":82,"text":149817},{"id":149841,"depth":82,"text":149842},{"id":149891,"depth":82,"text":149892},{"id":149955,"depth":82,"text":149956},{"id":149962,"depth":76,"text":149963},{"id":1498,"depth":76,"text":1499},{"_id":149997,"path":149998,"title":149999,"description":149999,"meta":150000,"body":150005},"content/blog/2017/08/30/groovy-happens-assign-biginteger-integer.md","/blog/2017/08/30/groovy-happens-assign-biginteger-integer","Groovy: What happens when you assign a BigInteger to an Integer",{"slug":150001,"date":150002,"published":13,"tags":150003,"author":-1,"cover":150004,"excerpt":-1},"groovy-happens-assign-biginteger-integer","2017-08-30T07:39:40-04:00",[53536,56],"./codes-coding.jpg",{"type":19,"value":150006,"toc":150149},[150007,150016,150021,150024,150056,150060,150066,150081,150084,150095,150098,150109,150112,150141,150143,150146],[22,150008,150009,150010,150015],{},"This question came in from a student in my ",[677,150011,150014],{"href":150012,"rel":150013},"https://www.danvega.dev/groovy",[681],"Apache Groovy Development Course",". It was actually quite interesting because it tripped me up at first before I understand what was going on behind the scenes. ",[22,150017,150018],{},[653,150019],{"alt":53553,"src":150020},"./pexels-photo-169573-1024x683.jpeg",[22,150022,150023],{},"The student was asking why when they created an integer but assigned to large of a number to it did the results end up like this.",[52,150025,150027],{"className":54,"code":150026,"language":56,"meta":57,"style":57},"int i = 2356524235623432414235234234\nprintln i.class // java.lang.Integer\nprintln i // 1413517242\n",[59,150028,150029,150040,150048],{"__ignoreMap":57},[62,150030,150031,150033,150035,150037],{"class":64,"line":65},[62,150032,747],{"class":68},[62,150034,750],{"class":72},[62,150036,146],{"class":68},[62,150038,150039],{"class":149}," 2356524235623432414235234234\n",[62,150041,150042,150045],{"class":64,"line":76},[62,150043,150044],{"class":72},"println i.class ",[62,150046,150047],{"class":85},"// java.lang.Integer\n",[62,150049,150050,150053],{"class":64,"line":82},[62,150051,150052],{"class":72},"println i ",[62,150054,150055],{"class":85},"// 1413517242\n",[26,150057,150059],{"id":150058},"groovy-integer","Groovy Integer",[22,150061,150062,150063,150065],{},"Looking at the code example above can you see what is going on and why? Let's take a minute and break this down. First, we are setting a very large number (BigInteger) to a data type integer. If you look at the Integer class there is a static MAX_VALUE that is set to \"A constant holding the maximum value an ",[59,150064,747],{}," can have, 231-1.\"",[52,150067,150069],{"className":54,"code":150068,"language":56,"meta":57,"style":57},"int i = 2356524235623432414235234234\n",[59,150070,150071],{"__ignoreMap":57},[62,150072,150073,150075,150077,150079],{"class":64,"line":65},[62,150074,747],{"class":68},[62,150076,750],{"class":72},[62,150078,146],{"class":68},[62,150080,150039],{"class":149},[22,150082,150083],{},"So we are trying to set a value that is outside of that max value range to an integer. When we print out the class though it's still an integer",[52,150085,150087],{"className":54,"code":150086,"language":56,"meta":57,"style":57},"println i.class // java.lang.Integer\n",[59,150088,150089],{"__ignoreMap":57},[62,150090,150091,150093],{"class":64,"line":65},[62,150092,150044],{"class":72},[62,150094,150047],{"class":85},[22,150096,150097],{},"When we print out the value though it appears it is some random number.",[52,150099,150101],{"className":54,"code":150100,"language":56,"meta":57,"style":57},"println i // 1413517242\n",[59,150102,150103],{"__ignoreMap":57},[62,150104,150105,150107],{"class":64,"line":65},[62,150106,150052],{"class":72},[62,150108,150055],{"class":85},[22,150110,150111],{},"You might expect this to just error out but that isn't the case. What happens underneath the hood is that the number on the right is actually a BigInteger and when you try to assign it to an integer something happens. We end up calling the BigInteger's class method int value which will convert that number to an integer. ",[52,150113,150115],{"className":54,"code":150114,"language":56,"meta":57,"style":57},"BigDecimal bd = 2356524235623432414235234234;\nprintln bd.intValue() // 1413517242\n",[59,150116,150117,150129],{"__ignoreMap":57},[62,150118,150119,150122,150124,150127],{"class":64,"line":65},[62,150120,150121],{"class":72},"BigDecimal bd ",[62,150123,146],{"class":68},[62,150125,150126],{"class":149}," 2356524235623432414235234234",[62,150128,153],{"class":72},[62,150130,150131,150134,150137,150139],{"class":64,"line":76},[62,150132,150133],{"class":72},"println bd.",[62,150135,150136],{"class":122},"intValue",[62,150138,5398],{"class":72},[62,150140,150055],{"class":85},[26,150142,1499],{"id":1498},[22,150144,150145],{},"When you understand what is happening under the hood everything seems to make a lot more sense.",[1527,150147,150148],{},"html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}html pre.shiki code .s4-Ip, html code.shiki .s4-Ip{--shiki-default:#6A737D;--shiki-dark:#6A737D;--shiki-sepia:#6A737D}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}",{"title":57,"searchDepth":76,"depth":76,"links":150150},[150151,150152],{"id":150058,"depth":76,"text":150059},{"id":1498,"depth":76,"text":1499},{"_id":150154,"path":150155,"title":150156,"description":150156,"meta":150157,"body":150161},"content/blog/2017/08/28/using-jhipster-development-mode.md","/blog/2017/08/28/using-jhipster-development-mode","Using JHipster in Development mode",{"slug":150158,"date":150159,"published":13,"tags":150160,"author":-1,"cover":54584,"excerpt":-1},"using-jhipster-development-mode","2017-08-28T09:30:30-04:00",[49752,11002],{"type":19,"value":150162,"toc":150279},[150163,150171,150175,150178,150183,150187,150190,150195,150198,150203,150211,150219,150222,150259,150263,150269,150271,150274],[22,150164,150165,150166,150170],{},"I have been working really hard lately to wrap up ",[677,150167,150169],{"href":149517,"rel":150168},[681],"my latest course on JHipster",". In one of the exercises, I have the students create their own application. In this application, one of the requirements is to make UI changes. When working with Angular you have probably come to expect to see those changes automatically reflected in the browser. In this tutorial, we will look at how to work with JHipster in Development. ",[26,150172,150174],{"id":150173},"jhipster-in-development","JHipster in Development",[22,150176,150177],{},"I was working on this course and I quickly realized that unless you were familiar with this stack it might be a little confusing to know what to do in development. It is important to remember that we are really working with 2 different applications, Angular & Spring Boot, that work together. When you first start working with JHipster your first thought is to simply run the Spring boot app.",[22,150179,150180],{},[653,150181],{"alt":150174,"src":150182},"./2017-08-28_08-49-35-1024x776.png",[636,150184,150186],{"id":150185},"angular-development","Angular Development",[22,150188,150189],{},"At this point, you might try to go into your webapp (Angular) directory, locate the home component and begin making changes to the application. ",[22,150191,150192],{},[653,150193],{"alt":150174,"src":150194},"./2017-08-28_08-53-38.png",[22,150196,150197],{},"The problem with this is that any changes you make won't be seen in the browser, even if you reload the page. JHipster gives us a way to work efficiently in development mode. If you have worked on Angular applications before you might have spun up a server using \"npm start\" that will watch for any changes and automatically refresh the browser window. JHipster gives us something similar by running the \"yarn start\" command and you can run it right in your IDE. ",[22,150199,150200],{},[653,150201],{"alt":150174,"src":150202},"./2017-08-28_08-57-46-1024x280.png",[22,150204,150205,150210],{},[677,150206,150209],{"href":150207,"rel":150208},"http://www.jhipster.tech/development/#working-with-angular",[681],"From the JHIpster documentation"," running this command provides us some very impressive features. ",[915,150212,150213,150216],{},[37,150214,150215],{},"As soon as you modify one of your HTML/CSS/TypeScript files, your browser will refresh itself automatically",[37,150217,150218],{},"As your testing your application on several different browsers or devices, all your clicks/scrolls/inputs should be automatically synchronized on all screens",[22,150220,150221],{},"This will launch:",[915,150223,150224,150227,150240,150252],{},[37,150225,150226],{},"A Webpack task that will automatically compile TypeScript code into JavaScript",[37,150228,150229,150230,150234,150235,150239],{},"A Webpack “hot module reload” server that will run on ",[677,150231,150232],{"href":150232,"rel":150233},"http://localhost:9060/",[681]," (and has a proxy to ",[677,150236,150237],{"href":150237,"rel":150238},"http://127.0.0.1:8080/api",[681]," to access the Java back-end)",[37,150241,150242,150243,150247,150248,150251],{},"A BrowserSync task that will run on ",[677,150244,150245],{"href":150245,"rel":150246},"http://localhost:9000/",[681],", which has a proxy to ",[677,150249,150232],{"href":150232,"rel":150250},[681]," (the Webpack “hot module reload” server), and which will synchronize the user’s clicks/scrolls/inputs",[37,150253,150254,150255],{},"The BrowserSync UI, which will be available on ",[677,150256,150257],{"href":150257,"rel":150258},"http://localhost:3001/",[681],[26,150260,150262],{"id":150261},"jhipster-in-development-mode-screencast","JHipster in Development Mode Screencast ",[22,150264,150265,67945],{},[677,150266,150267],{"href":150267,"rel":150268},"https://www.youtube.com/watch?v=SVUB3Yhv3sQ&t=114s",[681],[26,150270,1499],{"id":1498},[22,150272,150273],{},"I hope this short tutorial shed some light on how to work with JHipster in development. ",[22,150275,50754,150276,150278],{},[646,150277,49733],{}," What problems are you facing in your JHipster projects? _",{"title":57,"searchDepth":76,"depth":76,"links":150280},[150281,150284,150285],{"id":150173,"depth":76,"text":150174,"children":150282},[150283],{"id":150185,"depth":82,"text":150186},{"id":150261,"depth":76,"text":150262},{"id":1498,"depth":76,"text":1499},{"_id":150287,"path":150288,"title":150289,"description":150289,"meta":150290,"body":150295},"content/blog/2017/08/16/multiple-dependencies-spring-cli.md","/blog/2017/08/16/multiple-dependencies-spring-cli","How to add multiple dependencies with the Spring CLI",{"slug":150291,"date":150292,"published":13,"tags":150293,"author":-1,"cover":150294,"excerpt":-1},"multiple-dependencies-spring-cli","2017-08-16T08:30:40-04:00",[11002],"./pexels-photo-205421-760x506.jpeg",{"type":19,"value":150296,"toc":150582},[150297,150305,150313,150316,150320,150332,150336,150339,150355,150362,150367,150370,150406,150410,150413,150419,150426,150492,150495,150509,150515,150522,150566,150569,150571,150574,150579],[22,150298,150299,150300,150304],{},"In this tutorial, we are taking a look at a student's question from my ",[677,150301,56768],{"href":150302,"rel":150303},"https://www.danvega.dev/spring-boot",[681],". This question has to do with the Spring CLI which is a great tool that allows you to quickly prototype with Spring. ",[29685,150306,150307,150310],{},[22,150308,150309],{},"Hi Dan,",[22,150311,150312],{},"I have created the spring boot application through spring CLI in the same way you showed in the video but my question is, How can we add multiple dependencies in our application through spring CLI? You have shown it for only one dependency i.e \"web\" using -d option but what if we want to add more dependencies through CLI?",[22,150314,150315],{},"In this article, I am going to talk through what the Spring CLI, how to install it and use it and of course answer the question above. ",[26,150317,150319],{"id":150318},"spring-cli","Spring CLI",[22,150321,150322,150323,150326,150327,150331],{},"If you weren't already aware there is a really cool tool called the Spring CLI (Command Line Interface). The Spring Boot CLI is a command line tool that can be used if you want to quickly prototype with Spring. It allows you to run ",[677,150324,53590],{"href":53545,"rel":150325},[681]," scripts, which means that you have a familiar Java-like syntax, without so much boilerplate code. Did I mention I also ",[677,150328,150330],{"href":150012,"rel":150329},[681],"teach a course on Groovy"," and absolutely love the language? You don’t need to use the CLI to work with Spring Boot but it’s definitely the quickest way to get a Spring application off the ground.",[26,150333,150335],{"id":150334},"installing-the-spring-cli","Installing the Spring CLI",[22,150337,150338],{},"There are a couple of ways to install the Spring CLI and we will go through them here. First, you can install them manually by downloading the Spring CLI distribution from the Spring software repository:",[915,150340,150341,150348],{},[37,150342,150343],{},[677,150344,150347],{"href":150345,"rel":150346},"http://repo.spring.io/release/org/springframework/boot/spring-boot-cli/1.5.6.RELEASE/spring-boot-cli-1.5.6.RELEASE-bin.zip",[681],"spring-boot-cli-1.5.6.RELEASE-bin.zip",[37,150349,150350],{},[677,150351,150354],{"href":150352,"rel":150353},"http://repo.spring.io/release/org/springframework/boot/spring-boot-cli/1.5.6.RELEASE/spring-boot-cli-1.5.6.RELEASE-bin.tar.gz",[681],"spring-boot-cli-1.5.6.RELEASE-bin.tar.gz",[22,150356,150357,150358,57647],{},"Another way to install the Spring CLI is by using SDKMan. If you have been following me at all you know that I am a huge fan of ",[677,150359,61356],{"href":150360,"rel":150361},"http://sdkman.io/",[681],[22,150363,150364],{},[653,150365],{"alt":61356,"src":150366},"./2017-08-15_20-11-24.png",[22,150368,150369],{},"When you have SDKMan installed you can install the Spring CLI by running the following command. ",[52,150371,150373],{"className":1663,"code":150372,"language":1665,"meta":57,"style":57},"$ sdk install springboot\n$ spring --version\nSpring Boot v1.5.6.RELEASE\n",[59,150374,150375,150387,150396],{"__ignoreMap":57},[62,150376,150377,150379,150382,150384],{"class":64,"line":65},[62,150378,19949],{"class":122},[62,150380,150381],{"class":1675}," sdk",[62,150383,32750],{"class":1675},[62,150385,150386],{"class":1675}," springboot\n",[62,150388,150389,150391,150394],{"class":64,"line":76},[62,150390,19949],{"class":122},[62,150392,150393],{"class":1675}," spring",[62,150395,111324],{"class":149},[62,150397,150398,150400,150403],{"class":64,"line":82},[62,150399,16],{"class":122},[62,150401,150402],{"class":1675}," Boot",[62,150404,150405],{"class":1675}," v1.5.6.RELEASE\n",[26,150407,150409],{"id":150408},"using-the-spring-cli","Using the Spring CLI",[22,150411,150412],{},"Now that we have the Spring CLI installed its time to use it. If you aren't sure what options are available you can run the help command to get more information. ",[22,150414,150415],{},[653,150416],{"alt":150417,"src":150418},"Spring CLI Help","./2017-08-16_08-08-04.png",[22,150420,150421,150422,150425],{},"In this first example, we will create and run a simple example. First, create a file called ",[4534,150423,150424],{},"app.groovy"," that looks like this. ",[52,150427,150429],{"className":54,"code":150428,"language":56,"meta":57,"style":57},"@RestController\nclass AppController {\n\n @RequestMapping(\"/\")\n public String home() {\n return \"Hello, World!\";\n }\n\n}\n",[59,150430,150431,150437,150446,150450,150462,150472,150480,150484,150488],{"__ignoreMap":57},[62,150432,150433,150435],{"class":64,"line":65},[62,150434,942],{"class":72},[62,150436,2342],{"class":68},[62,150438,150439,150441,150444],{"class":64,"line":76},[62,150440,11671],{"class":68},[62,150442,150443],{"class":122}," AppController",[62,150445,126],{"class":72},[62,150447,150448],{"class":64,"line":82},[62,150449,79],{"emptyLinePlaceholder":13},[62,150451,150452,150454,150456,150458,150460],{"class":64,"line":89},[62,150453,2143],{"class":72},[62,150455,10592],{"class":68},[62,150457,2109],{"class":72},[62,150459,15635],{"class":1675},[62,150461,2212],{"class":72},[62,150463,150464,150466,150468,150470],{"class":64,"line":95},[62,150465,194],{"class":68},[62,150467,2469],{"class":72},[62,150469,18647],{"class":122},[62,150471,206],{"class":72},[62,150473,150474,150476,150478],{"class":64,"line":101},[62,150475,360],{"class":68},[62,150477,80556],{"class":1675},[62,150479,153],{"class":72},[62,150481,150482],{"class":64,"line":107},[62,150483,223],{"class":72},[62,150485,150486],{"class":64,"line":113},[62,150487,79],{"emptyLinePlaceholder":13},[62,150489,150490],{"class":64,"line":129},[62,150491,379],{"class":72},[22,150493,150494],{},"Now you can run this application from the command line using the following command. ",[52,150496,150498],{"className":1663,"code":150497,"language":1665,"meta":57,"style":57},"spring run app.groovy\n",[59,150499,150500],{"__ignoreMap":57},[62,150501,150502,150504,150506],{"class":64,"line":65},[62,150503,11002],{"class":122},[62,150505,1716],{"class":1675},[62,150507,150508],{"class":1675}," app.groovy\n",[22,150510,150511],{},[653,150512],{"alt":150513,"src":150514},"Spring CLI run app","./2017-08-16_08-13-27-1024x547.png",[22,150516,150517,150518,150521],{},"You can also use the Spring CLI to initialize a new project using ",[677,150519,40207],{"href":45295,"rel":150520},[681],". This is similar to creating a new project in IntelliJ using the fancy wizard. So to answer our initial question which was how can we initialize a new project from the Spring CLI and add multiple dependencies: ",[52,150523,150525],{"className":1663,"code":150524,"language":1665,"meta":57,"style":57},"$ spring init --dependencies=web,data-jpa my-project\nUsing service at https://start.spring.io\nProject extracted to '/Users/developer/example/my-project'\n",[59,150526,150527,150541,150554],{"__ignoreMap":57},[62,150528,150529,150531,150533,150535,150538],{"class":64,"line":65},[62,150530,19949],{"class":122},[62,150532,150393],{"class":1675},[62,150534,3282],{"class":1675},[62,150536,150537],{"class":149}," --dependencies=web,data-jpa",[62,150539,150540],{"class":1675}," my-project\n",[62,150542,150543,150546,150549,150551],{"class":64,"line":76},[62,150544,150545],{"class":122},"Using",[62,150547,150548],{"class":1675}," service",[62,150550,125157],{"class":1675},[62,150552,150553],{"class":1675}," https://start.spring.io\n",[62,150555,150556,150558,150561,150563],{"class":64,"line":82},[62,150557,2913],{"class":122},[62,150559,150560],{"class":1675}," extracted",[62,150562,57317],{"class":1675},[62,150564,150565],{"class":1675}," '/Users/developer/example/my-project'\n",[22,150567,150568],{},"All you need to do is separate them using a comma and you can add as many dependencies as you like. ",[26,150570,1499],{"id":1498},[22,150572,150573],{},"I think the Spring CLI is a great little tool for getting started. I hope you found this tutorial useful and I hope you check out the Spring CLI if you haven't already looked at it. ",[22,150575,50754,150576,150578],{},[646,150577,49733],{}," What other tools do you find are helpful for developers new to Spring Boot? _",[1527,150580,150581],{},"html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}",{"title":57,"searchDepth":76,"depth":76,"links":150583},[150584,150585,150586,150587],{"id":150318,"depth":76,"text":150319},{"id":150334,"depth":76,"text":150335},{"id":150408,"depth":76,"text":150409},{"id":1498,"depth":76,"text":1499},{"_id":150589,"path":150590,"title":150591,"description":150591,"meta":150592,"body":150597},"content/blog/2017/08/04/3-youtube-channels-java-developers.md","/blog/2017/08/04/3-youtube-channels-java-developers","3 YouTube Channels for Java Developers",{"slug":150593,"date":150594,"published":13,"tags":150595,"author":-1,"cover":150596,"excerpt":-1},"3-youtube-channels-java-developers","2017-08-04T13:14:00-04:00",[56,11002],"./pexels-photo-760x506.jpg",{"type":19,"value":150598,"toc":150669},[150599,150602,150606,150608,150611,150617,150623,150627,150630,150636,150642,150646,150649,150655,150661,150663],[22,150600,150601],{},"I think if I was given the choice to only keep a single website around I would have to go with YouTube. I love it for both entertainment and education and I love creating content on my channel. I am going to share with you 3 YouTube channels I think you should subscribe to. The reason I chose these 3 is that they have all put out some really great content lately. ",[26,150603,150605],{"id":150604},"youtube-channels-for-java-developers","YouTube Channels for Java Developers",[636,150607,15],{"id":56},[22,150609,150610],{},"I don't think I could start this list off without including Java. This is a great channel run by Oracle and in the past few days, the released nearly 20 videos. ",[22,150612,150613],{},[677,150614,150615],{"href":150615,"rel":150616},"https://www.youtube.com/user/java",[681],[22,150618,150619],{},[653,150620],{"alt":150621,"src":150622},"Java YouTube Channel","./2017-08-04_13-14-20-1024x169.png",[636,150624,150626],{"id":150625},"spring-io","Spring I/O",[22,150628,150629],{},"The Spring/IO channel is a collection of presentations from Spring I/O conferences. They recently added a bunch of presentations from their conference in Barcelona. ",[22,150631,150632,67835],{},[677,150633,150634],{"href":150634,"rel":150635},"https://www.youtube.com/channel/UCLMPXsvSrhNPN3i9h-u8PYg",[681],[22,150637,150638],{},[653,150639],{"alt":150640,"src":150641},"Spring I/O YouTube Channel","./2017-08-04_13-17-14-1024x170.png",[636,150643,150645],{"id":150644},"devoxx","Devoxx",[22,150647,150648],{},"The Devoxx YouTube channel is a collection of presentations from their conferences. They recently added a bunch of presentations from their conference in Poland. ",[22,150650,150651],{},[677,150652,150653],{"href":150653,"rel":150654},"https://www.youtube.com/channel/UCCBVCTuk6uJrN3iFV_3vurg",[681],[22,150656,150657],{},[653,150658],{"alt":150659,"src":150660},"Devoxx YouTube Channel","./2017-08-04_13-17-37-1024x170.png",[26,150662,1499],{"id":1498},[22,150664,150665,150666,150668],{},"I hope you liked these channels and you should think about subscribing to them. Thank you to 3 channels we discussed for putting out some amazing content! _",[646,150667,49733],{}," If you could only keep one website around what would it be? _",{"title":57,"searchDepth":76,"depth":76,"links":150670},[150671,150676],{"id":150604,"depth":76,"text":150605,"children":150672},[150673,150674,150675],{"id":56,"depth":82,"text":15},{"id":150625,"depth":82,"text":150626},{"id":150644,"depth":82,"text":150645},{"id":1498,"depth":76,"text":1499},{"_id":150678,"path":150679,"title":150680,"description":150680,"meta":150681,"body":150686},"content/blog/2017/08/02/kill-java-process-macos-sierra-using-activity-monitor.md","/blog/2017/08/02/kill-java-process-macos-sierra-using-activity-monitor","How to kill Java process on mac OS Sierra using Activity Monitor",{"slug":150682,"date":150683,"published":13,"tags":150684,"author":-1,"cover":150685,"excerpt":-1},"kill-java-process-macos-sierra-using-activity-monitor","2017-08-02T08:13:38-04:00",[56,11002],"./java.jpg",{"type":19,"value":150687,"toc":150758},[150688,150696,150700,150703,150713,150719,150722,150727,150731,150734,150740,150744,150747,150749,150755],[22,150689,150690,150691,150695],{},"Last week I ",[677,150692,131875],{"href":150693,"rel":150694},"https://www.danvega.dev/blog/2017/07/24/spring-boot-application-failed-start",[681]," on the improvements in Spring Boot 1.4 of startup errors. Specifically, we saw that we got a really informative error message when we try and run an application and port 8080 is already in use. I received a question from a reader that went like this \"I get the port 8080 is already in use error from time to time and I am not sure how to fix it, what can I do?\" This is actually pretty easy to fix and happens to all of us. In this short article, I will show you how to do it on macOS and link to another article I wrote on how to do this on Windows. ",[26,150697,150699],{"id":150698},"port-8080-is-already-in-use","Port 8080 is already in use",[22,150701,150702],{},"This problem usually comes up because another process wasn't properly terminated. If you want to simulate this issue you can fire up one app in a terminal and then try launching another in your favorite IDE (IntelliJ right?). I opened up a terminal and ran my other project using the Maven plugin.",[52,150704,150705],{"className":1663,"code":2297,"language":1665,"meta":57,"style":57},[59,150706,150707],{"__ignoreMap":57},[62,150708,150709,150711],{"class":64,"line":65},[62,150710,2304],{"class":122},[62,150712,2307],{"class":1675},[22,150714,150715],{},[653,150716],{"alt":150717,"src":150718},"Spring Boot Maven Plugin","./2017-08-02_08-11-41-1024x532.png",[22,150720,150721],{},"Now if we head into IntelliJ and try to run our application we are going to see an error that looks something like this. ",[22,150723,150724],{},[653,150725],{"alt":150699,"src":150726},"./2017-08-02_08-17-08-1024x450.png",[26,150728,150730],{"id":150729},"activity-monitor-ftw","Activity Monitor FTW",[22,150732,150733],{},"So we know that another process is running but it isn't always as simple as forgetting a terminal window is open and running another app. This can happen when an IDE is closed and the process is correctly terminated. macOS gives us a nice tool for monitoring process by name and port called Activity Monitor. If you open up Activity Monitor (I just use Spotlight) you could manually look for the processes or use the search bar in the upper right-hand corner. Now you can kill the existing processes and your unwanted process will be gone.",[22,150735,150736],{},[653,150737],{"alt":150738,"src":150739},"Activity Monitor","./2017-08-02_08-18-11.png",[636,150741,150743],{"id":150742},"what-about-windows","What about Windows? ",[22,150745,150746],{},"If you're a windows user I wrote an article awhile back on how to kill a process by port number. ",[26,150748,53087],{"id":116944},[22,150750,150751],{},[677,150752,150753],{"href":150753,"rel":150754},"https://www.youtube.com/watch?v=Qn3vhcDQmpI",[681],[1527,150756,150757],{},"html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}",{"title":57,"searchDepth":76,"depth":76,"links":150759},[150760,150761,150764],{"id":150698,"depth":76,"text":150699},{"id":150729,"depth":76,"text":150730,"children":150762},[150763],{"id":150742,"depth":82,"text":150743},{"id":116944,"depth":76,"text":53087},{"_id":150766,"path":150767,"title":150768,"description":150768,"meta":150769,"body":150773},"content/blog/2017/07/31/spring-data-aggregate-functions-repository.md","/blog/2017/07/31/spring-data-aggregate-functions-repository","Spring Data Aggregate Functions in a repository",{"slug":150770,"date":150771,"published":13,"tags":150772,"author":-1,"cover":49206,"excerpt":-1},"spring-data-aggregate-functions-repository","2017-07-31T08:48:35-04:00",[11002],{"type":19,"value":150774,"toc":151428},[150775,150780,150785,150788,150792,150800,150806,150809,150815,150818,150944,150951,151174,151177,151181,151189,151250,151259,151396,151399,151405,151407,151420,151425],[22,150776,150299,150777,2755],{},[677,150778,56768],{"href":150302,"rel":150779},[681],[29685,150781,150782],{},[22,150783,150784],{},"Hi Dan, How can I use the @Query to specify an aggregation function in a select statement like... \"select post_date, sum(value) from post group by post_date\" retrieve it to a DTO and show on screen?",[22,150786,150787],{},"This is actually pretty easy to do using the @Query annotation. In this tutorial, I will show you how to do this using JQL and native SQL. ",[26,150789,150791],{"id":150790},"creating-the-application","Creating the application",[22,150793,150794,150795,57647],{},"The first thing we need to do is to create a very basic Spring Boot application with the following dependencies. If you want you can grab the source code for this ",[677,150796,150799],{"href":150797,"rel":150798},"https://github.com/cfaddict/queryagg",[681],"demo here",[22,150801,150802],{},[653,150803],{"alt":150804,"src":150805},"Aggregate Functions Dependencies ","./2017-07-26_15-05-12-1024x645.png",[22,150807,150808],{},"I won't walk you through every single step of this application because I don't want to focus on the application itself. We are building a simple application that exposes some endpoints that call a service and a repository. ",[22,150810,150811],{},[653,150812],{"alt":150813,"src":150814},"Aggregate Functions Project Structure","./2017-07-31_08-38-06.png",[22,150816,150817],{},"This is what our domain looks like. ",[52,150819,150821],{"className":54,"code":150820,"language":56,"meta":57,"style":57},"@Entity\n@Data\n@NoArgsConstructor\npublic class User {\n\n @Id @GeneratedValue\n private long id;\n private String firstName;\n private int age;\n\n public User(String first, int age) {\n this.firstName = first;\n this.age = age;\n }\n\n}\n",[59,150822,150823,150829,150835,150841,150851,150855,150865,150873,150879,150888,150892,150911,150921,150932,150936,150940],{"__ignoreMap":57},[62,150824,150825,150827],{"class":64,"line":65},[62,150826,942],{"class":72},[62,150828,8999],{"class":68},[62,150830,150831,150833],{"class":64,"line":76},[62,150832,942],{"class":72},[62,150834,9388],{"class":68},[62,150836,150837,150839],{"class":64,"line":82},[62,150838,942],{"class":72},[62,150840,61750],{"class":68},[62,150842,150843,150845,150847,150849],{"class":64,"line":89},[62,150844,116],{"class":68},[62,150846,119],{"class":68},[62,150848,22289],{"class":122},[62,150850,126],{"class":72},[62,150852,150853],{"class":64,"line":95},[62,150854,79],{"emptyLinePlaceholder":13},[62,150856,150857,150859,150861,150863],{"class":64,"line":101},[62,150858,2143],{"class":72},[62,150860,9016],{"class":68},[62,150862,9019],{"class":72},[62,150864,9022],{"class":68},[62,150866,150867,150869,150871],{"class":64,"line":107},[62,150868,137],{"class":68},[62,150870,109641],{"class":68},[62,150872,9954],{"class":72},[62,150874,150875,150877],{"class":64,"line":113},[62,150876,137],{"class":68},[62,150878,22329],{"class":72},[62,150880,150881,150883,150885],{"class":64,"line":129},[62,150882,137],{"class":68},[62,150884,140],{"class":68},[62,150886,150887],{"class":72}," age;\n",[62,150889,150890],{"class":64,"line":134},[62,150891,79],{"emptyLinePlaceholder":13},[62,150893,150894,150896,150898,150900,150902,150904,150906,150909],{"class":64,"line":156},[62,150895,194],{"class":68},[62,150897,22289],{"class":122},[62,150899,1049],{"class":72},[62,150901,52049],{"class":889},[62,150903,976],{"class":72},[62,150905,747],{"class":68},[62,150907,150908],{"class":889}," age",[62,150910,768],{"class":72},[62,150912,150913,150915,150917,150919],{"class":64,"line":161},[62,150914,2405],{"class":149},[62,150916,101463],{"class":72},[62,150918,146],{"class":68},[62,150920,52073],{"class":72},[62,150922,150923,150925,150928,150930],{"class":64,"line":167},[62,150924,2405],{"class":149},[62,150926,150927],{"class":72},".age ",[62,150929,146],{"class":68},[62,150931,150887],{"class":72},[62,150933,150934],{"class":64,"line":173},[62,150935,223],{"class":72},[62,150937,150938],{"class":64,"line":179},[62,150939,79],{"emptyLinePlaceholder":13},[62,150941,150942],{"class":64,"line":185},[62,150943,379],{"class":72},[22,150945,150946,150947,57647],{},"And we will load some initial test data using a ",[677,150948,61684],{"href":150949,"rel":150950},"https://www.danvega.dev/blog/2017/04/07/spring-boot-command-line-runner",[681],[52,150952,150954],{"className":54,"code":150953,"language":56,"meta":57,"style":57},"@SpringBootApplication\npublic class Application {\n\n public static void main(String[] args) {\n SpringApplication.run(Application.class, args);\n }\n\n @Bean\n CommandLineRunner runner(UserRepository userRepository){\n return args -> {\n userRepository.save( new User(\"Sam\", 20) );\n userRepository.save( new User(\"Joe\", 25) );\n userRepository.save( new User(\"Mark\", 48) );\n userRepository.save( new User(\"Emily\", 26) );\n userRepository.save( new User(\"Nick\", 59) );\n };\n }\n\n}\n",[59,150955,150956,150962,150972,150976,150996,151004,151008,151012,151018,151032,151042,151065,151087,151110,151134,151158,151162,151166,151170],{"__ignoreMap":57},[62,150957,150958,150960],{"class":64,"line":65},[62,150959,942],{"class":72},[62,150961,2079],{"class":68},[62,150963,150964,150966,150968,150970],{"class":64,"line":76},[62,150965,116],{"class":68},[62,150967,119],{"class":68},[62,150969,2088],{"class":122},[62,150971,126],{"class":72},[62,150973,150974],{"class":64,"line":82},[62,150975,79],{"emptyLinePlaceholder":13},[62,150977,150978,150980,150982,150984,150986,150988,150990,150992,150994],{"class":64,"line":89},[62,150979,194],{"class":68},[62,150981,2101],{"class":68},[62,150983,200],{"class":68},[62,150985,2106],{"class":122},[62,150987,2109],{"class":72},[62,150989,973],{"class":68},[62,150991,2114],{"class":72},[62,150993,2117],{"class":889},[62,150995,768],{"class":72},[62,150997,150998,151000,151002],{"class":64,"line":95},[62,150999,2124],{"class":72},[62,151001,2127],{"class":122},[62,151003,2130],{"class":72},[62,151005,151006],{"class":64,"line":101},[62,151007,223],{"class":72},[62,151009,151010],{"class":64,"line":107},[62,151011,79],{"emptyLinePlaceholder":13},[62,151013,151014,151016],{"class":64,"line":113},[62,151015,2143],{"class":72},[62,151017,2146],{"class":68},[62,151019,151020,151022,151024,151027,151030],{"class":64,"line":129},[62,151021,2151],{"class":72},[62,151023,52603],{"class":122},[62,151025,151026],{"class":72},"(UserRepository ",[62,151028,151029],{"class":889},"userRepository",[62,151031,34126],{"class":72},[62,151033,151034,151036,151038,151040],{"class":64,"line":134},[62,151035,360],{"class":68},[62,151037,2169],{"class":72},[62,151039,800],{"class":68},[62,151041,126],{"class":72},[62,151043,151044,151047,151049,151051,151053,151055,151057,151059,151061,151063],{"class":64,"line":156},[62,151045,151046],{"class":72}," userRepository.",[62,151048,22562],{"class":122},[62,151050,52630],{"class":72},[62,151052,2426],{"class":68},[62,151054,22289],{"class":122},[62,151056,2109],{"class":72},[62,151058,75484],{"class":1675},[62,151060,976],{"class":72},[62,151062,30188],{"class":149},[62,151064,59004],{"class":72},[62,151066,151067,151069,151071,151073,151075,151077,151079,151081,151083,151085],{"class":64,"line":161},[62,151068,151046],{"class":72},[62,151070,22562],{"class":122},[62,151072,52630],{"class":72},[62,151074,2426],{"class":68},[62,151076,22289],{"class":122},[62,151078,2109],{"class":72},[62,151080,75504],{"class":1675},[62,151082,976],{"class":72},[62,151084,29448],{"class":149},[62,151086,59004],{"class":72},[62,151088,151089,151091,151093,151095,151097,151099,151101,151103,151105,151108],{"class":64,"line":167},[62,151090,151046],{"class":72},[62,151092,22562],{"class":122},[62,151094,52630],{"class":72},[62,151096,2426],{"class":68},[62,151098,22289],{"class":122},[62,151100,2109],{"class":72},[62,151102,99875],{"class":1675},[62,151104,976],{"class":72},[62,151106,151107],{"class":149},"48",[62,151109,59004],{"class":72},[62,151111,151112,151114,151116,151118,151120,151122,151124,151127,151129,151132],{"class":64,"line":173},[62,151113,151046],{"class":72},[62,151115,22562],{"class":122},[62,151117,52630],{"class":72},[62,151119,2426],{"class":68},[62,151121,22289],{"class":122},[62,151123,2109],{"class":72},[62,151125,151126],{"class":1675},"\"Emily\"",[62,151128,976],{"class":72},[62,151130,151131],{"class":149},"26",[62,151133,59004],{"class":72},[62,151135,151136,151138,151140,151142,151144,151146,151148,151151,151153,151156],{"class":64,"line":179},[62,151137,151046],{"class":72},[62,151139,22562],{"class":122},[62,151141,52630],{"class":72},[62,151143,2426],{"class":68},[62,151145,22289],{"class":122},[62,151147,2109],{"class":72},[62,151149,151150],{"class":1675},"\"Nick\"",[62,151152,976],{"class":72},[62,151154,151155],{"class":149},"59",[62,151157,59004],{"class":72},[62,151159,151160],{"class":64,"line":185},[62,151161,2252],{"class":72},[62,151163,151164],{"class":64,"line":191},[62,151165,223],{"class":72},[62,151167,151168],{"class":64,"line":209},[62,151169,79],{"emptyLinePlaceholder":13},[62,151171,151172],{"class":64,"line":220},[62,151173,379],{"class":72},[22,151175,151176],{},"With those in place, we can now begin to add some new methods to our repositories. ",[26,151178,151180],{"id":151179},"spring-data-repositories","Spring Data Repositories",[22,151182,151183,151184,57647],{},"In the first example, I want to find out the average age of all the users in our system. We can do so by using the Aggregate function AVG in both JPQL and SQL. We can write these queries using the ",[677,151185,151188],{"href":151186,"rel":151187},"https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods.at-query",[681],"@Query annotation",[52,151190,151192],{"className":54,"code":151191,"language":56,"meta":57,"style":57},"public interface UserRepository extends CrudRepository\u003CUser, Long> {\n\n @Query(\"SELECT AVG(u.age) from User u\")\n int getAverageAge();\n\n}\n",[59,151193,151194,151216,151220,151233,151242,151246],{"__ignoreMap":57},[62,151195,151196,151198,151200,151202,151204,151206,151208,151210,151212,151214],{"class":64,"line":65},[62,151197,116],{"class":68},[62,151199,8531],{"class":68},[62,151201,22390],{"class":122},[62,151203,8537],{"class":68},[62,151205,22395],{"class":122},[62,151207,760],{"class":72},[62,151209,22260],{"class":68},[62,151211,976],{"class":72},[62,151213,6850],{"class":68},[62,151215,8552],{"class":72},[62,151217,151218],{"class":64,"line":76},[62,151219,79],{"emptyLinePlaceholder":13},[62,151221,151222,151224,151226,151228,151231],{"class":64,"line":82},[62,151223,2143],{"class":72},[62,151225,83344],{"class":68},[62,151227,2109],{"class":72},[62,151229,151230],{"class":1675},"\"SELECT AVG(u.age) from User u\"",[62,151232,2212],{"class":72},[62,151234,151235,151237,151240],{"class":64,"line":89},[62,151236,18523],{"class":68},[62,151238,151239],{"class":122}," getAverageAge",[62,151241,822],{"class":72},[62,151243,151244],{"class":64,"line":95},[62,151245,79],{"emptyLinePlaceholder":13},[62,151247,151248],{"class":64,"line":101},[62,151249,379],{"class":72},[22,151251,151252,151253,151258],{},"In this example, we are using ",[677,151254,151257],{"href":151255,"rel":151256},"http://docs.oracle.com/html/E13946_04/ejb3_langref.html",[681],"JPQL"," to write the SQL that will fetch us the data we need. JPQL stands for Java Persistence Query Language and if you have ever worked with Hibernate before you have probably seen this in action. It is important to understand that this is the default and if you want to write native SQL you can, but you need to add a flag, more on that in a bit. Now let's say that we wanted to add a new method that would give us the max age of all the users but also allow us to exclude a single user. This is strictly a demo and you probably wouldn't use this in a real application but it does show off a couple of things. First, it shows us how to include parameters in our SQL statement and it also shows us that we can write native SQL by using the native SQL flag. ",[52,151260,151262],{"className":54,"code":151261,"language":56,"meta":57,"style":57},"package com.therealdanvega.repository;\n\nimport com.therealdanvega.domain.User;\nimport org.springframework.data.jpa.repository.Query;\nimport org.springframework.data.repository.CrudRepository;\n\npublic interface UserRepository extends CrudRepository\u003CUser, Long> {\n\n @Query(\"SELECT AVG(u.age) from User u\")\n int getAverageAge();\n\n @Query(value = \"SELECT max(age) from User where first_name \u003C> ?1\", nativeQuery = true)\n int getMaxAgeMinus(String name);\n\n}\n",[59,151263,151264,151271,151275,151282,151289,151295,151299,151321,151325,151337,151345,151349,151375,151388,151392],{"__ignoreMap":57},[62,151265,151266,151268],{"class":64,"line":65},[62,151267,69],{"class":68},[62,151269,151270],{"class":72}," com.therealdanvega.repository;\n",[62,151272,151273],{"class":64,"line":76},[62,151274,79],{"emptyLinePlaceholder":13},[62,151276,151277,151279],{"class":64,"line":82},[62,151278,27875],{"class":68},[62,151280,151281],{"class":72}," com.therealdanvega.domain.User;\n",[62,151283,151284,151286],{"class":64,"line":89},[62,151285,27875],{"class":68},[62,151287,151288],{"class":72}," org.springframework.data.jpa.repository.Query;\n",[62,151290,151291,151293],{"class":64,"line":95},[62,151292,27875],{"class":68},[62,151294,52430],{"class":72},[62,151296,151297],{"class":64,"line":101},[62,151298,79],{"emptyLinePlaceholder":13},[62,151300,151301,151303,151305,151307,151309,151311,151313,151315,151317,151319],{"class":64,"line":107},[62,151302,116],{"class":68},[62,151304,8531],{"class":68},[62,151306,22390],{"class":122},[62,151308,8537],{"class":68},[62,151310,22395],{"class":122},[62,151312,760],{"class":72},[62,151314,22260],{"class":68},[62,151316,976],{"class":72},[62,151318,6850],{"class":68},[62,151320,8552],{"class":72},[62,151322,151323],{"class":64,"line":113},[62,151324,79],{"emptyLinePlaceholder":13},[62,151326,151327,151329,151331,151333,151335],{"class":64,"line":129},[62,151328,2143],{"class":72},[62,151330,83344],{"class":68},[62,151332,2109],{"class":72},[62,151334,151230],{"class":1675},[62,151336,2212],{"class":72},[62,151338,151339,151341,151343],{"class":64,"line":134},[62,151340,18523],{"class":68},[62,151342,151239],{"class":122},[62,151344,822],{"class":72},[62,151346,151347],{"class":64,"line":156},[62,151348,79],{"emptyLinePlaceholder":13},[62,151350,151351,151353,151355,151357,151359,151361,151364,151366,151369,151371,151373],{"class":64,"line":161},[62,151352,2143],{"class":72},[62,151354,83344],{"class":68},[62,151356,2109],{"class":72},[62,151358,2553],{"class":149},[62,151360,2556],{"class":68},[62,151362,151363],{"class":1675}," \"SELECT max(age) from User where first_name \u003C> ?1\"",[62,151365,976],{"class":72},[62,151367,151368],{"class":149},"nativeQuery",[62,151370,2556],{"class":68},[62,151372,1227],{"class":149},[62,151374,2212],{"class":72},[62,151376,151377,151379,151382,151384,151386],{"class":64,"line":167},[62,151378,18523],{"class":68},[62,151380,151381],{"class":122}," getMaxAgeMinus",[62,151383,1049],{"class":72},[62,151385,3107],{"class":889},[62,151387,1133],{"class":72},[62,151389,151390],{"class":64,"line":173},[62,151391,79],{"emptyLinePlaceholder":13},[62,151393,151394],{"class":64,"line":179},[62,151395,379],{"class":72},[26,151397,151398],{"id":116944},"Screencast ",[22,151400,151401],{},[677,151402,151403],{"href":151403,"rel":151404},"https://www.youtube.com/watch?v=zLRavueFJy0",[681],[26,151406,1499],{"id":1498},[22,151408,151409,151410,151415,151416,57647],{},"I think the one that confuses people is not understanding that the default query syntax for @Query annotation is ",[677,151411,151414],{"href":151412,"rel":151413},"http://docs.oracle.com/html/E13946_04/ejb3_langref.html#ejb3_langref_agg_examples",[681],"JPQL and that it does support aggregate functions",". If you are interested in the full source code for this ",[677,151417,151419],{"href":150797,"rel":151418},[681],"demo you can grab it here",[22,151421,50754,151422,151424],{},[646,151423,49733],{}," Are you facing any issues with your Spring Data Repositories? _",[1527,151426,151427],{},"html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .spoOn, html code.shiki .spoOn{--shiki-default:#E36209;--shiki-dark:#FFAB70;--shiki-sepia:#E36209}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}",{"title":57,"searchDepth":76,"depth":76,"links":151429},[151430,151431,151432,151433],{"id":150790,"depth":76,"text":150791},{"id":151179,"depth":76,"text":151180},{"id":116944,"depth":76,"text":151398},{"id":1498,"depth":76,"text":1499},{"_id":151435,"path":151436,"title":151437,"description":151437,"meta":151438,"body":151443},"content/blog/2017/07/26/use-hikaricp-next-spring-boot-project.md","/blog/2017/07/26/use-hikaricp-next-spring-boot-project","How to use HikariCP in your next Spring Boot project",{"slug":151439,"date":151440,"published":13,"tags":151441,"author":-1,"cover":151442,"excerpt":-1},"use-hikaricp-next-spring-boot-project","2017-07-26T08:16:50-04:00",[56,11002],"./pexels-photo-169976-760x506.jpeg",{"type":19,"value":151444,"toc":151886},[151445,151454,151457,151464,151478,151491,151508,151522,151526,151529,151562,151565,151579,151582,151656,151660,151663,151827,151830,151850,151854,151868,151872,151878,151880,151883],[22,151446,151447,151448,151453],{},"Performance is something we are all trying to improve on when it comes to our applications. It turns out that there is a very reliable, high performance JDBC connection pool out there that we can start using in our Spring Boot applications today. In this article, we are going to take a look at HikariCP, the CP standing for a connection pool. ",[677,151449,151452],{"href":151450,"rel":151451},"https://brettwooldridge.github.io/HikariCP/",[681],"HikariCP"," is a “zero-overhead” production-quality connection pool.",[26,151455,151452],{"id":151456},"hikaricp",[22,151458,151459,151460,151463],{},"As I mentioned earlier, ",[677,151461,151452],{"href":151450,"rel":151462},[681]," is a reliable, high-performance JDBC connection pool. What is a connection pool you ask?",[29685,151465,151466],{},[22,151467,151468,151469,151472,151473],{},"In software engineering, a ",[646,151470,151471],{},"connection pool"," is a cache of database connections maintained so that the connections can be reused when future requests to the database are required. Connection pools are used to enhance the performance of executing commands on a database. - ",[677,151474,151477],{"href":151475,"rel":151476},"https://en.wikipedia.org/wiki/Connection_pool",[681],"Wikipedia",[22,151479,151480,151481,151486,151487,151490],{},"If you weren't already aware you are using the Tomcat pooling Datasource by default. Here is some great information ",[677,151482,151485],{"href":151483,"rel":151484},"http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-connect-to-production-database",[681],"from the documentation"," that explains how this is selected. Production database connections can also be auto-configured using a pooling ",[59,151488,151489],{},"DataSource"," . Here’s the algorithm for choosing a specific implementation:",[915,151492,151493,151499,151502,151505],{},[37,151494,151495,151496,151498],{},"We prefer the Tomcat pooling ",[59,151497,151489],{}," for its performance and concurrency, so if that is available we always choose it.",[37,151500,151501],{},"Otherwise, if HikariCP is available we will use it.",[37,151503,151504],{},"If neither the Tomcat pooling datasource nor HikariCP are available and if Commons DBCP is available we will use it, but we don’t recommend it in production and its support is deprecated.",[37,151506,151507],{},"Lastly, if Commons DBCP2 is available we will use it.",[22,151509,151510,151511,151514,151515,151518,151519,51832],{},"If you use the ",[59,151512,151513],{},"spring-boot-starter-jdbc"," or ",[59,151516,151517],{},"spring-boot-starter-data-jpa"," ‘starters’ you will automatically get a dependency to ",[59,151520,151521],{},"tomcat-jdbc",[26,151523,151525],{"id":151524},"using-hikaricp-in-spring-boot","Using HikariCP in Spring Boot",[22,151527,151528],{},"To use HikariCP you can simply add the following dependency to a new or existing project. ",[52,151530,151532],{"className":1769,"code":151531,"language":1771,"meta":57,"style":57},"\u003Cdependency>\n \u003CgroupId>com.zaxxer\u003C/groupId>\n \u003CartifactId>HikariCP\u003C/artifactId>\n \u003Cversion>2.6.1\u003C/version>\n \u003Cscope>compile\u003C/scope>\n\u003C/dependency>\n",[59,151533,151534,151538,151543,151548,151553,151558],{"__ignoreMap":57},[62,151535,151536],{"class":64,"line":65},[62,151537,46425],{},[62,151539,151540],{"class":64,"line":76},[62,151541,151542],{}," \u003CgroupId>com.zaxxer\u003C/groupId>\n",[62,151544,151545],{"class":64,"line":82},[62,151546,151547],{}," \u003CartifactId>HikariCP\u003C/artifactId>\n",[62,151549,151550],{"class":64,"line":89},[62,151551,151552],{}," \u003Cversion>2.6.1\u003C/version>\n",[62,151554,151555],{"class":64,"line":95},[62,151556,151557],{}," \u003Cscope>compile\u003C/scope>\n",[62,151559,151560],{"class":64,"line":101},[62,151561,46445],{},[22,151563,151564],{},"You used to have to configure your own Datasource but thanks to the magic of AutoConfiguration you don't have to anymore. Next, we are going to open up application.properties and add the following configuration. ",[52,151566,151568],{"className":54,"code":151567,"language":56,"meta":57,"style":57},"spring.datasource.type=com.zaxxer.hikari.HikariDataSource\n",[59,151569,151570],{"__ignoreMap":57},[62,151571,151572,151574,151576],{"class":64,"line":65},[62,151573,54849],{"class":72},[62,151575,146],{"class":68},[62,151577,151578],{"class":72},"com.zaxxer.hikari.HikariDataSource\n",[22,151580,151581],{},"Now if you run the application you should see something like this in the console letting us know our change was accepted. ",[52,151583,151585],{"className":1663,"code":151584,"language":1665,"meta":57,"style":57},"2017-07-26 07:57:26.345 INFO 1015 --- \\[ main\\] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...\n2017-07-26 07:57:26.505 INFO 1015 --- \\[ main\\] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.\n",[59,151586,151587,151623],{"__ignoreMap":57},[62,151588,151589,151592,151595,151597,151600,151602,151604,151607,151609,151612,151615,151618,151620],{"class":64,"line":65},[62,151590,151591],{"class":122},"2017-07-26",[62,151593,151594],{"class":1675}," 07:57:26.345",[62,151596,40606],{"class":1675},[62,151598,151599],{"class":149}," 1015",[62,151601,40612],{"class":149},[62,151603,58153],{"class":149},[62,151605,151606],{"class":1675}," main",[62,151608,74049],{"class":149},[62,151610,151611],{"class":1675}," com.zaxxer.hikari.HikariDataSource",[62,151613,151614],{"class":1675}," :",[62,151616,151617],{"class":1675}," HikariPool-1",[62,151619,128533],{"class":1675},[62,151621,151622],{"class":1675}," Starting...\n",[62,151624,151625,151627,151630,151632,151634,151636,151638,151640,151642,151644,151646,151648,151650,151653],{"class":64,"line":76},[62,151626,151591],{"class":122},[62,151628,151629],{"class":1675}," 07:57:26.505",[62,151631,40606],{"class":1675},[62,151633,151599],{"class":149},[62,151635,40612],{"class":149},[62,151637,58153],{"class":149},[62,151639,151606],{"class":1675},[62,151641,74049],{"class":149},[62,151643,151611],{"class":1675},[62,151645,151614],{"class":1675},[62,151647,151617],{"class":1675},[62,151649,128533],{"class":1675},[62,151651,151652],{"class":1675}," Start",[62,151654,151655],{"class":1675}," completed.\n",[26,151657,151659],{"id":151658},"spring-boot-autoconfiguration","Spring Boot AutoConfiguration",[22,151661,151662],{},"We have talked about AutoConfiguration before but I think this is a good chance to look at another great example of it in use. Remember how I said earlier that in the old days you might have had to configure your own Datasource? So why don't we have to do this now? If you do a quick search in IntelliJ (double tap shift key) and look for DataSourceConfiguration.class in (org.springframework.boot.autoconfigure.jdbc) you will find the following block of code.",[52,151664,151666],{"className":54,"code":151665,"language":56,"meta":57,"style":57},"@ConditionalOnClass({HikariDataSource.class})\n@ConditionalOnProperty(\n name = {\"spring.datasource.type\"},\n havingValue = \"com.zaxxer.hikari.HikariDataSource\",\n matchIfMissing = true\n)\nstatic class Hikari extends DataSourceConfiguration {\n Hikari() {\n }\n\n @Bean\n @ConfigurationProperties(\n prefix = \"spring.datasource.hikari\"\n )\n public HikariDataSource dataSource(DataSourceProperties properties) {\n return (HikariDataSource)this.createDataSource(properties, HikariDataSource.class);\n }\n}\n",[59,151667,151668,151678,151687,151700,151712,151721,151725,151742,151749,151753,151757,151763,151771,151781,151785,151802,151819,151823],{"__ignoreMap":57},[62,151669,151670,151672,151675],{"class":64,"line":65},[62,151671,942],{"class":72},[62,151673,151674],{"class":68},"ConditionalOnClass",[62,151676,151677],{"class":72},"({HikariDataSource.class})\n",[62,151679,151680,151682,151685],{"class":64,"line":76},[62,151681,942],{"class":72},[62,151683,151684],{"class":68},"ConditionalOnProperty",[62,151686,3301],{"class":72},[62,151688,151689,151691,151693,151695,151698],{"class":64,"line":82},[62,151690,139393],{"class":149},[62,151692,2556],{"class":68},[62,151694,57766],{"class":72},[62,151696,151697],{"class":1675},"\"spring.datasource.type\"",[62,151699,72286],{"class":72},[62,151701,151702,151705,151707,151710],{"class":64,"line":89},[62,151703,151704],{"class":149}," havingValue",[62,151706,2556],{"class":68},[62,151708,151709],{"class":1675}," \"com.zaxxer.hikari.HikariDataSource\"",[62,151711,3338],{"class":72},[62,151713,151714,151717,151719],{"class":64,"line":95},[62,151715,151716],{"class":149}," matchIfMissing",[62,151718,2556],{"class":68},[62,151720,69203],{"class":149},[62,151722,151723],{"class":64,"line":101},[62,151724,2212],{"class":72},[62,151726,151727,151730,151732,151735,151737,151740],{"class":64,"line":107},[62,151728,151729],{"class":68},"static",[62,151731,119],{"class":68},[62,151733,151734],{"class":122}," Hikari",[62,151736,8537],{"class":68},[62,151738,151739],{"class":122}," DataSourceConfiguration",[62,151741,126],{"class":72},[62,151743,151744,151747],{"class":64,"line":113},[62,151745,151746],{"class":122}," Hikari",[62,151748,206],{"class":72},[62,151750,151751],{"class":64,"line":129},[62,151752,223],{"class":72},[62,151754,151755],{"class":64,"line":134},[62,151756,79],{"emptyLinePlaceholder":13},[62,151758,151759,151761],{"class":64,"line":156},[62,151760,2143],{"class":72},[62,151762,2146],{"class":68},[62,151764,151765,151767,151769],{"class":64,"line":161},[62,151766,2143],{"class":72},[62,151768,96758],{"class":68},[62,151770,3301],{"class":72},[62,151772,151773,151776,151778],{"class":64,"line":167},[62,151774,151775],{"class":149}," prefix",[62,151777,2556],{"class":68},[62,151779,151780],{"class":1675}," \"spring.datasource.hikari\"\n",[62,151782,151783],{"class":64,"line":173},[62,151784,36951],{"class":72},[62,151786,151787,151789,151792,151795,151798,151800],{"class":64,"line":179},[62,151788,194],{"class":68},[62,151790,151791],{"class":72}," HikariDataSource ",[62,151793,151794],{"class":122},"dataSource",[62,151796,151797],{"class":72},"(DataSourceProperties ",[62,151799,1271],{"class":889},[62,151801,768],{"class":72},[62,151803,151804,151806,151809,151811,151813,151816],{"class":64,"line":185},[62,151805,360],{"class":68},[62,151807,151808],{"class":72}," (HikariDataSource)",[62,151810,1295],{"class":149},[62,151812,2755],{"class":72},[62,151814,151815],{"class":122},"createDataSource",[62,151817,151818],{"class":72},"(properties, HikariDataSource.class);\n",[62,151820,151821],{"class":64,"line":191},[62,151822,223],{"class":72},[62,151824,151825],{"class":64,"line":209},[62,151826,379],{"class":72},[22,151828,151829],{},"The @ConditionalOnClass is saying we are only going to create this class if this class exists on the classpath. Earlier we defined HikariCP as a dependency and this now becomes a true condition. The @ConditionalOnProperty is now looking for that property and looking for a specific value. When we defined that property in application.properties, that statement was now true. You can see that it creates a Datasource and uses the prefix \"spring.datasource.hikari\" for its configuration properties. This means all of the configuration Hikari exposes can be used using that prefix. The HikariCP documentation tells us that we can change the connection timeout property so in our application.properties we can simply use ",[52,151831,151833],{"className":54,"code":151832,"language":56,"meta":57,"style":57},"spring.datasource.hikari.connection-timeout=60000\n",[59,151834,151835],{"__ignoreMap":57},[62,151836,151837,151840,151842,151845,151847],{"class":64,"line":65},[62,151838,151839],{"class":72},"spring.datasource.hikari.connection",[62,151841,11635],{"class":68},[62,151843,151844],{"class":72},"timeout",[62,151846,146],{"class":68},[62,151848,151849],{"class":149},"60000\n",[26,151851,151853],{"id":151852},"spring-boot-20","Spring Boot 2.0",[22,151855,151856,151857,151862,151863,2755],{},"Spring Boot 2.0 is coming soon and when it ships we will no longer need to worry about this. This is because they are going to move to ",[677,151858,151861],{"href":151859,"rel":151860},"https://github.com/spring-projects/spring-boot/issues/6013",[681],"HikariCP by default",". This is just another awesome example of the Spring Boot team providing us with sensible defaults. Want to learn more about ",[677,151864,151867],{"href":151865,"rel":151866},"https://www.danvega.dev/spring-boot-2-0",[681],"Spring Boot 2.0, click here",[26,151869,151871],{"id":151870},"hikaricp-spring-boot-screencast","HikariCP Spring Boot Screencast",[22,151873,151874,67945],{},[677,151875,151876],{"href":151876,"rel":151877},"https://youtu.be/Q8Dx8EzIveM",[681],[26,151879,1499],{"id":1498},[22,151881,151882],{},"In conclusion AutoConfiguration is really awesome and helps save us some valuable time. In this case, we simply declared a dependency and a property and Spring Boot wired up a Hikari Datasource for us.",[1527,151884,151885],{},"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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}html pre.shiki code .spoOn, html code.shiki .spoOn{--shiki-default:#E36209;--shiki-dark:#FFAB70;--shiki-sepia:#E36209}",{"title":57,"searchDepth":76,"depth":76,"links":151887},[151888,151889,151890,151891,151892,151893],{"id":151456,"depth":76,"text":151452},{"id":151524,"depth":76,"text":151525},{"id":151658,"depth":76,"text":151659},{"id":151852,"depth":76,"text":151853},{"id":151870,"depth":76,"text":151871},{"id":1498,"depth":76,"text":1499},{"_id":151895,"path":151896,"title":151897,"description":151897,"meta":151898,"body":151903},"content/blog/2017/07/24/spring-boot-application-failed-start.md","/blog/2017/07/24/spring-boot-application-failed-start","Spring Boot Application Failed To Start",{"slug":151899,"date":151900,"published":13,"tags":151901,"author":-1,"cover":151902,"excerpt":-1},"spring-boot-application-failed-start","2017-07-24T09:55:33-04:00",[56,11002],"./pexels-photo-205316-760x506.png",{"type":19,"value":151904,"toc":152503},[151905,151908,151912,151915,152461,152464,152468,152471,152477,152483,152485,152493,152500],[22,151906,151907],{},"Errors are a part of the development lifecycle and there is no way getting around them. I know we all like to think we are top notch coders and that we are error prone but I can assure you that isn't the case. When errors come up it is nice to have as much information at our disposal as possible. We don't have time to search the web for hours of cryptic stack messages to find our solution. In this article, I am going to show you an error you might come across and how Spring Boot 1.4 has made it a little bit easier to decipher error messages. ",[26,151909,151911],{"id":151910},"analysis-of-startup-failures","Analysis of Startup Failures",[22,151913,151914],{},"Prior to Spring Boot version 1.4, it was a little tough decrypting error messages that were causing our applications not to start. If you already have an application running on port 8080 and try to start up another one you might have seen an error that looked like this. ",[52,151916,151918],{"className":1663,"code":151917,"language":1665,"meta":57,"style":57},"2016-02-16 17:46:14.334 ERROR 24753 --- \\[ main\\] o.s.boot.SpringApplication : Application startup failed\n\njava.lang.RuntimeException: java.net.BindException: Address already in use\n at io.undertow.Undertow.start(Undertow.java:181) ~\\[undertow-core-1.3.14.Final.jar:1.3.14.Final\\]\n at org.springframework.boot.context.embedded.undertow.UndertowEmbeddedServletContainer.start(UndertowEmbeddedServletContainer.java:121) ~\\[spring-boot-1.3.2.RELEASE.jar:1.3.2.RELEASE\\]\n at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.startEmbeddedServletContainer(EmbeddedWebApplicationContext.java:293) ~\\[spring-boot-1.3.2.RELEASE.jar:1.3.2.RELEASE\\]\n at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.finishRefresh(EmbeddedWebApplicationContext.java:141) ~\\[spring-boot-1.3.2.RELEASE.jar:1.3.2.RELEASE\\]\n at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:541) ~\\[spring-context-4.2.4.RELEASE.jar:4.2.4.RELEASE\\]\n at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:118) ~\\[spring-boot-1.3.2.RELEASE.jar:1.3.2.RELEASE\\]\n at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:766) \\[spring-boot-1.3.2.RELEASE.jar:1.3.2.RELEASE\\]\n at org.springframework.boot.SpringApplication.createAndRefreshContext(SpringApplication.java:361) \\[spring-boot-1.3.2.RELEASE.jar:1.3.2.RELEASE\\]\n at org.springframework.boot.SpringApplication.run(SpringApplication.java:307) \\[spring-boot-1.3.2.RELEASE.jar:1.3.2.RELEASE\\]\n at org.springframework.boot.SpringApplication.run(SpringApplication.java:1191) \\[spring-boot-1.3.2.RELEASE.jar:1.3.2.RELEASE\\]\n at org.springframework.boot.SpringApplication.run(SpringApplication.java:1180) \\[spring-boot-1.3.2.RELEASE.jar:1.3.2.RELEASE\\]\n at sample.undertow.SampleUndertowApplication.main(SampleUndertowApplication.java:26) \\[classes/:na\\]\nCaused by: java.net.BindException: Address already in use\n at sun.nio.ch.Net.bind0(Native Method) ~\\[na:1.8.0\\_60\\]\n at sun.nio.ch.Net.bind(Net.java:433) ~\\[na:1.8.0\\_60\\]\n at sun.nio.ch.Net.bind(Net.java:425) ~\\[na:1.8.0\\_60\\]\n at sun.nio.ch.ServerSocketChannelImpl.bind(ServerSocketChannelImpl.java:223) ~\\[na:1.8.0\\_60\\]\n at sun.nio.ch.ServerSocketAdaptor.bind(ServerSocketAdaptor.java:74) ~\\[na:1.8.0\\_60\\]\n at org.xnio.nio.NioXnioWorker.createTcpConnectionServer(NioXnioWorker.java:190) ~\\[xnio-nio-3.3.4.Final.jar:3.3.4.Final\\]\n at org.xnio.XnioWorker.createStreamConnectionServer(XnioWorker.java:243) ~\\[xnio-api-3.3.4.Final.jar:3.3.4.Final\\]\n at io.undertow.Undertow.start(Undertow.java:137) ~\\[undertow-core-1.3.14.Final.jar:1.3.14.Final\\]\n ... 11 common frames omitted\n",[59,151919,151920,151954,151958,151976,151999,152022,152044,152066,152089,152111,152131,152151,152171,152190,152209,152230,152246,152276,152302,152327,152353,152379,152402,152425,152446],{"__ignoreMap":57},[62,151921,151922,151925,151928,151930,151933,151935,151937,151939,151941,151944,151947,151949,151951],{"class":64,"line":65},[62,151923,151924],{"class":122},"2016-02-16",[62,151926,151927],{"class":1675}," 17:46:14.334",[62,151929,124805],{"class":1675},[62,151931,151932],{"class":149}," 24753",[62,151934,40612],{"class":149},[62,151936,58153],{"class":149},[62,151938,151606],{"class":1675},[62,151940,74049],{"class":149},[62,151942,151943],{"class":1675}," o.s.boot.SpringApplication",[62,151945,151946],{"class":1675}," :",[62,151948,2088],{"class":1675},[62,151950,125066],{"class":1675},[62,151952,151953],{"class":1675}," failed\n",[62,151955,151956],{"class":64,"line":76},[62,151957,79],{"emptyLinePlaceholder":13},[62,151959,151960,151963,151966,151968,151971,151973],{"class":64,"line":82},[62,151961,151962],{"class":122},"java.lang.RuntimeException:",[62,151964,151965],{"class":1675}," java.net.BindException:",[62,151967,101554],{"class":1675},[62,151969,151970],{"class":1675}," already",[62,151972,57031],{"class":1675},[62,151974,151975],{"class":1675}," use\n",[62,151977,151978,151980,151983,151985,151988,151990,151992,151994,151997],{"class":64,"line":89},[62,151979,70808],{"class":122},[62,151981,151982],{"class":1675}," io.undertow.Undertow.start",[62,151984,2109],{"class":72},[62,151986,151987],{"class":122},"Undertow.java:181",[62,151989,5024],{"class":72},[62,151991,70821],{"class":1675},[62,151993,70824],{"class":149},[62,151995,151996],{"class":1675},"undertow-core-1.3.14.Final.jar:1.3.14.Final",[62,151998,50699],{"class":149},[62,152000,152001,152003,152006,152008,152011,152013,152015,152017,152020],{"class":64,"line":95},[62,152002,70808],{"class":122},[62,152004,152005],{"class":1675}," org.springframework.boot.context.embedded.undertow.UndertowEmbeddedServletContainer.start",[62,152007,2109],{"class":72},[62,152009,152010],{"class":122},"UndertowEmbeddedServletContainer.java:121",[62,152012,5024],{"class":72},[62,152014,70821],{"class":1675},[62,152016,70824],{"class":149},[62,152018,152019],{"class":1675},"spring-boot-1.3.2.RELEASE.jar:1.3.2.RELEASE",[62,152021,50699],{"class":149},[62,152023,152024,152026,152029,152031,152034,152036,152038,152040,152042],{"class":64,"line":101},[62,152025,70808],{"class":122},[62,152027,152028],{"class":1675}," org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.startEmbeddedServletContainer",[62,152030,2109],{"class":72},[62,152032,152033],{"class":122},"EmbeddedWebApplicationContext.java:293",[62,152035,5024],{"class":72},[62,152037,70821],{"class":1675},[62,152039,70824],{"class":149},[62,152041,152019],{"class":1675},[62,152043,50699],{"class":149},[62,152045,152046,152048,152051,152053,152056,152058,152060,152062,152064],{"class":64,"line":107},[62,152047,70808],{"class":122},[62,152049,152050],{"class":1675}," org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.finishRefresh",[62,152052,2109],{"class":72},[62,152054,152055],{"class":122},"EmbeddedWebApplicationContext.java:141",[62,152057,5024],{"class":72},[62,152059,70821],{"class":1675},[62,152061,70824],{"class":149},[62,152063,152019],{"class":1675},[62,152065,50699],{"class":149},[62,152067,152068,152070,152073,152075,152078,152080,152082,152084,152087],{"class":64,"line":113},[62,152069,70808],{"class":122},[62,152071,152072],{"class":1675}," org.springframework.context.support.AbstractApplicationContext.refresh",[62,152074,2109],{"class":72},[62,152076,152077],{"class":122},"AbstractApplicationContext.java:541",[62,152079,5024],{"class":72},[62,152081,70821],{"class":1675},[62,152083,70824],{"class":149},[62,152085,152086],{"class":1675},"spring-context-4.2.4.RELEASE.jar:4.2.4.RELEASE",[62,152088,50699],{"class":149},[62,152090,152091,152093,152096,152098,152101,152103,152105,152107,152109],{"class":64,"line":129},[62,152092,70808],{"class":122},[62,152094,152095],{"class":1675}," org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh",[62,152097,2109],{"class":72},[62,152099,152100],{"class":122},"EmbeddedWebApplicationContext.java:118",[62,152102,5024],{"class":72},[62,152104,70821],{"class":1675},[62,152106,70824],{"class":149},[62,152108,152019],{"class":1675},[62,152110,50699],{"class":149},[62,152112,152113,152115,152118,152120,152123,152125,152127,152129],{"class":64,"line":134},[62,152114,70808],{"class":122},[62,152116,152117],{"class":1675}," org.springframework.boot.SpringApplication.refresh",[62,152119,2109],{"class":72},[62,152121,152122],{"class":122},"SpringApplication.java:766",[62,152124,5024],{"class":72},[62,152126,70824],{"class":149},[62,152128,152019],{"class":1675},[62,152130,50699],{"class":149},[62,152132,152133,152135,152138,152140,152143,152145,152147,152149],{"class":64,"line":156},[62,152134,70808],{"class":122},[62,152136,152137],{"class":1675}," org.springframework.boot.SpringApplication.createAndRefreshContext",[62,152139,2109],{"class":72},[62,152141,152142],{"class":122},"SpringApplication.java:361",[62,152144,5024],{"class":72},[62,152146,70824],{"class":149},[62,152148,152019],{"class":1675},[62,152150,50699],{"class":149},[62,152152,152153,152155,152158,152160,152163,152165,152167,152169],{"class":64,"line":161},[62,152154,70808],{"class":122},[62,152156,152157],{"class":1675}," org.springframework.boot.SpringApplication.run",[62,152159,2109],{"class":72},[62,152161,152162],{"class":122},"SpringApplication.java:307",[62,152164,5024],{"class":72},[62,152166,70824],{"class":149},[62,152168,152019],{"class":1675},[62,152170,50699],{"class":149},[62,152172,152173,152175,152177,152179,152182,152184,152186,152188],{"class":64,"line":167},[62,152174,70808],{"class":122},[62,152176,152157],{"class":1675},[62,152178,2109],{"class":72},[62,152180,152181],{"class":122},"SpringApplication.java:1191",[62,152183,5024],{"class":72},[62,152185,70824],{"class":149},[62,152187,152019],{"class":1675},[62,152189,50699],{"class":149},[62,152191,152192,152194,152196,152198,152201,152203,152205,152207],{"class":64,"line":173},[62,152193,70808],{"class":122},[62,152195,152157],{"class":1675},[62,152197,2109],{"class":72},[62,152199,152200],{"class":122},"SpringApplication.java:1180",[62,152202,5024],{"class":72},[62,152204,70824],{"class":149},[62,152206,152019],{"class":1675},[62,152208,50699],{"class":149},[62,152210,152211,152213,152216,152218,152221,152223,152225,152228],{"class":64,"line":179},[62,152212,70808],{"class":122},[62,152214,152215],{"class":1675}," sample.undertow.SampleUndertowApplication.main",[62,152217,2109],{"class":72},[62,152219,152220],{"class":122},"SampleUndertowApplication.java:26",[62,152222,5024],{"class":72},[62,152224,70824],{"class":149},[62,152226,152227],{"class":1675},"classes/:na",[62,152229,50699],{"class":149},[62,152231,152232,152234,152236,152238,152240,152242,152244],{"class":64,"line":185},[62,152233,59030],{"class":122},[62,152235,59033],{"class":1675},[62,152237,151965],{"class":1675},[62,152239,101554],{"class":1675},[62,152241,151970],{"class":1675},[62,152243,57031],{"class":1675},[62,152245,151975],{"class":1675},[62,152247,152248,152250,152253,152255,152258,152261,152263,152265,152267,152270,152272,152274],{"class":64,"line":191},[62,152249,70808],{"class":122},[62,152251,152252],{"class":1675}," sun.nio.ch.Net.bind0",[62,152254,2109],{"class":72},[62,152256,152257],{"class":122},"Native",[62,152259,152260],{"class":1675}," Method",[62,152262,5024],{"class":72},[62,152264,70821],{"class":1675},[62,152266,70824],{"class":149},[62,152268,152269],{"class":1675},"na:1.8.0",[62,152271,54363],{"class":149},[62,152273,122299],{"class":1675},[62,152275,50699],{"class":149},[62,152277,152278,152280,152283,152285,152288,152290,152292,152294,152296,152298,152300],{"class":64,"line":209},[62,152279,70808],{"class":122},[62,152281,152282],{"class":1675}," sun.nio.ch.Net.bind",[62,152284,2109],{"class":72},[62,152286,152287],{"class":122},"Net.java:433",[62,152289,5024],{"class":72},[62,152291,70821],{"class":1675},[62,152293,70824],{"class":149},[62,152295,152269],{"class":1675},[62,152297,54363],{"class":149},[62,152299,122299],{"class":1675},[62,152301,50699],{"class":149},[62,152303,152304,152306,152308,152310,152313,152315,152317,152319,152321,152323,152325],{"class":64,"line":220},[62,152305,70808],{"class":122},[62,152307,152282],{"class":1675},[62,152309,2109],{"class":72},[62,152311,152312],{"class":122},"Net.java:425",[62,152314,5024],{"class":72},[62,152316,70821],{"class":1675},[62,152318,70824],{"class":149},[62,152320,152269],{"class":1675},[62,152322,54363],{"class":149},[62,152324,122299],{"class":1675},[62,152326,50699],{"class":149},[62,152328,152329,152331,152334,152336,152339,152341,152343,152345,152347,152349,152351],{"class":64,"line":226},[62,152330,70808],{"class":122},[62,152332,152333],{"class":1675}," sun.nio.ch.ServerSocketChannelImpl.bind",[62,152335,2109],{"class":72},[62,152337,152338],{"class":122},"ServerSocketChannelImpl.java:223",[62,152340,5024],{"class":72},[62,152342,70821],{"class":1675},[62,152344,70824],{"class":149},[62,152346,152269],{"class":1675},[62,152348,54363],{"class":149},[62,152350,122299],{"class":1675},[62,152352,50699],{"class":149},[62,152354,152355,152357,152360,152362,152365,152367,152369,152371,152373,152375,152377],{"class":64,"line":231},[62,152356,70808],{"class":122},[62,152358,152359],{"class":1675}," sun.nio.ch.ServerSocketAdaptor.bind",[62,152361,2109],{"class":72},[62,152363,152364],{"class":122},"ServerSocketAdaptor.java:74",[62,152366,5024],{"class":72},[62,152368,70821],{"class":1675},[62,152370,70824],{"class":149},[62,152372,152269],{"class":1675},[62,152374,54363],{"class":149},[62,152376,122299],{"class":1675},[62,152378,50699],{"class":149},[62,152380,152381,152383,152386,152388,152391,152393,152395,152397,152400],{"class":64,"line":236},[62,152382,70808],{"class":122},[62,152384,152385],{"class":1675}," org.xnio.nio.NioXnioWorker.createTcpConnectionServer",[62,152387,2109],{"class":72},[62,152389,152390],{"class":122},"NioXnioWorker.java:190",[62,152392,5024],{"class":72},[62,152394,70821],{"class":1675},[62,152396,70824],{"class":149},[62,152398,152399],{"class":1675},"xnio-nio-3.3.4.Final.jar:3.3.4.Final",[62,152401,50699],{"class":149},[62,152403,152404,152406,152409,152411,152414,152416,152418,152420,152423],{"class":64,"line":242},[62,152405,70808],{"class":122},[62,152407,152408],{"class":1675}," org.xnio.XnioWorker.createStreamConnectionServer",[62,152410,2109],{"class":72},[62,152412,152413],{"class":122},"XnioWorker.java:243",[62,152415,5024],{"class":72},[62,152417,70821],{"class":1675},[62,152419,70824],{"class":149},[62,152421,152422],{"class":1675},"xnio-api-3.3.4.Final.jar:3.3.4.Final",[62,152424,50699],{"class":149},[62,152426,152427,152429,152431,152433,152436,152438,152440,152442,152444],{"class":64,"line":247},[62,152428,70808],{"class":122},[62,152430,151982],{"class":1675},[62,152432,2109],{"class":72},[62,152434,152435],{"class":122},"Undertow.java:137",[62,152437,5024],{"class":72},[62,152439,70821],{"class":1675},[62,152441,70824],{"class":149},[62,152443,151996],{"class":1675},[62,152445,50699],{"class":149},[62,152447,152448,152451,152453,152455,152458],{"class":64,"line":252},[62,152449,152450],{"class":149}," ...",[62,152452,105607],{"class":149},[62,152454,141521],{"class":1675},[62,152456,152457],{"class":1675}," frames",[62,152459,152460],{"class":1675}," omitted\n",[22,152462,152463],{},"Now if you that error message enough times you can quickly recognize it but if you're seeing it for the first time it can be a little confusing. ",[26,152465,152467],{"id":152466},"spring-boot-14-improvement","Spring Boot 1.4 Improvement",[22,152469,152470],{},"Spring Boot will now perform analysis of common start-up failures and provide useful diagnostic information rather than simply logging an exception and its stack trace. This is what the same error message looks like in 1.4+. ",[22,152472,152473],{},[653,152474],{"alt":152475,"src":152476},"Application Failed to Start","./2017-07-24_09-48-54-1024x345.png",[22,152478,152479,152480,51832],{},"With this error message, you can clearly see why our application is not starting. If you still want to see the stack trace of the underlying cause, enable debug logging for ",[59,152481,152482],{},"org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter",[26,152484,1499],{"id":1498},[22,152486,152487,152488,57647],{},"The Spring Boot team is always listening to feedback to improve the developer experience. If you have issues like this one ",[677,152489,152492],{"href":152490,"rel":152491},"https://github.com/spring-projects/spring-boot",[681],"please consider submitting an issue request",[22,152494,152495],{},[4534,152496,152497,152499],{},[646,152498,49733],{}," What problems are you facing debugging errors in Spring Boot applications?",[1527,152501,152502],{},"html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}",{"title":57,"searchDepth":76,"depth":76,"links":152504},[152505,152506,152507],{"id":151910,"depth":76,"text":151911},{"id":152466,"depth":76,"text":152467},{"id":1498,"depth":76,"text":1499},{"_id":152509,"path":152510,"title":152511,"description":152511,"meta":152512,"body":152517},"content/blog/2017/07/05/read-json-data-spring-boot-write-database.md","/blog/2017/07/05/read-json-data-spring-boot-write-database","How to read JSON data in Spring Boot and write to a database",{"slug":152513,"date":152514,"published":13,"tags":152515,"author":-1,"cover":152516,"excerpt":-1},"read-json-data-spring-boot-write-database","2017-07-05T12:00:27-04:00",[11002],"./pexels-photo-374899-760x506.jpeg",{"type":19,"value":152518,"toc":153814},[152519,152525,152530,152533,152535,152538,152542,152546,152559,152778,152781,152927,153025,153097,153192,153196,153199,153329,153332,153481,153485,153496,153502,153509,153512,153535,153538,153785,153793,153799,153801,153804,153811],[22,152520,150299,152521,152524],{},[677,152522,56768],{"href":152523},"/courses",". If you have a question you would like me to answer please feel free to reach out and ask me. The question I am answering today is:",[29685,152526,152527],{},[22,152528,152529],{},"How can I read in a JSON file in Spring Boot and save the records to a database?",[22,152531,152532],{},"If you have ever had to read and write JSON data you know that this can be tricky. First, you have to read in the JSON data and mapping that data to your domain model isn't always easy. Once you have that in place you need to take that data and save it off to a database. In this tutorial, we will look at a quick and easy way to do this in Spring Boot.",[26,152534,51884],{"id":51883},[22,152536,152537],{},"The first thing you need to do is to create a new Spring Boot application using the Spring Initalizr. This will create a new Spring Boot application and you will want to select the following dependencies:",[22,152539,152540],{},[653,152541],{"alt":61699,"src":61700},[636,152543,152545],{"id":152544},"json-data","JSON Data",[22,152547,152548,152549,152552,152553,152558],{},"Next you need some sample JSON data and one of my favorite services for doing this is ",[677,152550,77988],{"href":43711,"rel":152551},[681],". We are going to grab a ",[677,152554,152557],{"href":152555,"rel":152556},"https://jsonplaceholder.typicode.com/users",[681],"list of users"," and save that to a file called users.json and place it inside of /src/main/resources/json/. Each JSON record for a user will look something like this.",[52,152560,152562],{"className":3671,"code":152561,"language":3673,"meta":57,"style":57}," {\n \"id\": 1,\n \"name\": \"Leanne Graham\",\n \"username\": \"Bret\",\n \"email\": \"Sincere@april.biz\",\n \"address\": {\n \"street\": \"Kulas Light\",\n \"suite\": \"Apt. 556\",\n \"city\": \"Gwenborough\",\n \"zipcode\": \"92998-3874\",\n \"geo\": {\n \"lat\": \"-37.3159\",\n \"lng\": \"81.1496\"\n }\n },\n \"phone\": \"1-770-736-8031 x56442\",\n \"website\": \"hildegard.org\",\n \"company\": {\n \"name\": \"Romaguera-Crona\",\n \"catchPhrase\": \"Multi-layered client-server neural-net\",\n \"bs\": \"harness real-time e-markets\"\n }\n },\n",[59,152563,152564,152569,152579,152590,152602,152614,152621,152633,152645,152657,152669,152676,152688,152698,152702,152706,152718,152730,152737,152748,152760,152770,152774],{"__ignoreMap":57},[62,152565,152566],{"class":64,"line":65},[62,152567,152568],{"class":72}," {\n",[62,152570,152571,152573,152575,152577],{"class":64,"line":76},[62,152572,85256],{"class":149},[62,152574,3696],{"class":72},[62,152576,6689],{"class":149},[62,152578,3338],{"class":72},[62,152580,152581,152583,152585,152588],{"class":64,"line":82},[62,152582,49836],{"class":149},[62,152584,3696],{"class":72},[62,152586,152587],{"class":1675},"\"Leanne Graham\"",[62,152589,3338],{"class":72},[62,152591,152592,152595,152597,152600],{"class":64,"line":89},[62,152593,152594],{"class":149}," \"username\"",[62,152596,3696],{"class":72},[62,152598,152599],{"class":1675},"\"Bret\"",[62,152601,3338],{"class":72},[62,152603,152604,152607,152609,152612],{"class":64,"line":95},[62,152605,152606],{"class":149}," \"email\"",[62,152608,3696],{"class":72},[62,152610,152611],{"class":1675},"\"Sincere@april.biz\"",[62,152613,3338],{"class":72},[62,152615,152616,152619],{"class":64,"line":101},[62,152617,152618],{"class":149}," \"address\"",[62,152620,3688],{"class":72},[62,152622,152623,152626,152628,152631],{"class":64,"line":107},[62,152624,152625],{"class":149}," \"street\"",[62,152627,3696],{"class":72},[62,152629,152630],{"class":1675},"\"Kulas Light\"",[62,152632,3338],{"class":72},[62,152634,152635,152638,152640,152643],{"class":64,"line":113},[62,152636,152637],{"class":149}," \"suite\"",[62,152639,3696],{"class":72},[62,152641,152642],{"class":1675},"\"Apt. 556\"",[62,152644,3338],{"class":72},[62,152646,152647,152650,152652,152655],{"class":64,"line":129},[62,152648,152649],{"class":149}," \"city\"",[62,152651,3696],{"class":72},[62,152653,152654],{"class":1675},"\"Gwenborough\"",[62,152656,3338],{"class":72},[62,152658,152659,152662,152664,152667],{"class":64,"line":134},[62,152660,152661],{"class":149}," \"zipcode\"",[62,152663,3696],{"class":72},[62,152665,152666],{"class":1675},"\"92998-3874\"",[62,152668,3338],{"class":72},[62,152670,152671,152674],{"class":64,"line":156},[62,152672,152673],{"class":149}," \"geo\"",[62,152675,3688],{"class":72},[62,152677,152678,152681,152683,152686],{"class":64,"line":161},[62,152679,152680],{"class":149}," \"lat\"",[62,152682,3696],{"class":72},[62,152684,152685],{"class":1675},"\"-37.3159\"",[62,152687,3338],{"class":72},[62,152689,152690,152693,152695],{"class":64,"line":167},[62,152691,152692],{"class":149}," \"lng\"",[62,152694,3696],{"class":72},[62,152696,152697],{"class":1675},"\"81.1496\"\n",[62,152699,152700],{"class":64,"line":173},[62,152701,29042],{"class":72},[62,152703,152704],{"class":64,"line":179},[62,152705,50124],{"class":72},[62,152707,152708,152711,152713,152716],{"class":64,"line":185},[62,152709,152710],{"class":149}," \"phone\"",[62,152712,3696],{"class":72},[62,152714,152715],{"class":1675},"\"1-770-736-8031 x56442\"",[62,152717,3338],{"class":72},[62,152719,152720,152723,152725,152728],{"class":64,"line":191},[62,152721,152722],{"class":149}," \"website\"",[62,152724,3696],{"class":72},[62,152726,152727],{"class":1675},"\"hildegard.org\"",[62,152729,3338],{"class":72},[62,152731,152732,152735],{"class":64,"line":209},[62,152733,152734],{"class":149}," \"company\"",[62,152736,3688],{"class":72},[62,152738,152739,152741,152743,152746],{"class":64,"line":220},[62,152740,87990],{"class":149},[62,152742,3696],{"class":72},[62,152744,152745],{"class":1675},"\"Romaguera-Crona\"",[62,152747,3338],{"class":72},[62,152749,152750,152753,152755,152758],{"class":64,"line":226},[62,152751,152752],{"class":149}," \"catchPhrase\"",[62,152754,3696],{"class":72},[62,152756,152757],{"class":1675},"\"Multi-layered client-server neural-net\"",[62,152759,3338],{"class":72},[62,152761,152762,152765,152767],{"class":64,"line":231},[62,152763,152764],{"class":149}," \"bs\"",[62,152766,3696],{"class":72},[62,152768,152769],{"class":1675},"\"harness real-time e-markets\"\n",[62,152771,152772],{"class":64,"line":236},[62,152773,223],{"class":72},[62,152775,152776],{"class":64,"line":242},[62,152777,32848],{"class":72},[22,152779,152780],{},"Now that you have a list of users saved you need to model a domain after a user. You could create relationships between each of your domain models but to keep this simple I would just store all of this data in a single user table. To accomplish this you are going to use an embedded address and company domain.",[52,152782,152784],{"className":54,"code":152783,"language":56,"meta":57,"style":57},"@Data\n@AllArgsConstructor\n@Entity\npublic class User {\n\n @Id\n @GeneratedValue( strategy = GenerationType.AUTO )\n private Long id;\n private String name;\n private String username;\n private String email;\n private String phone;\n private String website;\n\n @Embedded\n private Address address;\n @Embedded\n private Company company;\n\n public User() {}\n\n}\n",[59,152785,152786,152792,152799,152805,152815,152819,152825,152840,152846,152852,152858,152864,152871,152877,152881,152888,152894,152900,152907,152911,152919,152923],{"__ignoreMap":57},[62,152787,152788,152790],{"class":64,"line":65},[62,152789,942],{"class":72},[62,152791,9388],{"class":68},[62,152793,152794,152796],{"class":64,"line":76},[62,152795,942],{"class":72},[62,152797,152798],{"class":68},"AllArgsConstructor\n",[62,152800,152801,152803],{"class":64,"line":82},[62,152802,942],{"class":72},[62,152804,8999],{"class":68},[62,152806,152807,152809,152811,152813],{"class":64,"line":89},[62,152808,116],{"class":68},[62,152810,119],{"class":68},[62,152812,22289],{"class":122},[62,152814,126],{"class":72},[62,152816,152817],{"class":64,"line":95},[62,152818,79],{"emptyLinePlaceholder":13},[62,152820,152821,152823],{"class":64,"line":101},[62,152822,2143],{"class":72},[62,152824,22298],{"class":68},[62,152826,152827,152829,152831,152833,152835,152837],{"class":64,"line":107},[62,152828,2143],{"class":72},[62,152830,89214],{"class":68},[62,152832,52630],{"class":72},[62,152834,89219],{"class":149},[62,152836,2556],{"class":68},[62,152838,152839],{"class":72}," GenerationType.AUTO )\n",[62,152841,152842,152844],{"class":64,"line":113},[62,152843,137],{"class":68},[62,152845,9029],{"class":72},[62,152847,152848,152850],{"class":64,"line":129},[62,152849,137],{"class":68},[62,152851,9406],{"class":72},[62,152853,152854,152856],{"class":64,"line":134},[62,152855,137],{"class":68},[62,152857,67488],{"class":72},[62,152859,152860,152862],{"class":64,"line":156},[62,152861,137],{"class":68},[62,152863,22360],{"class":72},[62,152865,152866,152868],{"class":64,"line":161},[62,152867,137],{"class":68},[62,152869,152870],{"class":72}," String phone;\n",[62,152872,152873,152875],{"class":64,"line":167},[62,152874,137],{"class":68},[62,152876,45445],{"class":72},[62,152878,152879],{"class":64,"line":173},[62,152880,79],{"emptyLinePlaceholder":13},[62,152882,152883,152885],{"class":64,"line":179},[62,152884,2143],{"class":72},[62,152886,152887],{"class":68},"Embedded\n",[62,152889,152890,152892],{"class":64,"line":185},[62,152891,137],{"class":68},[62,152893,101404],{"class":72},[62,152895,152896,152898],{"class":64,"line":191},[62,152897,2143],{"class":72},[62,152899,152887],{"class":68},[62,152901,152902,152904],{"class":64,"line":209},[62,152903,137],{"class":68},[62,152905,152906],{"class":72}," Company company;\n",[62,152908,152909],{"class":64,"line":220},[62,152910,79],{"emptyLinePlaceholder":13},[62,152912,152913,152915,152917],{"class":64,"line":226},[62,152914,194],{"class":68},[62,152916,22289],{"class":122},[62,152918,58809],{"class":72},[62,152920,152921],{"class":64,"line":231},[62,152922,79],{"emptyLinePlaceholder":13},[62,152924,152925],{"class":64,"line":236},[62,152926,379],{"class":72},[52,152928,152930],{"className":54,"code":152929,"language":56,"meta":57,"style":57},"@Data\n@AllArgsConstructor\n@Embeddable\npublic class Address {\n\n private String street;\n private String suite;\n private String city;\n private String zipcode;\n\n @Embedded\n private Geo geo;\n\n public Address() {}\n}\n",[59,152931,152932,152938,152944,152951,152961,152965,152972,152979,152985,152992,152996,153002,153009,153013,153021],{"__ignoreMap":57},[62,152933,152934,152936],{"class":64,"line":65},[62,152935,942],{"class":72},[62,152937,9388],{"class":68},[62,152939,152940,152942],{"class":64,"line":76},[62,152941,942],{"class":72},[62,152943,152798],{"class":68},[62,152945,152946,152948],{"class":64,"line":82},[62,152947,942],{"class":72},[62,152949,152950],{"class":68},"Embeddable\n",[62,152952,152953,152955,152957,152959],{"class":64,"line":89},[62,152954,116],{"class":68},[62,152956,119],{"class":68},[62,152958,101554],{"class":122},[62,152960,126],{"class":72},[62,152962,152963],{"class":64,"line":95},[62,152964,79],{"emptyLinePlaceholder":13},[62,152966,152967,152969],{"class":64,"line":101},[62,152968,137],{"class":68},[62,152970,152971],{"class":72}," String street;\n",[62,152973,152974,152976],{"class":64,"line":107},[62,152975,137],{"class":68},[62,152977,152978],{"class":72}," String suite;\n",[62,152980,152981,152983],{"class":64,"line":113},[62,152982,137],{"class":68},[62,152984,101590],{"class":72},[62,152986,152987,152989],{"class":64,"line":129},[62,152988,137],{"class":68},[62,152990,152991],{"class":72}," String zipcode;\n",[62,152993,152994],{"class":64,"line":134},[62,152995,79],{"emptyLinePlaceholder":13},[62,152997,152998,153000],{"class":64,"line":156},[62,152999,2143],{"class":72},[62,153001,152887],{"class":68},[62,153003,153004,153006],{"class":64,"line":161},[62,153005,137],{"class":68},[62,153007,153008],{"class":72}," Geo geo;\n",[62,153010,153011],{"class":64,"line":167},[62,153012,79],{"emptyLinePlaceholder":13},[62,153014,153015,153017,153019],{"class":64,"line":173},[62,153016,194],{"class":68},[62,153018,101554],{"class":122},[62,153020,58809],{"class":72},[62,153022,153023],{"class":64,"line":179},[62,153024,379],{"class":72},[52,153026,153028],{"className":54,"code":153027,"language":56,"meta":57,"style":57},"@Data\n@AllArgsConstructor\n@Embeddable\npublic class Geo {\n\n private String lat;\n private String lng;\n\n public Geo() {}\n\n}\n",[59,153029,153030,153036,153042,153048,153059,153063,153070,153077,153081,153089,153093],{"__ignoreMap":57},[62,153031,153032,153034],{"class":64,"line":65},[62,153033,942],{"class":72},[62,153035,9388],{"class":68},[62,153037,153038,153040],{"class":64,"line":76},[62,153039,942],{"class":72},[62,153041,152798],{"class":68},[62,153043,153044,153046],{"class":64,"line":82},[62,153045,942],{"class":72},[62,153047,152950],{"class":68},[62,153049,153050,153052,153054,153057],{"class":64,"line":89},[62,153051,116],{"class":68},[62,153053,119],{"class":68},[62,153055,153056],{"class":122}," Geo",[62,153058,126],{"class":72},[62,153060,153061],{"class":64,"line":95},[62,153062,79],{"emptyLinePlaceholder":13},[62,153064,153065,153067],{"class":64,"line":101},[62,153066,137],{"class":68},[62,153068,153069],{"class":72}," String lat;\n",[62,153071,153072,153074],{"class":64,"line":107},[62,153073,137],{"class":68},[62,153075,153076],{"class":72}," String lng;\n",[62,153078,153079],{"class":64,"line":113},[62,153080,79],{"emptyLinePlaceholder":13},[62,153082,153083,153085,153087],{"class":64,"line":129},[62,153084,194],{"class":68},[62,153086,153056],{"class":122},[62,153088,58809],{"class":72},[62,153090,153091],{"class":64,"line":134},[62,153092,79],{"emptyLinePlaceholder":13},[62,153094,153095],{"class":64,"line":156},[62,153096,379],{"class":72},[52,153098,153100],{"className":54,"code":153099,"language":56,"meta":57,"style":57},"@Data\n@AllArgsConstructor\n@Embeddable\npublic class Company {\n\n @Column( name = \"company_name\")\n private String name;\n private String catchPhrase;\n private String bs;\n\n public Company() {}\n\n}\n",[59,153101,153102,153108,153114,153120,153131,153135,153152,153158,153165,153172,153176,153184,153188],{"__ignoreMap":57},[62,153103,153104,153106],{"class":64,"line":65},[62,153105,942],{"class":72},[62,153107,9388],{"class":68},[62,153109,153110,153112],{"class":64,"line":76},[62,153111,942],{"class":72},[62,153113,152798],{"class":68},[62,153115,153116,153118],{"class":64,"line":82},[62,153117,942],{"class":72},[62,153119,152950],{"class":68},[62,153121,153122,153124,153126,153129],{"class":64,"line":89},[62,153123,116],{"class":68},[62,153125,119],{"class":68},[62,153127,153128],{"class":122}," Company",[62,153130,126],{"class":72},[62,153132,153133],{"class":64,"line":95},[62,153134,79],{"emptyLinePlaceholder":13},[62,153136,153137,153139,153141,153143,153145,153147,153150],{"class":64,"line":101},[62,153138,2143],{"class":72},[62,153140,45385],{"class":68},[62,153142,52630],{"class":72},[62,153144,3107],{"class":149},[62,153146,2556],{"class":68},[62,153148,153149],{"class":1675}," \"company_name\"",[62,153151,2212],{"class":72},[62,153153,153154,153156],{"class":64,"line":107},[62,153155,137],{"class":68},[62,153157,9406],{"class":72},[62,153159,153160,153162],{"class":64,"line":113},[62,153161,137],{"class":68},[62,153163,153164],{"class":72}," String catchPhrase;\n",[62,153166,153167,153169],{"class":64,"line":129},[62,153168,137],{"class":68},[62,153170,153171],{"class":72}," String bs;\n",[62,153173,153174],{"class":64,"line":134},[62,153175,79],{"emptyLinePlaceholder":13},[62,153177,153178,153180,153182],{"class":64,"line":156},[62,153179,194],{"class":68},[62,153181,153128],{"class":122},[62,153183,58809],{"class":72},[62,153185,153186],{"class":64,"line":161},[62,153187,79],{"emptyLinePlaceholder":13},[62,153189,153190],{"class":64,"line":167},[62,153191,379],{"class":72},[636,153193,153195],{"id":153194},"spring-boot-rest-application","Spring Boot REST Application",[22,153197,153198],{},"Now that you have our domain model in place you are going to build out a REST controller that uses a service & Repository to list and save data.",[52,153200,153202],{"className":54,"code":153201,"language":56,"meta":57,"style":57},"@RestController\n@RequestMapping(\"/users\")\npublic class UserController {\n\n private final UserService userService;\n\n public UserController(UserService userService) {\n this.userService = userService;\n }\n\n @GetMapping(\"/list\")\n public Iterable\u003CUser> list() {\n return userService.list();\n }\n}\n",[59,153203,153204,153210,153223,153233,153237,153246,153250,153264,153276,153280,153284,153296,153310,153321,153325],{"__ignoreMap":57},[62,153205,153206,153208],{"class":64,"line":65},[62,153207,942],{"class":72},[62,153209,2342],{"class":68},[62,153211,153212,153214,153216,153218,153221],{"class":64,"line":76},[62,153213,942],{"class":72},[62,153215,10592],{"class":68},[62,153217,2109],{"class":72},[62,153219,153220],{"class":1675},"\"/users\"",[62,153222,2212],{"class":72},[62,153224,153225,153227,153229,153231],{"class":64,"line":82},[62,153226,116],{"class":68},[62,153228,119],{"class":68},[62,153230,22436],{"class":122},[62,153232,126],{"class":72},[62,153234,153235],{"class":64,"line":89},[62,153236,79],{"emptyLinePlaceholder":13},[62,153238,153239,153241,153243],{"class":64,"line":95},[62,153240,137],{"class":68},[62,153242,458],{"class":68},[62,153244,153245],{"class":72}," UserService userService;\n",[62,153247,153248],{"class":64,"line":101},[62,153249,79],{"emptyLinePlaceholder":13},[62,153251,153252,153254,153256,153259,153262],{"class":64,"line":107},[62,153253,194],{"class":68},[62,153255,22436],{"class":122},[62,153257,153258],{"class":72},"(UserService ",[62,153260,153261],{"class":889},"userService",[62,153263,768],{"class":72},[62,153265,153266,153268,153271,153273],{"class":64,"line":113},[62,153267,2405],{"class":149},[62,153269,153270],{"class":72},".userService ",[62,153272,146],{"class":68},[62,153274,153275],{"class":72}," userService;\n",[62,153277,153278],{"class":64,"line":129},[62,153279,223],{"class":72},[62,153281,153282],{"class":64,"line":134},[62,153283,79],{"emptyLinePlaceholder":13},[62,153285,153286,153288,153290,153292,153294],{"class":64,"line":156},[62,153287,2143],{"class":72},[62,153289,2548],{"class":68},[62,153291,2109],{"class":72},[62,153293,66267],{"class":1675},[62,153295,2212],{"class":72},[62,153297,153298,153300,153302,153304,153306,153308],{"class":64,"line":161},[62,153299,194],{"class":68},[62,153301,57781],{"class":72},[62,153303,22260],{"class":68},[62,153305,3135],{"class":72},[62,153307,38739],{"class":122},[62,153309,206],{"class":72},[62,153311,153312,153314,153317,153319],{"class":64,"line":167},[62,153313,360],{"class":68},[62,153315,153316],{"class":72}," userService.",[62,153318,38739],{"class":122},[62,153320,822],{"class":72},[62,153322,153323],{"class":64,"line":173},[62,153324,223],{"class":72},[62,153326,153327],{"class":64,"line":179},[62,153328,379],{"class":72},[22,153330,153331],{},"The important thing about our service here is that it takes a list of users and calls our repository to save them all at once.",[52,153333,153335],{"className":54,"code":153334,"language":56,"meta":57,"style":57},"@Service\npublic class UserService {\n\n private final UserRepository userRepository;\n\n public UserService(UserRepository userRepository) {\n this.userRepository = userRepository;\n }\n\n public Iterable\u003CUser> list() {\n return userRepository.findAll();\n }\n\n public Iterable\u003CUser> save(List\u003CUser> users) {\n return userRepository.save(users);\n }\n\n}\n",[59,153336,153337,153343,153353,153357,153366,153370,153382,153394,153398,153402,153416,153427,153431,153435,153458,153469,153473,153477],{"__ignoreMap":57},[62,153338,153339,153341],{"class":64,"line":65},[62,153340,942],{"class":72},[62,153342,945],{"class":68},[62,153344,153345,153347,153349,153351],{"class":64,"line":76},[62,153346,116],{"class":68},[62,153348,119],{"class":68},[62,153350,27265],{"class":122},[62,153352,126],{"class":72},[62,153354,153355],{"class":64,"line":82},[62,153356,79],{"emptyLinePlaceholder":13},[62,153358,153359,153361,153363],{"class":64,"line":89},[62,153360,137],{"class":68},[62,153362,458],{"class":68},[62,153364,153365],{"class":72}," UserRepository userRepository;\n",[62,153367,153368],{"class":64,"line":95},[62,153369,79],{"emptyLinePlaceholder":13},[62,153371,153372,153374,153376,153378,153380],{"class":64,"line":101},[62,153373,194],{"class":68},[62,153375,27265],{"class":122},[62,153377,151026],{"class":72},[62,153379,151029],{"class":889},[62,153381,768],{"class":72},[62,153383,153384,153386,153389,153391],{"class":64,"line":107},[62,153385,2405],{"class":149},[62,153387,153388],{"class":72},".userRepository ",[62,153390,146],{"class":68},[62,153392,153393],{"class":72}," userRepository;\n",[62,153395,153396],{"class":64,"line":113},[62,153397,223],{"class":72},[62,153399,153400],{"class":64,"line":129},[62,153401,79],{"emptyLinePlaceholder":13},[62,153403,153404,153406,153408,153410,153412,153414],{"class":64,"line":134},[62,153405,194],{"class":68},[62,153407,57781],{"class":72},[62,153409,22260],{"class":68},[62,153411,3135],{"class":72},[62,153413,38739],{"class":122},[62,153415,206],{"class":72},[62,153417,153418,153420,153423,153425],{"class":64,"line":156},[62,153419,360],{"class":68},[62,153421,153422],{"class":72}," userRepository.",[62,153424,10287],{"class":122},[62,153426,822],{"class":72},[62,153428,153429],{"class":64,"line":161},[62,153430,223],{"class":72},[62,153432,153433],{"class":64,"line":167},[62,153434,79],{"emptyLinePlaceholder":13},[62,153436,153437,153439,153441,153443,153445,153447,153450,153452,153454,153456],{"class":64,"line":173},[62,153438,194],{"class":68},[62,153440,57781],{"class":72},[62,153442,22260],{"class":68},[62,153444,3135],{"class":72},[62,153446,22562],{"class":122},[62,153448,153449],{"class":72},"(List\u003C",[62,153451,22260],{"class":68},[62,153453,3135],{"class":72},[62,153455,96112],{"class":889},[62,153457,768],{"class":72},[62,153459,153460,153462,153464,153466],{"class":64,"line":179},[62,153461,360],{"class":68},[62,153463,153422],{"class":72},[62,153465,22562],{"class":122},[62,153467,153468],{"class":72},"(users);\n",[62,153470,153471],{"class":64,"line":185},[62,153472,223],{"class":72},[62,153474,153475],{"class":64,"line":191},[62,153476,79],{"emptyLinePlaceholder":13},[62,153478,153479],{"class":64,"line":209},[62,153480,379],{"class":72},[636,153482,153484],{"id":153483},"read-write-json-data-to-database","Read & Write JSON Data to Database",[22,153486,153487,153488,153492,153493,153495],{},"Before you can write to a database you need to make sure that you have a database configured. In this tutorial you are using an in-memory H2 database. The in-memory part just means that each time we restart the application the database will be wiped clean. If you're new H2 you can check out ",[677,153489,95884],{"href":153490,"rel":153491},"https://youtu.be/tSJW5NKPhcM",[681],". For now you will need to open up ",[59,153494,1265],{}," and add the following lines:",[52,153497,153500],{"className":153498,"code":153499,"language":1727},[1725],"spring.h2.console.enabled=true\nspring.datasource.generate-unique-name=false\nspring.datasource.name=users\n",[59,153501,153499],{"__ignoreMap":57},[22,153503,153504,153505,2755],{},"With our application in place, there is only one step left to do. To read the JSON and write it to a database you are going to use a command-line runner. If you're not familiar with a command-line runner it is an interface that you can implement to executed some code before the application starts up. If you want to read more on this you can check out ",[677,153506,153508],{"href":153507},"/blog/2017/04/07/spring-boot-command-line-runner/","my blog post here",[22,153510,153511],{},"When you bring in the Web dependency you also get the jackson-databind dependency. This contains an Object Mapper class which allows us to easily map JSON data to our domain model.",[52,153513,153515],{"className":1769,"code":153514,"language":1771,"meta":57,"style":57},"\u003Cdependency>\n \u003CgroupId>com.fasterxml.jackson.core\u003C/groupId>\n \u003CartifactId>jackson-databind\u003C/artifactId>\n\u003C/dependency>\n",[59,153516,153517,153521,153526,153531],{"__ignoreMap":57},[62,153518,153519],{"class":64,"line":65},[62,153520,46425],{},[62,153522,153523],{"class":64,"line":76},[62,153524,153525],{}," \u003CgroupId>com.fasterxml.jackson.core\u003C/groupId>\n",[62,153527,153528],{"class":64,"line":82},[62,153529,153530],{}," \u003CartifactId>jackson-databind\u003C/artifactId>\n",[62,153532,153533],{"class":64,"line":89},[62,153534,46445],{},[22,153536,153537],{},"Using that Object Mapper and our well-crafted domain model from above we have what we need to accomplish our goal. You can read in users.json file and then map that data to the domain model.",[52,153539,153541],{"className":54,"code":153540,"language":56,"meta":57,"style":57},"@SpringBootApplication\npublic class JsondbApplication {\n\n public static void main(String[] args) {\n SpringApplication.run(JsondbApplication.class, args);\n }\n\n @Bean\n CommandLineRunner runner(UserService userService) {\n return args -> {\n // read json and write to db\n ObjectMapper mapper = new ObjectMapper();\n TypeReference\u003CList\u003CUser>> typeReference = new TypeReference\u003CList\u003CUser>>(){};\n InputStream inputStream = TypeReference.class.getResourceAsStream(\"/json/users.json\");\n try {\n List\u003CUser> users = mapper.readValue(inputStream,typeReference);\n userService.save(users);\n System.out.println(\"Users Saved!\");\n } catch (IOException e){\n System.out.println(\"Unable to save users: \" + e.getMessage());\n }\n };\n }\n}\n",[59,153542,153543,153549,153560,153564,153584,153593,153597,153601,153607,153619,153629,153634,153648,153670,153690,153696,153716,153725,153738,153750,153769,153773,153777,153781],{"__ignoreMap":57},[62,153544,153545,153547],{"class":64,"line":65},[62,153546,942],{"class":72},[62,153548,2079],{"class":68},[62,153550,153551,153553,153555,153558],{"class":64,"line":76},[62,153552,116],{"class":68},[62,153554,119],{"class":68},[62,153556,153557],{"class":122}," JsondbApplication",[62,153559,126],{"class":72},[62,153561,153562],{"class":64,"line":82},[62,153563,79],{"emptyLinePlaceholder":13},[62,153565,153566,153568,153570,153572,153574,153576,153578,153580,153582],{"class":64,"line":89},[62,153567,194],{"class":68},[62,153569,2101],{"class":68},[62,153571,200],{"class":68},[62,153573,2106],{"class":122},[62,153575,2109],{"class":72},[62,153577,973],{"class":68},[62,153579,2114],{"class":72},[62,153581,2117],{"class":889},[62,153583,768],{"class":72},[62,153585,153586,153588,153590],{"class":64,"line":95},[62,153587,2124],{"class":72},[62,153589,2127],{"class":122},[62,153591,153592],{"class":72},"(JsondbApplication.class, args);\n",[62,153594,153595],{"class":64,"line":101},[62,153596,223],{"class":72},[62,153598,153599],{"class":64,"line":107},[62,153600,79],{"emptyLinePlaceholder":13},[62,153602,153603,153605],{"class":64,"line":113},[62,153604,2143],{"class":72},[62,153606,2146],{"class":68},[62,153608,153609,153611,153613,153615,153617],{"class":64,"line":129},[62,153610,2151],{"class":72},[62,153612,52603],{"class":122},[62,153614,153258],{"class":72},[62,153616,153261],{"class":889},[62,153618,768],{"class":72},[62,153620,153621,153623,153625,153627],{"class":64,"line":134},[62,153622,360],{"class":68},[62,153624,2169],{"class":72},[62,153626,800],{"class":68},[62,153628,126],{"class":72},[62,153630,153631],{"class":64,"line":156},[62,153632,153633],{"class":85}," // read json and write to db\n",[62,153635,153636,153639,153641,153643,153646],{"class":64,"line":161},[62,153637,153638],{"class":72}," ObjectMapper mapper ",[62,153640,146],{"class":68},[62,153642,466],{"class":68},[62,153644,153645],{"class":122}," ObjectMapper",[62,153647,822],{"class":72},[62,153649,153650,153653,153655,153658,153660,153662,153665,153667],{"class":64,"line":167},[62,153651,153652],{"class":72}," TypeReference\u003CList\u003C",[62,153654,22260],{"class":68},[62,153656,153657],{"class":72},">> typeReference ",[62,153659,146],{"class":68},[62,153661,466],{"class":68},[62,153663,153664],{"class":72}," TypeReference\u003CList\u003C",[62,153666,22260],{"class":68},[62,153668,153669],{"class":72},">>(){};\n",[62,153671,153672,153675,153677,153680,153683,153685,153688],{"class":64,"line":173},[62,153673,153674],{"class":72}," InputStream inputStream ",[62,153676,146],{"class":68},[62,153678,153679],{"class":72}," TypeReference.class.",[62,153681,153682],{"class":122},"getResourceAsStream",[62,153684,2109],{"class":72},[62,153686,153687],{"class":1675},"\"/json/users.json\"",[62,153689,1133],{"class":72},[62,153691,153692,153694],{"class":64,"line":179},[62,153693,21692],{"class":68},[62,153695,126],{"class":72},[62,153697,153698,153700,153702,153705,153707,153710,153713],{"class":64,"line":185},[62,153699,6355],{"class":72},[62,153701,22260],{"class":68},[62,153703,153704],{"class":72},"> users ",[62,153706,146],{"class":68},[62,153708,153709],{"class":72}," mapper.",[62,153711,153712],{"class":122},"readValue",[62,153714,153715],{"class":72},"(inputStream,typeReference);\n",[62,153717,153718,153721,153723],{"class":64,"line":191},[62,153719,153720],{"class":72}," userService.",[62,153722,22562],{"class":122},[62,153724,153468],{"class":72},[62,153726,153727,153729,153731,153733,153736],{"class":64,"line":209},[62,153728,5065],{"class":72},[62,153730,2244],{"class":122},[62,153732,2109],{"class":72},[62,153734,153735],{"class":1675},"\"Users Saved!\"",[62,153737,1133],{"class":72},[62,153739,153740,153742,153744,153746,153748],{"class":64,"line":220},[62,153741,21862],{"class":72},[62,153743,883],{"class":68},[62,153745,28437],{"class":72},[62,153747,890],{"class":889},[62,153749,34126],{"class":72},[62,153751,153752,153754,153756,153758,153761,153763,153765,153767],{"class":64,"line":226},[62,153753,5065],{"class":72},[62,153755,2244],{"class":122},[62,153757,2109],{"class":72},[62,153759,153760],{"class":1675},"\"Unable to save users: \"",[62,153762,4507],{"class":68},[62,153764,69416],{"class":72},[62,153766,28459],{"class":122},[62,153768,1091],{"class":72},[62,153770,153771],{"class":64,"line":231},[62,153772,861],{"class":72},[62,153774,153775],{"class":64,"line":236},[62,153776,2252],{"class":72},[62,153778,153779],{"class":64,"line":242},[62,153780,223],{"class":72},[62,153782,153783],{"class":64,"line":247},[62,153784,379],{"class":72},[22,153786,153787,153788,153792],{},"If you run our application and look visit the ",[677,153789,153791],{"href":52672,"rel":153790},[681],"H2 Database console"," you should see the 10 records have been inserted. Congratulations, you just read data from a JSON file and inserted it into a database.",[22,153794,153795],{},[653,153796],{"alt":153797,"src":153798},"Spring Boot H2 Console","./spring-boot-h2-console.png",[26,153800,1499],{"id":1498},[22,153802,153803],{},"I often get quetions like the one we looked at today. The first thing you need to do is to break down this problem into smaller pieces. The first problem to solve for was creating some sample JSON, modeling our domain model after it and reading it in. Once you have that you can move on to persisting that data into a database.",[22,153805,153806,153807,2755],{},"If you want to check out the source code head over to ",[677,153808,50830],{"href":153809,"rel":153810},"https://github.com/cfaddict/spring-boot-jsontodb",[681],[1527,153812,153813],{},"html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .spoOn, html code.shiki .spoOn{--shiki-default:#E36209;--shiki-dark:#FFAB70;--shiki-sepia:#E36209}html pre.shiki code .s4-Ip, html code.shiki .s4-Ip{--shiki-default:#6A737D;--shiki-dark:#6A737D;--shiki-sepia:#6A737D}",{"title":57,"searchDepth":76,"depth":76,"links":153815},[153816,153821],{"id":51883,"depth":76,"text":51884,"children":153817},[153818,153819,153820],{"id":152544,"depth":82,"text":152545},{"id":153194,"depth":82,"text":153195},{"id":153483,"depth":82,"text":153484},{"id":1498,"depth":76,"text":1499},{"_id":153823,"path":153824,"title":153825,"description":153825,"meta":153826,"body":153831},"content/blog/2017/07/03/multiple-request-mappings-spring-boot.md","/blog/2017/07/03/multiple-request-mappings-spring-boot","Multiple Request Mappings in Spring Boot",{"slug":153827,"date":153828,"published":13,"tags":153829,"author":-1,"cover":153830,"excerpt":-1},"multiple-request-mappings-spring-boot","2017-07-03T08:30:50-04:00",[11002],"./pexels-photo-450035-760x506.jpeg",{"type":19,"value":153832,"toc":154232},[153833,153839,153844,153848,153856,153949,153956,154061,154064,154096,154099,154197,154209,154213,154219,154221,154224,154229],[22,153834,150299,153835,153838],{},[677,153836,56768],{"href":150302,"rel":153837},[681],". The question was related to building out Spring Controllers and how to define multiple request mappings for a single method.",[29685,153840,153841],{},[22,153842,153843],{},"\"How can I create multiple request mappings for a single method?\"",[26,153845,153847],{"id":153846},"multiple-request-mappings","Multiple Request Mappings",[22,153849,153850,153851,153855],{},"If you want to follow along with this tutorial you can create a simple Spring Boot project that includes the Web dependency. We know that we can use the ",[677,153852,56483],{"href":153853,"rel":153854},"https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/bind/annotation/RequestMapping.html",[681]," annotation to map web requests onto specific handler classes and/or handler methods. If we wanted to map our home method to the root \"/\" we can do so using the following code. ",[52,153857,153859],{"className":54,"code":153858,"language":56,"meta":57,"style":57},"package com.therealdanvega;\n\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class HomeController {\n\n @RequestMapping(\"/\")\n public String home() {\n return \"Hello, World!\";\n }\n\n}\n\n",[59,153860,153861,153867,153871,153877,153883,153887,153893,153903,153907,153919,153929,153937,153941,153945],{"__ignoreMap":57},[62,153862,153863,153865],{"class":64,"line":65},[62,153864,69],{"class":68},[62,153866,52481],{"class":72},[62,153868,153869],{"class":64,"line":76},[62,153870,79],{"emptyLinePlaceholder":13},[62,153872,153873,153875],{"class":64,"line":82},[62,153874,27875],{"class":68},[62,153876,49517],{"class":72},[62,153878,153879,153881],{"class":64,"line":89},[62,153880,27875],{"class":68},[62,153882,27892],{"class":72},[62,153884,153885],{"class":64,"line":95},[62,153886,79],{"emptyLinePlaceholder":13},[62,153888,153889,153891],{"class":64,"line":101},[62,153890,942],{"class":72},[62,153892,2342],{"class":68},[62,153894,153895,153897,153899,153901],{"class":64,"line":107},[62,153896,116],{"class":68},[62,153898,119],{"class":68},[62,153900,25636],{"class":122},[62,153902,126],{"class":72},[62,153904,153905],{"class":64,"line":113},[62,153906,79],{"emptyLinePlaceholder":13},[62,153908,153909,153911,153913,153915,153917],{"class":64,"line":129},[62,153910,2143],{"class":72},[62,153912,10592],{"class":68},[62,153914,2109],{"class":72},[62,153916,15635],{"class":1675},[62,153918,2212],{"class":72},[62,153920,153921,153923,153925,153927],{"class":64,"line":134},[62,153922,194],{"class":68},[62,153924,2469],{"class":72},[62,153926,18647],{"class":122},[62,153928,206],{"class":72},[62,153930,153931,153933,153935],{"class":64,"line":156},[62,153932,360],{"class":68},[62,153934,80556],{"class":1675},[62,153936,153],{"class":72},[62,153938,153939],{"class":64,"line":161},[62,153940,223],{"class":72},[62,153942,153943],{"class":64,"line":167},[62,153944,79],{"emptyLinePlaceholder":13},[62,153946,153947],{"class":64,"line":173},[62,153948,379],{"class":72},[22,153950,153951,153952,153955],{},"If you fire up this application and visit ",[677,153953,17969],{"href":17969,"rel":153954},[681]," you will see the string \"Hello, World!\" returned to the browser. What happens if you wanted to define 2 or more web requests to be handled by the same method? It turns out you can only use one annotation per method, so something like this will not work. ",[52,153957,153959],{"className":54,"code":153958,"language":56,"meta":57,"style":57},"package com.therealdanvega;\n\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class HomeController {\n\n @RequestMapping(\"/\")\n @RequestMapping(\"/home\")\n public String home() {\n return \"Hello, World!\";\n }\n\n}\n",[59,153960,153961,153967,153971,153977,153983,153987,153993,154003,154007,154019,154031,154041,154049,154053,154057],{"__ignoreMap":57},[62,153962,153963,153965],{"class":64,"line":65},[62,153964,69],{"class":68},[62,153966,52481],{"class":72},[62,153968,153969],{"class":64,"line":76},[62,153970,79],{"emptyLinePlaceholder":13},[62,153972,153973,153975],{"class":64,"line":82},[62,153974,27875],{"class":68},[62,153976,49517],{"class":72},[62,153978,153979,153981],{"class":64,"line":89},[62,153980,27875],{"class":68},[62,153982,27892],{"class":72},[62,153984,153985],{"class":64,"line":95},[62,153986,79],{"emptyLinePlaceholder":13},[62,153988,153989,153991],{"class":64,"line":101},[62,153990,942],{"class":72},[62,153992,2342],{"class":68},[62,153994,153995,153997,153999,154001],{"class":64,"line":107},[62,153996,116],{"class":68},[62,153998,119],{"class":68},[62,154000,25636],{"class":122},[62,154002,126],{"class":72},[62,154004,154005],{"class":64,"line":113},[62,154006,79],{"emptyLinePlaceholder":13},[62,154008,154009,154011,154013,154015,154017],{"class":64,"line":129},[62,154010,2143],{"class":72},[62,154012,10592],{"class":68},[62,154014,2109],{"class":72},[62,154016,15635],{"class":1675},[62,154018,2212],{"class":72},[62,154020,154021,154023,154025,154027,154029],{"class":64,"line":134},[62,154022,2143],{"class":72},[62,154024,10592],{"class":68},[62,154026,2109],{"class":72},[62,154028,49615],{"class":1675},[62,154030,2212],{"class":72},[62,154032,154033,154035,154037,154039],{"class":64,"line":156},[62,154034,194],{"class":68},[62,154036,2469],{"class":72},[62,154038,18647],{"class":122},[62,154040,206],{"class":72},[62,154042,154043,154045,154047],{"class":64,"line":161},[62,154044,360],{"class":68},[62,154046,80556],{"class":1675},[62,154048,153],{"class":72},[62,154050,154051],{"class":64,"line":167},[62,154052,223],{"class":72},[62,154054,154055],{"class":64,"line":173},[62,154056,79],{"emptyLinePlaceholder":13},[62,154058,154059],{"class":64,"line":179},[62,154060,379],{"class":72},[22,154062,154063],{},"If we look at the Request Mapping class value will take an array of strings and defaults to an empty array.",[52,154065,154067],{"className":54,"code":154066,"language":56,"meta":57,"style":57},"@AliasFor(\"value\")\nString\\[\\] path() default {};\n",[59,154068,154069,154082],{"__ignoreMap":57},[62,154070,154071,154073,154075,154077,154080],{"class":64,"line":65},[62,154072,942],{"class":72},[62,154074,72671],{"class":68},[62,154076,2109],{"class":72},[62,154078,154079],{"class":1675},"\"value\"",[62,154081,2212],{"class":72},[62,154083,154084,154087,154090,154092,154094],{"class":64,"line":76},[62,154085,154086],{"class":72},"String\\[\\] ",[62,154088,154089],{"class":122},"path",[62,154091,5398],{"class":72},[62,154093,59515],{"class":68},[62,154095,59540],{"class":72},[22,154097,154098],{},"So we can pass in an array of strings that represent web requests. ",[52,154100,154102],{"className":54,"code":154101,"language":56,"meta":57,"style":57},"package com.therealdanvega;\n\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class HomeController {\n\n @RequestMapping( {\"/\", \"/home\"} )\n public String home() {\n return \"Hello, World!\";\n }\n\n}\n",[59,154103,154104,154110,154114,154120,154126,154130,154136,154146,154150,154167,154177,154185,154189,154193],{"__ignoreMap":57},[62,154105,154106,154108],{"class":64,"line":65},[62,154107,69],{"class":68},[62,154109,52481],{"class":72},[62,154111,154112],{"class":64,"line":76},[62,154113,79],{"emptyLinePlaceholder":13},[62,154115,154116,154118],{"class":64,"line":82},[62,154117,27875],{"class":68},[62,154119,49517],{"class":72},[62,154121,154122,154124],{"class":64,"line":89},[62,154123,27875],{"class":68},[62,154125,27892],{"class":72},[62,154127,154128],{"class":64,"line":95},[62,154129,79],{"emptyLinePlaceholder":13},[62,154131,154132,154134],{"class":64,"line":101},[62,154133,942],{"class":72},[62,154135,2342],{"class":68},[62,154137,154138,154140,154142,154144],{"class":64,"line":107},[62,154139,116],{"class":68},[62,154141,119],{"class":68},[62,154143,25636],{"class":122},[62,154145,126],{"class":72},[62,154147,154148],{"class":64,"line":113},[62,154149,79],{"emptyLinePlaceholder":13},[62,154151,154152,154154,154156,154159,154161,154163,154165],{"class":64,"line":129},[62,154153,2143],{"class":72},[62,154155,10592],{"class":68},[62,154157,154158],{"class":72},"( {",[62,154160,15635],{"class":1675},[62,154162,976],{"class":72},[62,154164,49615],{"class":1675},[62,154166,67773],{"class":72},[62,154168,154169,154171,154173,154175],{"class":64,"line":134},[62,154170,194],{"class":68},[62,154172,2469],{"class":72},[62,154174,18647],{"class":122},[62,154176,206],{"class":72},[62,154178,154179,154181,154183],{"class":64,"line":156},[62,154180,360],{"class":68},[62,154182,80556],{"class":1675},[62,154184,153],{"class":72},[62,154186,154187],{"class":64,"line":161},[62,154188,223],{"class":72},[62,154190,154191],{"class":64,"line":167},[62,154192,79],{"emptyLinePlaceholder":13},[62,154194,154195],{"class":64,"line":173},[62,154196,379],{"class":72},[22,154198,154199,154200,154203,154204,154208],{},"Now if visit ",[677,154201,17969],{"href":17969,"rel":154202},[681]," or ",[677,154205,154206],{"href":154206,"rel":154207},"http://localhost:8080/home",[681]," we should see the string \"Hello, World!\" printed to the browser window. ",[26,154210,154212],{"id":154211},"multiple-request-mappings-screencast","Multiple Request Mappings Screencast",[22,154214,154215],{},[677,154216,154217],{"href":154217,"rel":154218},"https://www.youtube.com/watch?v=qmALVM38oec&t=1s",[681],[26,154220,1499],{"id":1498},[22,154222,154223],{},"There are some things that might not be apparent but as we saw in this tutorial we can quickly drill into the code and find a solution. This is one of the many reasons open source is so great.",[22,154225,50754,154226,154228],{},[646,154227,49733],{}," Do you face any problems setting up request mappings? _",[1527,154230,154231],{},"html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}",{"title":57,"searchDepth":76,"depth":76,"links":154233},[154234,154235,154236],{"id":153846,"depth":76,"text":153847},{"id":154211,"depth":76,"text":154212},{"id":1498,"depth":76,"text":1499},{"_id":154238,"path":154239,"title":154240,"description":154240,"meta":154241,"body":154246},"content/blog/2017/06/30/9-presentations-can-watch-right-now-learn-java-9.md","/blog/2017/06/30/9-presentations-can-watch-right-now-learn-java-9","9 Presentations you can watch right now to learn about Java 9",{"slug":154242,"date":154243,"published":13,"tags":154244,"author":-1,"cover":154245,"excerpt":-1},"9-presentations-can-watch-right-now-learn-java-9","2017-06-30T08:00:48-04:00",[56],"./coding.jpg",{"type":19,"value":154247,"toc":154467},[154248,154251,154255,154258,154262,154265,154271,154275,154283,154289,154293,154296,154313,154316,154322,154326,154329,154335,154339,154342,154348,154352,154355,154361,154365,154368,154374,154378,154381,154387,154391,154394,154400,154402,154411,154450,154456,154458,154461],[22,154249,154250],{},"In this article, I am going to give you 9 presentations that can you help learn all about Java 9.",[26,154252,154254],{"id":154253},"_9-presentations-on-java-9","9 Presentations on Java 9",[22,154256,154257],{},"The following presentations cover a wide variety of topics to help you understand what is coming in Java 9. ",[636,154259,154261],{"id":154260},"real-world-java-9-by-trisha-gee","Real World Java 9 by Trisha Gee",[22,154263,154264],{},"The feature we always hear about whenever Java 9 is in the news is Jigsaw, modularity for Java. But modularity just doesn't scratch the same developer itch that Java 8's lambdas and streams did, and as developers, we're left with a vague sensation that version 9 might just not be that interesting. In fact, Java 9 actually has a lot of great additions and changes which will make Java just that bit nicer to work with. These features can't be lumped under a nice umbrella term like Java 8's lambdas and streams, but the Java 9 changes are scattered throughout the APIs and language features that we regularly use.",[22,154266,154267],{},[677,154268,154269],{"href":154269,"rel":154270},"https://www.youtube.com/watch?v=watS54iWH9U&feature=youtu.be&list=PLEx5khR4g7PLcjLdaugk3GndelpTGbYDS",[681],[636,154272,154274],{"id":154273},"exploring-java-9-by-venkat-subramaniam","Exploring Java 9 by Venkat Subramaniam",[22,154276,154277,154278,154282],{},"How we write code was greatly influenced by Java 8. How we package the code and interact with it will be impacted by Java 9. In this presentations, we will learn about the major features of Java 9. We will start by discussing the current concerns and how the module system tries to alleviate those pains. We will learn about modules, how to define dependencies, and also how to work with existing jar files. Finally, we will explore Java 9 REPL, the reasons to use it, and various features that can benefit the programmers. Dr. Venkat Subramaniam is an award-winning author, founder of Agile Developer, Inc., creator of agilelearner.com, and an instructional professor at the University of Houston. He has trained and mentored thousands of software developers in the US, Canada, Europe, and Asia, and is a regularly-invited speaker at several international conferences. Venkat helps his clients effectively apply and succeed with sustainable agile practices on their software projects. Venkat is a (co)author of multiple technical books, including the 2007 Jolt Productivity award-winning book Practices of an Agile Developer. You can find a list of his books at agiledeveloper.com. You can reach him by email at ",[677,154279,154281],{"href":154280},"mailto:venkats@agiledeveloper.com","venkats@agiledeveloper.com"," or on Twitter at @venkat_s",[22,154284,154285],{},[677,154286,154287],{"href":154287,"rel":154288},"https://www.youtube.com/watch?v=8XmYT89fBKg&t=1099s",[681],[636,154290,154292],{"id":154291},"_55-new-features-in-jdk-9-by-simon-ritter","55 New Features in JDK 9 by Simon Ritter",[22,154294,154295],{},"Following on from the popular “55 New Features in Java SE 8” we bring you the eagerly-awaited sequel, “55 New Features in JDK 9”. Obviously, the big new feature in JDK 9 is modularity and project Jigsaw, but there’s lots more to tempt developers. We’ll divide things into five categories:",[915,154297,154298,154301,154304,154307,154310],{},[37,154299,154300],{},"Features",[37,154302,154303],{},"Standards",[37,154305,154306],{},"Inside the JVM",[37,154308,154309],{},"Specialised",[37,154311,154312],{},"Housekeeping",[22,154314,154315],{},"Join us on a whirlwind tour of what’s in (and what’s out) in JDK 9 so you’re ready to get started with the latest version of the most popular programming platform on the planet.",[22,154317,154318],{},[677,154319,154320],{"href":154320,"rel":154321},"https://www.youtube.com/watch?v=CMMzG8I23lY",[681],[636,154323,154325],{"id":154324},"what-you-need-to-know-about-java-9-including-migration-by-simon-maple","What You Need to Know About Java 9, Including Migration by Simon Maple",[22,154327,154328],{},"Java 9 is due for release in just a few months. The latest version brings new, interesting features including modules, JShell, G1GC, and many improvements to the existing programming model. This session examines what these features mean to developer and production environments and delves into what you need to know when migrating to the latest version. What do you need to do to make your applications modular? How do you break down your existing application to make them run in the module-path? How does the new default garbage collector affect how your application will run in production? Come to this session to learn what's new in Java 9 and discover how to best adopt and migrate to the new version.",[22,154330,154331],{},[677,154332,154333],{"href":154333,"rel":154334},"https://www.youtube.com/watch?v=s0WHIXaSbr4",[681],[636,154336,154338],{"id":154337},"migrating-to-java-9-modules-by-paul-bakker","Migrating to Java 9 Modules by Paul Bakker",[22,154340,154341],{},"Java 9 comes to your doorstep with major changes to all of us, whether you ordered it or not. The module system in Java 9 is a great advancement for the Java language, and we would like to migrate existing code to make use of the module system. Migrating an existing code base from the classpath to any kind of module system can be a daunting task, however. Not only do we have to take care of migrating our own code to modules, we also have to take third party libraries into consideration. Java 9 comes with a number of features to ease migration. This includes automatic modules, the unnamed module and a number of command line arguments. While these features provide great value, they do require a deep understanding of the module system to use them to their full potential. In this talk, we will look at examples of migrating real code, based on a Spring/Hibernate application. We will face common problems we run into during migration, which gives us practical tips to apply, but also a good understanding of the module framework itself and the various migration features it supports. This talk is an excellent preparation to start migrating your own code. Paul Bakker is a senior software engineer with Netflix in the Edge Developer Experience team. Besides loving to write code he has a passion for sharing knowledge. He is the co-author of \"Java 9 Modularity\" that will be published by O'Reilly in 2017, and the co-author of \"Modular Cloud Apps with OSGi\" which was published by O'Reilly in 2013. Paul is also frequently speaking at conferences about Modularity, container technology, and many other topics.",[22,154343,154344],{},[677,154345,154346],{"href":154346,"rel":154347},"https://www.youtube.com/watch?v=TEoexFsDP6A",[681],[636,154349,154351],{"id":154350},"modular-development-with-jdk-9-by-alex-buckley","Modular Development with JDK 9 by Alex Buckley",[22,154353,154354],{},"A modular development style benefits every Java developer, whether your application is one JAR or one hundred JARs. This session will introduce the Java 9 module system that's been used to structure the JDK as dozens of reusable modules that strongly protect their internals. Then, the session will explain how you can create modules to enforce the structure inherent in your application. The session will prepare you for some of the pitfalls of modular development, notably the technical debt present in popular libraries that rely on JDK internals. Finally, the session will look at how tools are preparing for modules. Alex Buckley is the Specification Lead for the Java programming language and the Java Virtual Machine at Oracle. He holds a Ph.D. in Computing from Imperial College London.",[22,154356,154357],{},[677,154358,154359],{"href":154359,"rel":154360},"https://www.youtube.com/watch?v=rfOjch4p0Po",[681],[636,154362,154364],{"id":154363},"jdk-9-language-tooling-and-library-features-by-joe-darcy","JDK 9 Language, Tooling, and Library Features by Joe Darcy",[22,154366,154367],{},"Modularity support from Project Jigsaw is the largest change coming in Java SE 9, but many other improvements are coming to the Java programming language, related tooling, and libraries in JDK 9. On the language front, some polish is added to the Project Coin features and the number of uninformative warnings has been reduced. The javac tool can now accurately cross-compile to older platform versions and has improved attribution engineering to better support type inference. Javadoc is getting a search box, a new modernized doclet API, and HTML5 support. The libraries offer better process control, improved collections, and more-compact strings. Learn more at this session. Joe Darcy is a long-time JDK engineer who has worked on projects ranging over floating-point arithmetic, introducing annotation processing to javac, serving as the inaugural release manager for OpenJDK 6, leading the Project Coin language additions, and migrating JDK bugs to a JIRA system.",[22,154369,154370],{},[677,154371,154372],{"href":154372,"rel":154373},"https://www.youtube.com/watch?v=KQiYlWFvc68",[681],[636,154375,154377],{"id":154376},"enhanced-deprecation-in-java-9-by-stuart-marks","Enhanced Deprecation in Java 9 by Stuart Marks",[22,154379,154380],{},"Java has grown immensely since its introduction, more than 20 years ago. However, there has not been a corresponding effort to remove obsolete features from the system. Some features have been deprecated, but very few have actually been removed. The platform has grown ever larger and more complex, resulting in an accumulation of complexity and technical debt. These impose a continual tax on development. This session explores “depreciation,” the trailing edge of the feature lifecycle. This includes work in Java 9 to update and clarify the @Deprecated annotation, plus additional tools being provided to enable developers to assess the impact of depreciation on their codebases and to help them deal with code migration more effectively. ",[22,154382,154383],{},[677,154384,154385],{"href":154385,"rel":154386},"https://www.youtube.com/watch?v=T_O9merCgKw",[681],[636,154388,154390],{"id":154389},"interactive-development-and-fast-feedback-with-java-9-repl-by-venkat-subramaniam","Interactive Development and Fast Feedback with Java 9 REPL by Venkat Subramaniam",[22,154392,154393],{},"Programming is an act of continuous discoveries. Auto-Completion in IDEs are great, but they're more of a speculation than experimentation. Read-Evaluate-Print-Loop or REPL gives an instant feedback and the ability to quickly try out your ideas. Fast feedbacks are the rage today in development. Come to this all live coding, no slides session to learn how to leverage the Java 9 REPL to accelerate your Java Development.",[22,154395,154396],{},[677,154397,154398],{"href":154398,"rel":154399},"https://www.youtube.com/watch?v=DHTVcq_fK2U",[681],[26,154401,2983],{"id":52743},[22,154403,154404,154405,154410],{},"It looks like the release of Java 9 has been pushed back again until September 21st, 2017. While this is unfortunate it was a little expected and gives me some more time to finish my course. If you're interested in signing up for the waitlist for this course ",[677,154406,154409],{"href":154407,"rel":154408},"https://www.danvega.dev/java-9",[681],"please click here",". In this course, we are going to dive into what’s new in Java 9. We are going to walk through how to get Java 9 as well as work on upgrading existing Java 8 applications to Java 9. After that, we will look at some tools and resources to get us started. Then we will dive into the meat of the course where we will have mini workshops that dive into the new features of JDK 9. ",[915,154412,154413,154415,154418,154421,154424,154427,154430,154447],{},[37,154414,52935],{},[37,154416,154417],{},"Prepare for JDK 9",[37,154419,154420],{},"Installation & Usage",[37,154422,154423],{},"Upgrading existing applications to JDK 9",[37,154425,154426],{},"Tools & Resources",[37,154428,154429],{},"JDK 9 Language, Tooling, and Library Features",[37,154431,154432,154433],{},"Feature Workshops\n",[915,154434,154435,154438,154441,154444],{},[37,154436,154437],{},"Java 9 Modules (Jigsaw)",[37,154439,154440],{},"JShell ",[37,154442,154443],{},"Http 2",[37,154445,154446],{},"Language Enhancements ",[37,154448,154449],{},"The Future of Java",[22,154451,154452],{},[677,154453,154455],{"href":154407,"rel":154454},[681],"Java 9 Course Waitlist",[26,154457,1499],{"id":1498},[22,154459,154460],{},"There is already some amazing content out there on Java 9 and it is always improving. I hope these presentations help you along your journey in learning Java 9. ",[22,154462,154463],{},[4534,154464,154465,61459],{},[646,154466,49733],{},{"title":57,"searchDepth":76,"depth":76,"links":154468},[154469,154480,154481],{"id":154253,"depth":76,"text":154254,"children":154470},[154471,154472,154473,154474,154475,154476,154477,154478,154479],{"id":154260,"depth":82,"text":154261},{"id":154273,"depth":82,"text":154274},{"id":154291,"depth":82,"text":154292},{"id":154324,"depth":82,"text":154325},{"id":154337,"depth":82,"text":154338},{"id":154350,"depth":82,"text":154351},{"id":154363,"depth":82,"text":154364},{"id":154376,"depth":82,"text":154377},{"id":154389,"depth":82,"text":154390},{"id":52743,"depth":76,"text":2983},{"id":1498,"depth":76,"text":1499},{"_id":154483,"path":154484,"title":154485,"description":154485,"meta":154486,"body":154491},"content/blog/2017/06/28/deploying-war-application-server-spring-boot.md","/blog/2017/06/28/deploying-war-application-server-spring-boot","Deploying a WAR to an application server in Spring Boot",{"slug":154487,"date":154488,"published":13,"tags":154489,"author":-1,"cover":154490,"excerpt":-1},"deploying-war-application-server-spring-boot","2017-06-28T09:14:15-04:00",[11002],"./pexels-photo-292627-760x506.jpeg",{"type":19,"value":154492,"toc":155109},[154493,154499,154504,154507,154511,154528,154532,154535,154541,154545,154548,154781,154784,154788,154791,154880,154883,154986,154989,154995,154999,155002,155029,155033,155058,155062,155065,155076,155082,155085,155089,155095,155097,155100,155107],[22,154494,150299,154495,154498],{},[677,154496,56768],{"href":150302,"rel":154497},[681],". We might not realize it but when we start a new project we are selecting how we want to package this application when we ready to go live. I recently received the following question from a student: ",[29685,154500,154501],{},[22,154502,154503],{},"In the real world, an application will be deployed to an application server like JBoss. How can the application be wrapped into a war file and deploy to the application server?",[22,154505,154506],{},"Thank you for the question and now let's dive into the answer. ",[26,154508,154510],{"id":154509},"war-vs-jar","WAR vs JAR",[22,154512,154513,154514,154517,154518,154520,154521,154523,154524,154527],{},"The first thing we need to discuss is what is the difference between a WAR and a JAR. ",[4534,154515,154516],{},"WAR"," stands for Web Application Archive and is deployed on a Servlet Container like Tomcat or Jetty. ",[4534,154519,148205],{}," stands for ",[646,154522,143155],{},"ava ",[646,154525,154526],{},"AR","chive and this will contain and embedded servlet container like Tomcat. These are two very different ways of deploying an application but I can assure that in the \"real world\" deploying a JAR file is very common. ",[26,154529,154531],{"id":154530},"selecting-your-package-type","Selecting your Package Type",[22,154533,154534],{},"When you create a new project using the Spring Initializr you have the option to select how you want to package your application. ",[22,154536,154537],{},[653,154538],{"alt":154539,"src":154540},"Spring Boot Package Type","./2017-06-28_08-26-58-1024x645.png",[26,154542,154544],{"id":154543},"converting-a-spring-boot-jar-application-to-a-war","Converting a Spring Boot JAR Application to a WAR",[22,154546,154547],{},"We don't always get the opportunity to create a new project from scratch, so what happens when you're working on an existing project? If you have a Maven based project that is already using JAR as its packaging type you might have a POM that looks like this. ",[52,154549,154551],{"className":1769,"code":154550,"language":1771,"meta":57,"style":57},"\u003C?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\u003Cproject xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n \u003CmodelVersion>4.0.0\u003C/modelVersion>\n\n \u003CgroupId>com.therealdanvega\u003C/groupId>\n \u003CartifactId>warnotjar\u003C/artifactId>\n \u003Cversion>0.0.1-SNAPSHOT\u003C/version>\n \u003Cpackaging>jar\u003C/packaging>\n\n \u003Cname>warnotjar\u003C/name>\n \u003Cdescription>Demo project for Spring Boot\u003C/description>\n\n \u003Cparent>\n \u003CgroupId>org.springframework.boot\u003C/groupId>\n \u003CartifactId>spring-boot-starter-parent\u003C/artifactId>\n \u003Cversion>1.5.4.RELEASE\u003C/version>\n \u003CrelativePath/> \u003C!-- lookup parent from repository -->\n \u003C/parent>\n\n \u003Cproperties>\n \u003Cproject.build.sourceEncoding>UTF-8\u003C/project.build.sourceEncoding>\n \u003Cproject.reporting.outputEncoding>UTF-8\u003C/project.reporting.outputEncoding>\n \u003Cjava.version>1.8\u003C/java.version>\n \u003C/properties>\n\n \u003Cdependencies>\n \u003Cdependency>\n \u003CgroupId>org.springframework.boot\u003C/groupId>\n \u003CartifactId>spring-boot-starter-web\u003C/artifactId>\n \u003C/dependency>\n\n \u003Cdependency>\n \u003CgroupId>org.springframework.boot\u003C/groupId>\n \u003CartifactId>spring-boot-starter-test\u003C/artifactId>\n \u003Cscope>test\u003C/scope>\n \u003C/dependency>\n \u003C/dependencies>\n\n \u003Cbuild>\n \u003Cplugins>\n \u003Cplugin>\n \u003CgroupId>org.springframework.boot\u003C/groupId>\n \u003CartifactId>spring-boot-maven-plugin\u003C/artifactId>\n \u003C/plugin>\n \u003C/plugins>\n \u003C/build>\n\u003C/project>\n",[59,154552,154553,154558,154563,154568,154573,154577,154582,154587,154592,154597,154601,154606,154611,154615,154620,154624,154629,154634,154639,154644,154648,154653,154658,154663,154668,154673,154677,154682,154687,154691,154696,154701,154705,154709,154713,154718,154723,154727,154732,154736,154741,154746,154751,154756,154761,154766,154771,154776],{"__ignoreMap":57},[62,154554,154555],{"class":64,"line":65},[62,154556,154557],{},"\u003C?xml version=\"1.0\" encoding=\"UTF-8\"?>\n",[62,154559,154560],{"class":64,"line":76},[62,154561,154562],{},"\u003Cproject xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n",[62,154564,154565],{"class":64,"line":82},[62,154566,154567],{}," xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n",[62,154569,154570],{"class":64,"line":89},[62,154571,154572],{}," \u003CmodelVersion>4.0.0\u003C/modelVersion>\n",[62,154574,154575],{"class":64,"line":95},[62,154576,79],{"emptyLinePlaceholder":13},[62,154578,154579],{"class":64,"line":101},[62,154580,154581],{}," \u003CgroupId>com.therealdanvega\u003C/groupId>\n",[62,154583,154584],{"class":64,"line":107},[62,154585,154586],{}," \u003CartifactId>warnotjar\u003C/artifactId>\n",[62,154588,154589],{"class":64,"line":113},[62,154590,154591],{}," \u003Cversion>0.0.1-SNAPSHOT\u003C/version>\n",[62,154593,154594],{"class":64,"line":129},[62,154595,154596],{}," \u003Cpackaging>jar\u003C/packaging>\n",[62,154598,154599],{"class":64,"line":134},[62,154600,79],{"emptyLinePlaceholder":13},[62,154602,154603],{"class":64,"line":156},[62,154604,154605],{}," \u003Cname>warnotjar\u003C/name>\n",[62,154607,154608],{"class":64,"line":161},[62,154609,154610],{}," \u003Cdescription>Demo project for Spring Boot\u003C/description>\n",[62,154612,154613],{"class":64,"line":167},[62,154614,79],{"emptyLinePlaceholder":13},[62,154616,154617],{"class":64,"line":173},[62,154618,154619],{}," \u003Cparent>\n",[62,154621,154622],{"class":64,"line":179},[62,154623,56813],{},[62,154625,154626],{"class":64,"line":185},[62,154627,154628],{}," \u003CartifactId>spring-boot-starter-parent\u003C/artifactId>\n",[62,154630,154631],{"class":64,"line":191},[62,154632,154633],{}," \u003Cversion>1.5.4.RELEASE\u003C/version>\n",[62,154635,154636],{"class":64,"line":209},[62,154637,154638],{}," \u003CrelativePath/> \u003C!-- lookup parent from repository -->\n",[62,154640,154641],{"class":64,"line":220},[62,154642,154643],{}," \u003C/parent>\n",[62,154645,154646],{"class":64,"line":226},[62,154647,79],{"emptyLinePlaceholder":13},[62,154649,154650],{"class":64,"line":231},[62,154651,154652],{}," \u003Cproperties>\n",[62,154654,154655],{"class":64,"line":236},[62,154656,154657],{}," \u003Cproject.build.sourceEncoding>UTF-8\u003C/project.build.sourceEncoding>\n",[62,154659,154660],{"class":64,"line":242},[62,154661,154662],{}," \u003Cproject.reporting.outputEncoding>UTF-8\u003C/project.reporting.outputEncoding>\n",[62,154664,154665],{"class":64,"line":247},[62,154666,154667],{}," \u003Cjava.version>1.8\u003C/java.version>\n",[62,154669,154670],{"class":64,"line":252},[62,154671,154672],{}," \u003C/properties>\n",[62,154674,154675],{"class":64,"line":257},[62,154676,79],{"emptyLinePlaceholder":13},[62,154678,154679],{"class":64,"line":271},[62,154680,154681],{}," \u003Cdependencies>\n",[62,154683,154684],{"class":64,"line":281},[62,154685,154686],{}," \u003Cdependency>\n",[62,154688,154689],{"class":64,"line":286},[62,154690,56952],{},[62,154692,154693],{"class":64,"line":291},[62,154694,154695],{}," \u003CartifactId>spring-boot-starter-web\u003C/artifactId>\n",[62,154697,154698],{"class":64,"line":296},[62,154699,154700],{}," \u003C/dependency>\n",[62,154702,154703],{"class":64,"line":302},[62,154704,79],{"emptyLinePlaceholder":13},[62,154706,154707],{"class":64,"line":308},[62,154708,154686],{},[62,154710,154711],{"class":64,"line":314},[62,154712,56952],{},[62,154714,154715],{"class":64,"line":320},[62,154716,154717],{}," \u003CartifactId>spring-boot-starter-test\u003C/artifactId>\n",[62,154719,154720],{"class":64,"line":326},[62,154721,154722],{}," \u003Cscope>test\u003C/scope>\n",[62,154724,154725],{"class":64,"line":338},[62,154726,154700],{},[62,154728,154729],{"class":64,"line":343},[62,154730,154731],{}," \u003C/dependencies>\n",[62,154733,154734],{"class":64,"line":357},[62,154735,79],{"emptyLinePlaceholder":13},[62,154737,154738],{"class":64,"line":366},[62,154739,154740],{}," \u003Cbuild>\n",[62,154742,154743],{"class":64,"line":371},[62,154744,154745],{}," \u003Cplugins>\n",[62,154747,154748],{"class":64,"line":376},[62,154749,154750],{}," \u003Cplugin>\n",[62,154752,154753],{"class":64,"line":16333},[62,154754,154755],{}," \u003CgroupId>org.springframework.boot\u003C/groupId>\n",[62,154757,154758],{"class":64,"line":16349},[62,154759,154760],{}," \u003CartifactId>spring-boot-maven-plugin\u003C/artifactId>\n",[62,154762,154763],{"class":64,"line":16365},[62,154764,154765],{}," \u003C/plugin>\n",[62,154767,154768],{"class":64,"line":16381},[62,154769,154770],{}," \u003C/plugins>\n",[62,154772,154773],{"class":64,"line":16402},[62,154774,154775],{}," \u003C/build>\n",[62,154777,154778],{"class":64,"line":16411},[62,154779,154780],{},"\u003C/project>\n",[22,154782,154783],{},"We can use 3 very simple steps to convert our application to use WAR as the package type. ",[636,154785,154787],{"id":154786},"step-1-adding-the-servlet-initializer-class","Step 1: Adding the Servlet Initializer Class",[22,154789,154790],{},"In an application where we are using JAR as the package type, we only need the main application class that looks like this. ",[52,154792,154794],{"className":54,"code":154793,"language":56,"meta":57,"style":57},"package com.therealdanvega;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class WarnotjarApplication {\n\n public static void main(String[] args) {\n SpringApplication.run(WarnotjarApplication.class, args);\n }\n}\n",[59,154795,154796,154802,154806,154812,154818,154822,154828,154839,154843,154863,154872,154876],{"__ignoreMap":57},[62,154797,154798,154800],{"class":64,"line":65},[62,154799,69],{"class":68},[62,154801,52481],{"class":72},[62,154803,154804],{"class":64,"line":76},[62,154805,79],{"emptyLinePlaceholder":13},[62,154807,154808,154810],{"class":64,"line":82},[62,154809,27875],{"class":68},[62,154811,52513],{"class":72},[62,154813,154814,154816],{"class":64,"line":89},[62,154815,27875],{"class":68},[62,154817,52520],{"class":72},[62,154819,154820],{"class":64,"line":95},[62,154821,79],{"emptyLinePlaceholder":13},[62,154823,154824,154826],{"class":64,"line":101},[62,154825,942],{"class":72},[62,154827,2079],{"class":68},[62,154829,154830,154832,154834,154837],{"class":64,"line":107},[62,154831,116],{"class":68},[62,154833,119],{"class":68},[62,154835,154836],{"class":122}," WarnotjarApplication",[62,154838,126],{"class":72},[62,154840,154841],{"class":64,"line":113},[62,154842,79],{"emptyLinePlaceholder":13},[62,154844,154845,154847,154849,154851,154853,154855,154857,154859,154861],{"class":64,"line":129},[62,154846,194],{"class":68},[62,154848,2101],{"class":68},[62,154850,200],{"class":68},[62,154852,2106],{"class":122},[62,154854,2109],{"class":72},[62,154856,973],{"class":68},[62,154858,2114],{"class":72},[62,154860,2117],{"class":889},[62,154862,768],{"class":72},[62,154864,154865,154867,154869],{"class":64,"line":134},[62,154866,2124],{"class":72},[62,154868,2127],{"class":122},[62,154870,154871],{"class":72},"(WarnotjarApplication.class, args);\n",[62,154873,154874],{"class":64,"line":156},[62,154875,223],{"class":72},[62,154877,154878],{"class":64,"line":161},[62,154879,379],{"class":72},[22,154881,154882],{},"In our WAR deployment, we are going to add another class that will help configure our web application. We are going to add a new class called Servlet Initializer that looks like this. You could also extend your main application class if you like and override configure in that class but I like separating this out. ",[52,154884,154886],{"className":54,"code":154885,"language":56,"meta":57,"style":57},"package com.therealdanvega;\n\nimport org.springframework.boot.builder.SpringApplicationBuilder;\nimport org.springframework.boot.web.support.SpringBootServletInitializer;\n\npublic class ServletInitializer extends SpringBootServletInitializer {\n\n @Override\n protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {\n return application.sources(WarnotjarApplication.class);\n }\n\n}\n",[59,154887,154888,154894,154898,154905,154912,154916,154932,154936,154942,154961,154974,154978,154982],{"__ignoreMap":57},[62,154889,154890,154892],{"class":64,"line":65},[62,154891,69],{"class":68},[62,154893,52481],{"class":72},[62,154895,154896],{"class":64,"line":76},[62,154897,79],{"emptyLinePlaceholder":13},[62,154899,154900,154902],{"class":64,"line":82},[62,154901,27875],{"class":68},[62,154903,154904],{"class":72}," org.springframework.boot.builder.SpringApplicationBuilder;\n",[62,154906,154907,154909],{"class":64,"line":89},[62,154908,27875],{"class":68},[62,154910,154911],{"class":72}," org.springframework.boot.web.support.SpringBootServletInitializer;\n",[62,154913,154914],{"class":64,"line":95},[62,154915,79],{"emptyLinePlaceholder":13},[62,154917,154918,154920,154922,154925,154927,154930],{"class":64,"line":101},[62,154919,116],{"class":68},[62,154921,119],{"class":68},[62,154923,154924],{"class":122}," ServletInitializer",[62,154926,8537],{"class":68},[62,154928,154929],{"class":122}," SpringBootServletInitializer",[62,154931,126],{"class":72},[62,154933,154934],{"class":64,"line":107},[62,154935,79],{"emptyLinePlaceholder":13},[62,154937,154938,154940],{"class":64,"line":113},[62,154939,2143],{"class":72},[62,154941,13555],{"class":68},[62,154943,154944,154947,154950,154953,154956,154959],{"class":64,"line":129},[62,154945,154946],{"class":68}," protected",[62,154948,154949],{"class":72}," SpringApplicationBuilder ",[62,154951,154952],{"class":122},"configure",[62,154954,154955],{"class":72},"(SpringApplicationBuilder ",[62,154957,154958],{"class":889},"application",[62,154960,768],{"class":72},[62,154962,154963,154965,154968,154971],{"class":64,"line":134},[62,154964,360],{"class":68},[62,154966,154967],{"class":72}," application.",[62,154969,154970],{"class":122},"sources",[62,154972,154973],{"class":72},"(WarnotjarApplication.class);\n",[62,154975,154976],{"class":64,"line":156},[62,154977,223],{"class":72},[62,154979,154980],{"class":64,"line":161},[62,154981,79],{"emptyLinePlaceholder":13},[62,154983,154984],{"class":64,"line":167},[62,154985,379],{"class":72},[22,154987,154988],{},"In my example, I am using my main application class (WarnotjarApplication.class) as an argument to the sources method. You should end up with the main application class and a class called ServletInitializer. ",[22,154990,154991],{},[653,154992],{"alt":154993,"src":154994},"WAR not JAR","./2017-06-28_08-53-25.png",[636,154996,154998],{"id":154997},"step-2-update-the-tomcat-dependency","Step 2: Update the Tomcat Dependency",[22,155000,155001],{},"In our Spring Boot Starter Web dependency, there is a Tomcat dependency declared. We can't go in and make a change there but we can make the change in our POM. We need to change that dependency to say that this will be provided for us. ",[52,155003,155005],{"className":1769,"code":155004,"language":1771,"meta":57,"style":57},"\u003Cdependency>\n \u003CgroupId>org.springframework.boot\u003C/groupId>\n \u003CartifactId>spring-boot-starter-tomcat\u003C/artifactId>\n \u003Cscope>provided\u003C/scope>\n\u003C/dependency>\n",[59,155006,155007,155011,155015,155020,155025],{"__ignoreMap":57},[62,155008,155009],{"class":64,"line":65},[62,155010,46425],{},[62,155012,155013],{"class":64,"line":76},[62,155014,54736],{},[62,155016,155017],{"class":64,"line":82},[62,155018,155019],{}," \u003CartifactId>spring-boot-starter-tomcat\u003C/artifactId>\n",[62,155021,155022],{"class":64,"line":89},[62,155023,155024],{}," \u003Cscope>provided\u003C/scope>\n",[62,155026,155027],{"class":64,"line":95},[62,155028,46445],{},[636,155030,155032],{"id":155031},"step-3-change-the-package-type-to-war","Step 3: Change the package type to war",[52,155034,155036],{"className":1769,"code":155035,"language":1771,"meta":57,"style":57},"\u003CgroupId>com.therealdanvega\u003C/groupId>\n\u003CartifactId>warnotjar\u003C/artifactId>\n\u003Cversion>0.0.1-SNAPSHOT\u003C/version>\n\u003Cpackaging>war\u003C/packaging>\n",[59,155037,155038,155043,155048,155053],{"__ignoreMap":57},[62,155039,155040],{"class":64,"line":65},[62,155041,155042],{},"\u003CgroupId>com.therealdanvega\u003C/groupId>\n",[62,155044,155045],{"class":64,"line":76},[62,155046,155047],{},"\u003CartifactId>warnotjar\u003C/artifactId>\n",[62,155049,155050],{"class":64,"line":82},[62,155051,155052],{},"\u003Cversion>0.0.1-SNAPSHOT\u003C/version>\n",[62,155054,155055],{"class":64,"line":89},[62,155056,155057],{},"\u003Cpackaging>war\u003C/packaging>\n",[26,155059,155061],{"id":155060},"packaging-your-application","Packaging your application",[22,155063,155064],{},"With those changes in place, you can now package your application. ",[52,155066,155068],{"className":1663,"code":155067,"language":1665,"meta":57,"style":57},"./mvnw package\n",[59,155069,155070],{"__ignoreMap":57},[62,155071,155072,155074],{"class":64,"line":65},[62,155073,2304],{"class":122},[62,155075,3645],{"class":1675},[22,155077,155078],{},[653,155079],{"alt":155080,"src":155081},"Maven Package WAR","./2017-06-28_09-08-50.png",[22,155083,155084],{},"If you look inside of the /target folder now you should see the war file. You can now take that file and deploy on your servlet container. ",[26,155086,155088],{"id":155087},"spring-boot-war-screencast","Spring Boot WAR Screencast",[22,155090,155091],{},[677,155092,155093],{"href":155093,"rel":155094},"https://youtu.be/92ceKwUZoA0",[681],[26,155096,1499],{"id":1498},[22,155098,155099],{},"Spring Boot makes it easy for us to package our applications to fit our needs. I hope this tutorial showed how easy it was to convert an existing JAR application over to a WAR. ",[22,155101,155102],{},[4534,155103,155104,155106],{},[646,155105,49733],{}," What Spring Boot Deployment issues are you facing?",[1527,155108,78650],{},{"title":57,"searchDepth":76,"depth":76,"links":155110},[155111,155112,155113,155118,155119,155120],{"id":154509,"depth":76,"text":154510},{"id":154530,"depth":76,"text":154531},{"id":154543,"depth":76,"text":154544,"children":155114},[155115,155116,155117],{"id":154786,"depth":82,"text":154787},{"id":154997,"depth":82,"text":154998},{"id":155031,"depth":82,"text":155032},{"id":155060,"depth":76,"text":155061},{"id":155087,"depth":76,"text":155088},{"id":1498,"depth":76,"text":1499},{"_id":155122,"path":155123,"title":155124,"description":155124,"meta":155125,"body":155129},"content/blog/2017/06/26/spring-boot-configuration-using-yaml.md","/blog/2017/06/26/spring-boot-configuration-using-yaml","Spring Boot Configuration using YAML",{"slug":155126,"date":155127,"published":13,"tags":155128,"author":-1,"cover":56759,"excerpt":-1},"spring-boot-configuration-using-yaml","2017-06-26T09:00:06-04:00",[11002],{"type":19,"value":155130,"toc":155774},[155131,155139,155144,155199,155366,155370,155373,155435,155438,155482,155486,155489,155494,155498,155501,155589,155598,155748,155751,155755,155761,155763,155766,155771],[22,155132,155133,155134,155138],{},"In this tutorial, we are going to look at a question from a student in my ",[677,155135,155137],{"href":150302,"rel":155136},[681],"Spring Boot Introduction"," course. This question had to do with using a YAML file for configuration and the best way about how to inject a single property for multiple environments. Let's take a look at the question and then we will talk through the solution. ",[29685,155140,155141],{},[22,155142,155143],{},"Hi! How can I access these properties from a single yml properties file in the DataSourceConfig? Thanks.",[52,155145,155147],{"className":1663,"code":155146,"language":1665,"meta":57,"style":57},"environments:\n development:\n name: Development setup\n url: http://dev.bar.com\n production:\n name: Production setup\n url: http://prod.bar.com\n",[59,155148,155149,155154,155159,155170,155178,155183,155192],{"__ignoreMap":57},[62,155150,155151],{"class":64,"line":65},[62,155152,155153],{"class":122},"environments:\n",[62,155155,155156],{"class":64,"line":76},[62,155157,155158],{"class":122}," development:\n",[62,155160,155161,155164,155167],{"class":64,"line":82},[62,155162,155163],{"class":122}," name:",[62,155165,155166],{"class":1675}," Development",[62,155168,155169],{"class":1675}," setup\n",[62,155171,155172,155175],{"class":64,"line":89},[62,155173,155174],{"class":122}," url:",[62,155176,155177],{"class":1675}," http://dev.bar.com\n",[62,155179,155180],{"class":64,"line":95},[62,155181,155182],{"class":122}," production:\n",[62,155184,155185,155187,155190],{"class":64,"line":101},[62,155186,155163],{"class":122},[62,155188,155189],{"class":1675}," Production",[62,155191,155169],{"class":1675},[62,155193,155194,155196],{"class":64,"line":107},[62,155195,155174],{"class":122},[62,155197,155198],{"class":1675}," http://prod.bar.com\n",[52,155200,155202],{"className":54,"code":155201,"language":56,"meta":57,"style":57},"@Configuration\npublic class DataSourceConfig\n{\n String url;\n\n @Bean(name=\"datasource\")\n @Profile(\"development\")\n DataSource development()\n {\n return new DataSource(url, 9999);\n }\n\n @Bean(name=\"datasource\")\n @Profile(\"production\")\n DataSource production()\n {\n return new DataSource(url, 9999);\n }\n}\n",[59,155203,155204,155210,155219,155223,155228,155232,155249,155263,155273,155277,155294,155298,155302,155318,155331,155340,155344,155358,155362],{"__ignoreMap":57},[62,155205,155206,155208],{"class":64,"line":65},[62,155207,942],{"class":72},[62,155209,11133],{"class":68},[62,155211,155212,155214,155216],{"class":64,"line":76},[62,155213,116],{"class":68},[62,155215,119],{"class":68},[62,155217,155218],{"class":122}," DataSourceConfig\n",[62,155220,155221],{"class":64,"line":82},[62,155222,3680],{"class":72},[62,155224,155225],{"class":64,"line":89},[62,155226,155227],{"class":72}," String url;\n",[62,155229,155230],{"class":64,"line":95},[62,155231,79],{"emptyLinePlaceholder":13},[62,155233,155234,155236,155238,155240,155242,155244,155247],{"class":64,"line":101},[62,155235,2143],{"class":72},[62,155237,72241],{"class":68},[62,155239,2109],{"class":72},[62,155241,3107],{"class":149},[62,155243,146],{"class":68},[62,155245,155246],{"class":1675},"\"datasource\"",[62,155248,2212],{"class":72},[62,155250,155251,155253,155256,155258,155261],{"class":64,"line":107},[62,155252,2143],{"class":72},[62,155254,155255],{"class":68},"Profile",[62,155257,2109],{"class":72},[62,155259,155260],{"class":1675},"\"development\"",[62,155262,2212],{"class":72},[62,155264,155265,155268,155271],{"class":64,"line":113},[62,155266,155267],{"class":72}," DataSource ",[62,155269,155270],{"class":122},"development",[62,155272,2223],{"class":72},[62,155274,155275],{"class":64,"line":129},[62,155276,49857],{"class":72},[62,155278,155279,155281,155283,155286,155289,155292],{"class":64,"line":134},[62,155280,360],{"class":68},[62,155282,466],{"class":68},[62,155284,155285],{"class":122}," DataSource",[62,155287,155288],{"class":72},"(url, ",[62,155290,155291],{"class":149},"9999",[62,155293,1133],{"class":72},[62,155295,155296],{"class":64,"line":156},[62,155297,223],{"class":72},[62,155299,155300],{"class":64,"line":161},[62,155301,79],{"emptyLinePlaceholder":13},[62,155303,155304,155306,155308,155310,155312,155314,155316],{"class":64,"line":167},[62,155305,2143],{"class":72},[62,155307,72241],{"class":68},[62,155309,2109],{"class":72},[62,155311,3107],{"class":149},[62,155313,146],{"class":68},[62,155315,155246],{"class":1675},[62,155317,2212],{"class":72},[62,155319,155320,155322,155324,155326,155329],{"class":64,"line":173},[62,155321,2143],{"class":72},[62,155323,155255],{"class":68},[62,155325,2109],{"class":72},[62,155327,155328],{"class":1675},"\"production\"",[62,155330,2212],{"class":72},[62,155332,155333,155335,155338],{"class":64,"line":179},[62,155334,155267],{"class":72},[62,155336,155337],{"class":122},"production",[62,155339,2223],{"class":72},[62,155341,155342],{"class":64,"line":185},[62,155343,49857],{"class":72},[62,155345,155346,155348,155350,155352,155354,155356],{"class":64,"line":191},[62,155347,360],{"class":68},[62,155349,466],{"class":68},[62,155351,155285],{"class":122},[62,155353,155288],{"class":72},[62,155355,155291],{"class":149},[62,155357,1133],{"class":72},[62,155359,155360],{"class":64,"line":209},[62,155361,223],{"class":72},[62,155363,155364],{"class":64,"line":220},[62,155365,379],{"class":72},[26,155367,155369],{"id":155368},"yaml-configuration","YAML Configuration",[22,155371,155372],{},"If you haven't heard of YAML before Wikipedia's definition of it is as follows",[29685,155374,155375],{},[22,155376,155377,744,155379,155410,155411,155414,155415,155421,155427,155428,155434],{},[646,155378,103832],{},[62,155380,155382],{"style":155381},"nowrap",[62,155383,155385],{"style":155384},"IPA nopopups noexcerpt",[677,155386,6936,155390,155394,155398,155402,155406,6936],{"href":155387,"rel":155388,"title":155389},"https://en.wikipedia.org/wiki/Help:IPA_for_English",[681],"Help:IPA for English",[62,155391,155393],{"style":155392},"/ˈ/ primary stress follows","ˈ",[62,155395,155397],{"style":155396},"/j/ 'y' in 'yes'","j",[62,155399,155401],{"style":155400},"/æ/ short 'a' in 'bad'","æ",[62,155403,155405],{"style":155404},"'m' in 'my'","m",[62,155407,155409],{"style":155408},"/əl/ 'le' in 'bottle'","əl",", rhymes with ",[4534,155412,155413],{},"mammal",") is a ",[677,155416,155420],{"href":155417,"rel":155418,"title":155419},"https://en.wikipedia.org/wiki/Human-readable",[681],"Human-readable","human-readable",[677,155422,155426],{"href":155423,"rel":155424,"title":155425},"https://en.wikipedia.org/wiki/Data_serialization_language",[681],"Data serialization language","data serialization language",". It is commonly used for ",[677,155429,155433],{"href":155430,"rel":155431,"title":155432},"https://en.wikipedia.org/wiki/Configuration_file",[681],"Configuration file","configuration files",", but could be used in many applications where data is being stored (e.g. debugging output) or transmitted (e.g. document headers).",[22,155436,155437],{},"We can use it in place of or right along side of the application.properties. It really just comes down to a preference but if you have a ton of configuration it is usually easier to read YAML configuration in my opinion. If we take a look back our student had a YAML configuration file that looked like this. ",[52,155439,155440],{"className":1663,"code":155146,"language":1665,"meta":57,"style":57},[59,155441,155442,155446,155450,155458,155464,155468,155476],{"__ignoreMap":57},[62,155443,155444],{"class":64,"line":65},[62,155445,155153],{"class":122},[62,155447,155448],{"class":64,"line":76},[62,155449,155158],{"class":122},[62,155451,155452,155454,155456],{"class":64,"line":82},[62,155453,155163],{"class":122},[62,155455,155166],{"class":1675},[62,155457,155169],{"class":1675},[62,155459,155460,155462],{"class":64,"line":89},[62,155461,155174],{"class":122},[62,155463,155177],{"class":1675},[62,155465,155466],{"class":64,"line":95},[62,155467,155182],{"class":122},[62,155469,155470,155472,155474],{"class":64,"line":101},[62,155471,155163],{"class":122},[62,155473,155189],{"class":1675},[62,155475,155169],{"class":1675},[62,155477,155478,155480],{"class":64,"line":107},[62,155479,155174],{"class":122},[62,155481,155198],{"class":1675},[636,155483,155485],{"id":155484},"profile-specific-configuration","Profile Specific Configuration",[22,155487,155488],{},"The first thing I will say about this is that I usually break out different environment configurations into separate configuration files. You can do this with both .properties or .yml configuration using a -environment name on the file. If we had a development, test and production environment we would have 3 files in src/main/resources that looked like this. ",[22,155490,155491],{},[653,155492],{"alt":155485,"src":155493},"./2017-06-27_12-38-38.png",[636,155495,155497],{"id":155496},"one-config-to-rule-them-all","One Config to rule them all",[22,155499,155500],{},"That is what I would do but if you want to keep them all in one file you most certainly can do so. If we had them all in a single file we can use the 3 dashes and treat them as separate configurations. In this configuration, I am setting the active profile to the development profile and set up two different profiles. ",[52,155502,155504],{"className":1663,"code":155503,"language":1665,"meta":57,"style":57},"spring:\n profiles:.active: development\n---\nspring:\n profiles: development\ndatasource:\n name: Development Setup\n url: http://dev.bar.com\n---\nspring:\n profiles: production\ndatasource:\n name: Production Setup\n url: http://prod.bar.com\n\n",[59,155505,155506,155511,155519,155523,155527,155534,155539,155549,155556,155560,155564,155571,155575,155583],{"__ignoreMap":57},[62,155507,155508],{"class":64,"line":65},[62,155509,155510],{"class":122},"spring:\n",[62,155512,155513,155516],{"class":64,"line":76},[62,155514,155515],{"class":122}," profiles:.active:",[62,155517,155518],{"class":1675}," development\n",[62,155520,155521],{"class":64,"line":82},[62,155522,121228],{"class":122},[62,155524,155525],{"class":64,"line":89},[62,155526,155510],{"class":122},[62,155528,155529,155532],{"class":64,"line":95},[62,155530,155531],{"class":122}," profiles:",[62,155533,155518],{"class":1675},[62,155535,155536],{"class":64,"line":101},[62,155537,155538],{"class":122},"datasource:\n",[62,155540,155541,155544,155546],{"class":64,"line":107},[62,155542,155543],{"class":122}," name:",[62,155545,155166],{"class":1675},[62,155547,155548],{"class":1675}," Setup\n",[62,155550,155551,155554],{"class":64,"line":113},[62,155552,155553],{"class":122}," url:",[62,155555,155177],{"class":1675},[62,155557,155558],{"class":64,"line":129},[62,155559,121228],{"class":122},[62,155561,155562],{"class":64,"line":134},[62,155563,155510],{"class":122},[62,155565,155566,155568],{"class":64,"line":156},[62,155567,155531],{"class":122},[62,155569,155570],{"class":1675}," production\n",[62,155572,155573],{"class":64,"line":161},[62,155574,155538],{"class":122},[62,155576,155577,155579,155581],{"class":64,"line":167},[62,155578,155543],{"class":122},[62,155580,155189],{"class":1675},[62,155582,155548],{"class":1675},[62,155584,155585,155587],{"class":64,"line":173},[62,155586,155553],{"class":122},[62,155588,155198],{"class":1675},[22,155590,155591,155592,155597],{},"Back in our DataSource Configuration class, we can make a small change. When the only thing that changes about the bean is the properties it's instantiated with like we have here there is no need to create different beans based on the environment we are in. We can simply create one single bean and we will use the correct properties based on the active profile. To get our properties into our class we will use the ",[677,155593,155596],{"href":155594,"rel":155595},"https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/beans/factory/annotation/Value.html",[681],"@Value annotation"," and the correct values based on our profile will get injected. ",[52,155599,155601],{"className":54,"code":155600,"language":56,"meta":57,"style":57},"package com.therealdanvega.config;\n\nimport com.therealdanvega.domain.DataSource;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration\npublic class DataSourceConfig {\n\n @Value(\"${datasource.name}\")\n private String name;\n\n @Value(\"${datasource.url}\")\n private String url;\n\n @Bean\n DataSource dataSource() {\n return new DataSource(name,url);\n }\n\n}\n\n",[59,155602,155603,155610,155614,155621,155627,155633,155639,155643,155649,155660,155664,155677,155683,155687,155700,155707,155711,155717,155725,155736,155740,155744],{"__ignoreMap":57},[62,155604,155605,155607],{"class":64,"line":65},[62,155606,69],{"class":68},[62,155608,155609],{"class":72}," com.therealdanvega.config;\n",[62,155611,155612],{"class":64,"line":76},[62,155613,79],{"emptyLinePlaceholder":13},[62,155615,155616,155618],{"class":64,"line":82},[62,155617,27875],{"class":68},[62,155619,155620],{"class":72}," com.therealdanvega.domain.DataSource;\n",[62,155622,155623,155625],{"class":64,"line":89},[62,155624,27875],{"class":68},[62,155626,28187],{"class":72},[62,155628,155629,155631],{"class":64,"line":95},[62,155630,27875],{"class":68},[62,155632,52527],{"class":72},[62,155634,155635,155637],{"class":64,"line":101},[62,155636,27875],{"class":68},[62,155638,72015],{"class":72},[62,155640,155641],{"class":64,"line":107},[62,155642,79],{"emptyLinePlaceholder":13},[62,155644,155645,155647],{"class":64,"line":113},[62,155646,942],{"class":72},[62,155648,11133],{"class":68},[62,155650,155651,155653,155655,155658],{"class":64,"line":129},[62,155652,116],{"class":68},[62,155654,119],{"class":68},[62,155656,155657],{"class":122}," DataSourceConfig",[62,155659,126],{"class":72},[62,155661,155662],{"class":64,"line":134},[62,155663,79],{"emptyLinePlaceholder":13},[62,155665,155666,155668,155670,155672,155675],{"class":64,"line":156},[62,155667,2143],{"class":72},[62,155669,14632],{"class":68},[62,155671,2109],{"class":72},[62,155673,155674],{"class":1675},"\"${datasource.name}\"",[62,155676,2212],{"class":72},[62,155678,155679,155681],{"class":64,"line":161},[62,155680,137],{"class":68},[62,155682,9406],{"class":72},[62,155684,155685],{"class":64,"line":167},[62,155686,79],{"emptyLinePlaceholder":13},[62,155688,155689,155691,155693,155695,155698],{"class":64,"line":173},[62,155690,2143],{"class":72},[62,155692,14632],{"class":68},[62,155694,2109],{"class":72},[62,155696,155697],{"class":1675},"\"${datasource.url}\"",[62,155699,2212],{"class":72},[62,155701,155702,155704],{"class":64,"line":179},[62,155703,137],{"class":68},[62,155705,155706],{"class":72}," String url;\n",[62,155708,155709],{"class":64,"line":185},[62,155710,79],{"emptyLinePlaceholder":13},[62,155712,155713,155715],{"class":64,"line":191},[62,155714,2143],{"class":72},[62,155716,2146],{"class":68},[62,155718,155719,155721,155723],{"class":64,"line":209},[62,155720,155267],{"class":72},[62,155722,151794],{"class":122},[62,155724,206],{"class":72},[62,155726,155727,155729,155731,155733],{"class":64,"line":220},[62,155728,360],{"class":68},[62,155730,466],{"class":68},[62,155732,155285],{"class":122},[62,155734,155735],{"class":72},"(name,url);\n",[62,155737,155738],{"class":64,"line":226},[62,155739,223],{"class":72},[62,155741,155742],{"class":64,"line":231},[62,155743,79],{"emptyLinePlaceholder":13},[62,155745,155746],{"class":64,"line":236},[62,155747,379],{"class":72},[22,155749,155750],{},"This is the approach I would take and it works out pretty well. ",[26,155752,155754],{"id":155753},"spring-boot-configuration-using-yaml-properties-screencast","Spring Boot Configuration using YAML Properties Screencast",[22,155756,155757],{},[677,155758,155759],{"href":155759,"rel":155760},"https://youtu.be/Utwu-17Ct9Y",[681],[26,155762,1499],{"id":1498},[22,155764,155765],{},"Thanks for the question and I hope this helped others clear up some questions on using YAML configuration. This can be a little confusing when you have some many ways to inject properties but after some practice it all makes sense. ",[22,155767,50754,155768,155770],{},[646,155769,49733],{}," What problems are you facing with YAML Configuration? _",[1527,155772,155773],{},"html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}",{"title":57,"searchDepth":76,"depth":76,"links":155775},[155776,155780,155781],{"id":155368,"depth":76,"text":155369,"children":155777},[155778,155779],{"id":155484,"depth":82,"text":155485},{"id":155496,"depth":82,"text":155497},{"id":155753,"depth":76,"text":155754},{"id":1498,"depth":76,"text":1499},{"_id":155783,"path":155784,"title":155785,"description":155785,"meta":155786,"body":155791},"content/blog/2017/06/23/building-angular-spring-boot.md","/blog/2017/06/23/building-angular-spring-boot","Building your Angular Application for Spring Boot",{"slug":155787,"date":155788,"published":13,"tags":155789,"author":-1,"cover":155790,"excerpt":-1},"building-angular-spring-boot","2017-06-23T08:00:27-04:00",[49752,11002],"./imac-apple-mockup-app-38544-760x505.jpeg",{"type":19,"value":155792,"toc":156184},[155793,155801,155805,155808,155815,155818,155822,155825,155907,155910,155924,155927,155942,155950,155956,155959,155974,155987,156002,156005,156114,156117,156129,156132,156149,156152,156158,156165,156167,156176,156181],[22,155794,155795,155796,155800],{},"I have been working on a ton of different Angular projects lately and some of them are going to be included in my ",[677,155797,155799],{"href":149517,"rel":155798},[681],"Angular 4 Java Developers Course",". There might be times when you have one team working on the front end (Angular) and another working on the back end. There are certainly times when it makes sense to separate the tasks for these applications but there are also times when you as a developer wear many hats. Today we are going talk about the scenario where you are working on both the Angular & Spring Boot application side by side. ",[26,155802,155804],{"id":155803},"angular-spring-boot-application","Angular & Spring Boot Application",[22,155806,155807],{},"If you haven't already checked out the tutorial below please check it out. In this tutorial, I walk you through a quick application that uses Angular and Spring Boot in the same project. ",[22,155809,155810],{},[677,155811,155814],{"href":155812,"rel":155813},"https://www.youtube.com/watch?v=v7X%5C_ZHdcNvc&t=5s",[681],"https://www.youtube.com/watch?v=v7X\\_ZHdcNvc&t=5s",[22,155816,155817],{},"I had someone that watched this video and reached out to me asking how the build process works. So this is a good time to talk through how the build works if you're working on both applications at once.",[636,155819,155821],{"id":155820},"building-angular","Building Angular",[22,155823,155824],{},"If you saw towards the end of the tutorial we were building the Angular application so that it would place all of the files in the /src/main/resources directory. I want to dive into that process a little further and explain how we make that happen. If you look at the package.json file inside of your Angular application you will see a scripts block that looks something like this. ",[52,155826,155828],{"className":3671,"code":155827,"language":3673,"meta":57,"style":57}," \"scripts\": {\n \"ng\": \"ng\",\n \"start\": \"ng serve --proxy-config proxy-conf.json\",\n \"build\": \"ng build\",\n \"test\": \"ng test\",\n \"lint\": \"ng lint\",\n \"e2e\": \"ng e2e\"\n },\n",[59,155829,155830,155836,155848,155860,155871,155882,155893,155903],{"__ignoreMap":57},[62,155831,155832,155834],{"class":64,"line":65},[62,155833,125580],{"class":1675},[62,155835,3688],{"class":72},[62,155837,155838,155841,155843,155846],{"class":64,"line":76},[62,155839,155840],{"class":149}," \"ng\"",[62,155842,3696],{"class":72},[62,155844,155845],{"class":1675},"\"ng\"",[62,155847,3338],{"class":72},[62,155849,155850,155853,155855,155858],{"class":64,"line":82},[62,155851,155852],{"class":149}," \"start\"",[62,155854,3696],{"class":72},[62,155856,155857],{"class":1675},"\"ng serve --proxy-config proxy-conf.json\"",[62,155859,3338],{"class":72},[62,155861,155862,155864,155866,155869],{"class":64,"line":89},[62,155863,125599],{"class":149},[62,155865,3696],{"class":72},[62,155867,155868],{"class":1675},"\"ng build\"",[62,155870,3338],{"class":72},[62,155872,155873,155875,155877,155880],{"class":64,"line":95},[62,155874,127147],{"class":149},[62,155876,3696],{"class":72},[62,155878,155879],{"class":1675},"\"ng test\"",[62,155881,3338],{"class":72},[62,155883,155884,155886,155888,155891],{"class":64,"line":101},[62,155885,125611],{"class":149},[62,155887,3696],{"class":72},[62,155889,155890],{"class":1675},"\"ng lint\"",[62,155892,3338],{"class":72},[62,155894,155895,155898,155900],{"class":64,"line":107},[62,155896,155897],{"class":149}," \"e2e\"",[62,155899,3696],{"class":72},[62,155901,155902],{"class":1675},"\"ng e2e\"\n",[62,155904,155905],{"class":64,"line":113},[62,155906,32848],{"class":72},[22,155908,155909],{},"This is almost what your scripts section will look like and if we wanted to build the project we could run the following command. ",[52,155911,155913],{"className":1663,"code":155912,"language":1665,"meta":57,"style":57},"npm run build\n",[59,155914,155915],{"__ignoreMap":57},[62,155916,155917,155919,155921],{"class":64,"line":65},[62,155918,32645],{"class":122},[62,155920,1716],{"class":1675},[62,155922,155923],{"class":1675}," build\n",[22,155925,155926],{},"What we want to do is build upon that (pun intended) and to do so we are going to add a couple more scripts. When a build is called a script called post build will be called automatically. In our post-build script, we are going to reference another script. ",[52,155928,155930],{"className":3671,"code":155929,"language":3673,"meta":57,"style":57},"\"postbuild\": \"npm run deploy\"\n",[59,155931,155932],{"__ignoreMap":57},[62,155933,155934,155937,155939],{"class":64,"line":65},[62,155935,155936],{"class":1675},"\"postbuild\"",[62,155938,3696],{"class":72},[62,155940,155941],{"class":1675},"\"npm run deploy\"\n",[22,155943,155944,155945,57647],{},"With that in place, we need to create a deploy script and have it do something. What we want to do is copy all of the files from the result of the Angular (ng) build process to our /resources/static directory. To do so we need some functionality to copy those files. We could write that ourselves or we could use a package that already exists on npm called ",[677,155946,155949],{"href":155947,"rel":155948},"https://www.npmjs.com/package/copyfiles",[681],"copyfiles",[22,155951,155952],{},[653,155953],{"alt":155954,"src":155955},"npm copy files","./2017-06-22_15-50-54.png",[22,155957,155958],{},"You can install it using the instructions above or in my case I just used \"npm install copyfiles --save-dev\". With that in place, we can now write our deploy script. ",[52,155960,155962],{"className":3671,"code":155961,"language":3673,"meta":57,"style":57},"\"deploy\": \"copyfiles -f dist/** ../resources/static\"\n",[59,155963,155964],{"__ignoreMap":57},[62,155965,155966,155969,155971],{"class":64,"line":65},[62,155967,155968],{"class":1675},"\"deploy\"",[62,155970,3696],{"class":72},[62,155972,155973],{"class":1675},"\"copyfiles -f dist/** ../resources/static\"\n",[22,155975,155976,155977,61149,155982,57647],{},"This is great and will work the first time but what about subsequent builds? We need to make sure that the resources/static directory is clean for us to run a proper build & deploy. Just as with post build we can run a script prior to another one running. In this case, I am going to run a pre-deploy script that removes that directory and then creates it. We are going to use another two packages from npm called ",[677,155978,155981],{"href":155979,"rel":155980},"https://www.npmjs.com/package/rimraf",[681],"rimraf",[677,155983,155986],{"href":155984,"rel":155985},"https://www.npmjs.com/package/mkdirp",[681],"mkdirp",[52,155988,155990],{"className":3671,"code":155989,"language":3673,"meta":57,"style":57},"\"predeploy\": \"rimraf ../resources/static/ && mkdirp ../resources/static\"\n",[59,155991,155992],{"__ignoreMap":57},[62,155993,155994,155997,155999],{"class":64,"line":65},[62,155995,155996],{"class":1675},"\"predeploy\"",[62,155998,3696],{"class":72},[62,156000,156001],{"class":1675},"\"rimraf ../resources/static/ && mkdirp ../resources/static\"\n",[22,156003,156004],{},"Now your scripts should look something like this",[52,156006,156008],{"className":3671,"code":156007,"language":3673,"meta":57,"style":57}," \"scripts\": {\n \"ng\": \"ng\",\n \"start\": \"ng serve --proxy-config proxy-conf.json\",\n \"build\": \"ng build\",\n \"postbuild\": \"npm run deploy\",\n \"predeploy\": \"rimraf ../resources/static/ && mkdirp ../resources/static\",\n \"deploy\": \"copyfiles -f dist/** ../resources/static\",\n \"test\": \"ng test\",\n \"lint\": \"ng lint\",\n \"e2e\": \"ng e2e\"\n },\n",[59,156009,156010,156016,156026,156036,156046,156058,156070,156082,156092,156102,156110],{"__ignoreMap":57},[62,156011,156012,156014],{"class":64,"line":65},[62,156013,125580],{"class":1675},[62,156015,3688],{"class":72},[62,156017,156018,156020,156022,156024],{"class":64,"line":76},[62,156019,155840],{"class":149},[62,156021,3696],{"class":72},[62,156023,155845],{"class":1675},[62,156025,3338],{"class":72},[62,156027,156028,156030,156032,156034],{"class":64,"line":82},[62,156029,155852],{"class":149},[62,156031,3696],{"class":72},[62,156033,155857],{"class":1675},[62,156035,3338],{"class":72},[62,156037,156038,156040,156042,156044],{"class":64,"line":89},[62,156039,125599],{"class":149},[62,156041,3696],{"class":72},[62,156043,155868],{"class":1675},[62,156045,3338],{"class":72},[62,156047,156048,156051,156053,156056],{"class":64,"line":95},[62,156049,156050],{"class":149}," \"postbuild\"",[62,156052,3696],{"class":72},[62,156054,156055],{"class":1675},"\"npm run deploy\"",[62,156057,3338],{"class":72},[62,156059,156060,156063,156065,156068],{"class":64,"line":101},[62,156061,156062],{"class":149}," \"predeploy\"",[62,156064,3696],{"class":72},[62,156066,156067],{"class":1675},"\"rimraf ../resources/static/ && mkdirp ../resources/static\"",[62,156069,3338],{"class":72},[62,156071,156072,156075,156077,156080],{"class":64,"line":107},[62,156073,156074],{"class":149}," \"deploy\"",[62,156076,3696],{"class":72},[62,156078,156079],{"class":1675},"\"copyfiles -f dist/** ../resources/static\"",[62,156081,3338],{"class":72},[62,156083,156084,156086,156088,156090],{"class":64,"line":113},[62,156085,127147],{"class":149},[62,156087,3696],{"class":72},[62,156089,155879],{"class":1675},[62,156091,3338],{"class":72},[62,156093,156094,156096,156098,156100],{"class":64,"line":129},[62,156095,125611],{"class":149},[62,156097,3696],{"class":72},[62,156099,155890],{"class":1675},[62,156101,3338],{"class":72},[62,156103,156104,156106,156108],{"class":64,"line":134},[62,156105,155897],{"class":149},[62,156107,3696],{"class":72},[62,156109,155902],{"class":1675},[62,156111,156112],{"class":64,"line":156},[62,156113,32848],{"class":72},[22,156115,156116],{},"When you run a build simply use the following command and this will run build, postbuild, predeploy and deploy in that order. ",[52,156118,156119],{"className":1663,"code":155912,"language":1665,"meta":57,"style":57},[59,156120,156121],{"__ignoreMap":57},[62,156122,156123,156125,156127],{"class":64,"line":65},[62,156124,32645],{"class":122},[62,156126,1716],{"class":1675},[62,156128,155923],{"class":1675},[22,156130,156131],{},"This will perform the following steps",[915,156133,156134,156140,156143,156146],{},[37,156135,156136,156137,156139],{},"Runs ",[4534,156138,49802],{}," to build your angular app which among many things will bundle your assets. ",[37,156141,156142],{},"Deletes out the /src/main/resources/static directory ",[37,156144,156145],{},"Creates /src/main/resources/static directory ",[37,156147,156148],{},"Copy all the files in the dist/ folder (result of ng build) to the /src/main/resources/static directory",[22,156150,156151],{},"All the files need will now be in the /static folder allowing you to run the Spring Boot Application.",[22,156153,156154],{},[653,156155],{"alt":156156,"src":156157},"Adding Angular Dist","./2017-06-22_16-02-14.png",[22,156159,156160,156161,156164],{},"When you visit ",[677,156162,16942],{"href":16942,"rel":156163},[681]," the index.html file of the static folder is served and your Angular application should run as intended. ",[26,156166,1499],{"id":1498},[22,156168,156169,156170,156175],{},"I just want to give a huge shoutout to ",[677,156171,156174],{"href":156172,"rel":156173},"http://amzn.to/2tTMa5D",[681],"Yakov Fain, the author of Angular 2 Development with TypeScript",". I picked up this little tip from him and can't recommend his book enough. I own that book and LOVE it. ",[22,156177,50754,156178,156180],{},[646,156179,49733],{}," Are you facing any issues developing Angular & Spring Boot applications? _",[1527,156182,156183],{},"html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}",{"title":57,"searchDepth":76,"depth":76,"links":156185},[156186,156189],{"id":155803,"depth":76,"text":155804,"children":156187},[156188],{"id":155820,"depth":82,"text":155821},{"id":1498,"depth":76,"text":1499},{"_id":156191,"path":156192,"title":156193,"description":156193,"meta":156194,"body":156199},"content/blog/2017/06/21/spring-boot-properties-setting-locale.md","/blog/2017/06/21/spring-boot-properties-setting-locale","Spring Boot Properties: Setting the locale",{"slug":156195,"date":156196,"published":13,"tags":156197,"author":-1,"cover":156198,"excerpt":-1},"spring-boot-properties-setting-locale","2017-06-21T08:19:09-04:00",[11002],"./andre-benz-256762-760x507.jpg",{"type":19,"value":156200,"toc":156466},[156201,156208,156218,156222,156225,156238,156241,156247,156250,156363,156370,156375,156379,156397,156425,156428,156433,156445,156449,156452,156458,156460,156463],[22,156202,156203,156204,156207],{},"In today's article, we are going to look at a question from a student in my ",[677,156205,51879],{"href":150302,"rel":156206},[681],". This question has to do with Internationalization and more specifically how to set the locale in the properties file of a Spring Boot application. This question comes in from Mykhaylo. ",[29685,156209,156210,156213,156217],{},[22,156211,156212],{},"Hi, I've put in my property file spring.mvc.locale=fr , but it is still messages from default messages.properties file that are displayed. If I display a value of current locale I see that this is en_GB, and not French: ",[22,156214,156216],{"th:text":156215},"${#locale}","....."," What is the way to set up a locale in SpringBoot?",[26,156219,156221],{"id":156220},"setting-the-locale","Setting the locale",[22,156223,156224],{},"Before we dive into the solution let's take a look at what the student was doing here. If you want to override the locale and give it a fixed value you can do so by setting the following property in your application.properties. ",[52,156226,156228],{"className":1663,"code":156227,"language":1665,"meta":57,"style":57},"spring.mvc.locale=fr_FR\n",[59,156229,156230],{"__ignoreMap":57},[62,156231,156232,156235],{"class":64,"line":65},[62,156233,156234],{"class":122},"spring.mvc.locale",[62,156236,156237],{"class":1675},"=fr_FR\n",[22,156239,156240],{},"In this example, we are setting the locale to French. If you aren't sure of the different locale settings, don't worry. IntelliJ will provide intelli-sense here and give you a list of choices to choose from.",[22,156242,156243],{},[653,156244],{"alt":156245,"src":156246},"Spring Boot Locale","./2017-06-21_07-58-13.png",[22,156248,156249],{},"With that in place, I went ahead and created a quick example. ",[52,156251,156253],{"className":54,"code":156252,"language":56,"meta":57,"style":57},"package com.therealdanvega.controller;\n\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.Locale;\n\n@RestController\npublic class HomeController {\n\n @RequestMapping(\"/\")\n public String home(Locale locale) {\n return locale.toString();\n }\n\n}\n",[59,156254,156255,156261,156265,156271,156277,156281,156288,156292,156298,156308,156312,156324,156340,156351,156355,156359],{"__ignoreMap":57},[62,156256,156257,156259],{"class":64,"line":65},[62,156258,69],{"class":68},[62,156260,49492],{"class":72},[62,156262,156263],{"class":64,"line":76},[62,156264,79],{"emptyLinePlaceholder":13},[62,156266,156267,156269],{"class":64,"line":82},[62,156268,27875],{"class":68},[62,156270,49517],{"class":72},[62,156272,156273,156275],{"class":64,"line":89},[62,156274,27875],{"class":68},[62,156276,27892],{"class":72},[62,156278,156279],{"class":64,"line":95},[62,156280,79],{"emptyLinePlaceholder":13},[62,156282,156283,156285],{"class":64,"line":101},[62,156284,27875],{"class":68},[62,156286,156287],{"class":72}," java.util.Locale;\n",[62,156289,156290],{"class":64,"line":107},[62,156291,79],{"emptyLinePlaceholder":13},[62,156293,156294,156296],{"class":64,"line":113},[62,156295,942],{"class":72},[62,156297,2342],{"class":68},[62,156299,156300,156302,156304,156306],{"class":64,"line":129},[62,156301,116],{"class":68},[62,156303,119],{"class":68},[62,156305,25636],{"class":122},[62,156307,126],{"class":72},[62,156309,156310],{"class":64,"line":134},[62,156311,79],{"emptyLinePlaceholder":13},[62,156313,156314,156316,156318,156320,156322],{"class":64,"line":156},[62,156315,2143],{"class":72},[62,156317,10592],{"class":68},[62,156319,2109],{"class":72},[62,156321,15635],{"class":1675},[62,156323,2212],{"class":72},[62,156325,156326,156328,156330,156332,156335,156338],{"class":64,"line":161},[62,156327,194],{"class":68},[62,156329,2469],{"class":72},[62,156331,18647],{"class":122},[62,156333,156334],{"class":72},"(Locale ",[62,156336,156337],{"class":889},"locale",[62,156339,768],{"class":72},[62,156341,156342,156344,156347,156349],{"class":64,"line":167},[62,156343,360],{"class":68},[62,156345,156346],{"class":72}," locale.",[62,156348,23175],{"class":122},[62,156350,822],{"class":72},[62,156352,156353],{"class":64,"line":173},[62,156354,223],{"class":72},[62,156356,156357],{"class":64,"line":179},[62,156358,79],{"emptyLinePlaceholder":13},[62,156360,156361],{"class":64,"line":185},[62,156362,379],{"class":72},[22,156364,156365,156366,156369],{},"I placed a debug marker on line 13, ran the application and visited ",[677,156367,16942],{"href":16942,"rel":156368},[681],". To my surprise, the locale was still what I would expect mine to show up as, en_US. ",[22,156371,156372],{},[653,156373],{"alt":156245,"src":156374},"./2017-06-21_08-04-10.png",[26,156376,156378],{"id":156377},"locale-resolver","Locale Resolver",[22,156380,156381,156382,156387,156388,156396],{},"This is actually expected behavior and it has to do with something called the ",[677,156383,156386],{"href":156384,"rel":156385},"http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/servlet/LocaleResolver.html",[681],"LocaleResolver",". This interface allows for implementations based on the request, session, cookies, etc. The default implementation is,",[677,156389,60103,156393,60103],{"href":156390,"rel":156391,"title":156392},"http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/servlet/i18n/AcceptHeaderLocaleResolver.html",[681],"class in org.springframework.web.servlet.i18n",[59,156394,156395],{},"AcceptHeaderLocaleResolver"," simply using the request's locale provided by the respective HTTP header. This means that no matter what you set the locale to Spring is always going to look at the HTTP request header to determine the locale. Luckily for us we can override the LocaleResolver using another property in Spring Boot. ",[52,156398,156400],{"className":54,"code":156399,"language":56,"meta":57,"style":57},"spring.mvc.locale=fr_FR\nspring.mvc.locale-resolver=fixed\n",[59,156401,156402,156411],{"__ignoreMap":57},[62,156403,156404,156406,156408],{"class":64,"line":65},[62,156405,156234],{"class":72},[62,156407,146],{"class":68},[62,156409,156410],{"class":72},"fr_FR\n",[62,156412,156413,156415,156417,156420,156422],{"class":64,"line":76},[62,156414,156234],{"class":72},[62,156416,11635],{"class":68},[62,156418,156419],{"class":72},"resolver",[62,156421,146],{"class":68},[62,156423,156424],{"class":72},"fixed\n",[22,156426,156427],{},"With this setting in place, our application should now use the locale that you set in your properties file. ",[22,156429,156430],{},[653,156431],{"alt":156245,"src":156432},"./2017-06-21_08-12-21.png",[22,156434,156435,156438,156439,156444],{},[4534,156436,156437],{},"If you think you had this working at one point in your application you"," aren't_ crazy. This worked in Spring Boot 1.3.7 but then in 1.4 ",[677,156440,156443],{"href":156441,"rel":156442},"https://github.com/spring-projects/spring-boot/commit/d54474b81cd7e1ae08d1e7200da725df2fd23e13",[681],"there was a change"," and if you want it to work going forward you must set the locale resolver. _",[26,156446,156448],{"id":156447},"setting-the-locale-screencast","Setting the locale Screencast",[22,156450,156451],{},"I created a quick screencast to walk through setting the locale in your Spring Boot application. Please subscribe to my YouTube channel to get notifications about new screencasts. ",[22,156453,156454],{},[677,156455,156456],{"href":156456,"rel":156457},"https://www.youtube.com/watch?v=nt27SPF10vY",[681],[26,156459,1499],{"id":1498},[22,156461,156462],{},"I want to thank Mykhaylo again for the question. This is one of those things that has probably tripped most of us at some point.",[1527,156464,156465],{},"html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .spoOn, html code.shiki .spoOn{--shiki-default:#E36209;--shiki-dark:#FFAB70;--shiki-sepia:#E36209}",{"title":57,"searchDepth":76,"depth":76,"links":156467},[156468,156469,156470,156471],{"id":156220,"depth":76,"text":156221},{"id":156377,"depth":76,"text":156378},{"id":156447,"depth":76,"text":156448},{"id":1498,"depth":76,"text":1499},{"_id":156473,"path":156474,"title":156475,"description":156475,"meta":156476,"body":156480},"content/blog/2017/06/19/spring-boot-2-0-m2-now-available.md","/blog/2017/06/19/spring-boot-2-0-m2-now-available","Spring Boot 2.0 M2 Now Available",{"slug":156477,"date":156478,"published":13,"tags":156479,"author":-1,"cover":54703,"excerpt":-1},"spring-boot-2-0-m2-now-available","2017-06-19T08:30:53-04:00",[11002],{"type":19,"value":156481,"toc":156582},[156482,156485,156489,156507,156512,156515,156517,156520,156524,156527,156531,156534,156538,156541,156545,156548,156552,156555,156557,156564,156570,156573,156576],[22,156483,156484],{},"A few weeks ago we got our first look at the next major version of Spring Boot. We talked about all kinds of exciting things such as a Java 8 baseline, Spring Framework 5 support and the new Gradle plugin. In this article, we take a quick look at the next iteration of Spring Boot 2.0, milestone 2. ",[26,156486,156488],{"id":156487},"spring-boot-20-m2","Spring Boot 2.0 M2",[22,156490,156491,156492,156496,156497,156502,156503,156506],{},"If you want to read the full release notes you can do so ",[677,156493,8441],{"href":156494,"rel":156495},"https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0.0-M2-Release-Notes",[681],". This release covers over ",[677,156498,156501],{"href":156499,"rel":156500},"https://github.com/spring-projects/spring-boot/milestone/54?closed=1",[681],"90 issues and"," pull requests. If you want to test out any of the new features you can start a new project using ",[677,156504,40207],{"href":40207,"rel":156505},[681]," or by firing up my favorite IDE, IntelliJ. ",[22,156508,156509],{},[653,156510],{"alt":156488,"src":156511},"./2017-06-16_21-27-09-1024x645.png",[22,156513,156514],{},"We won't go through all of the features here but I will mention a few of them. ",[636,156516,147097],{"id":147096},[22,156518,156519],{},"Spring Boot 2 provides support for the Quartz scheduler that can be used via the spring-boot-starter-quartz dedicated starter. Both in-memory and jdbc stores can be configured.",[636,156521,156523],{"id":156522},"spring-data-web-configuration","Spring Data Web configuration",[22,156525,156526],{},"Spring Boot exposes a new spring.data.web configuration namespace that allows to easily configure paging and sorting.",[636,156528,156530],{"id":156529},"json-starter","JSON starter",[22,156532,156533],{},"A new spring-boot-starter-json starter gathers the necessary bits to read and write json. It provides not only jackson-databind but also useful modules when working with Java8: jackson-datatype-jdk8, jackson-datatype-jsr310 and jackson-module-parameter-names. This new starter is now used where jackson-databind was previously defined.",[636,156535,156537],{"id":156536},"thymeleaf-starter","Thymeleaf starter",[22,156539,156540],{},"The Thymeleaf starter now includes thymeleaf-extras-java8time out of the box. ",[636,156542,156544],{"id":156543},"jdbctemplate","JdbcTemplate",[22,156546,156547],{},"The JdbcTemplate that Spring Boot auto-configures can now be customized via the spring.jdbc.template namespace. Also, the NamedParameterJdbcTemplate that is auto-configured reuses the JdbcTemplate behind the scenes.",[636,156549,156551],{"id":156550},"jooq","jOOQ",[22,156553,156554],{},"Spring Boot detects the jOOQ dialect automatically based on the DataSource (similarly to what is done for the JPA dialect). Also a @JooqTest has been introduced to ease testing where only jOOQ has to be used. ",[26,156556,55337],{"id":55336},[22,156558,55340,156559,156563],{},[677,156560,51879],{"href":156561,"rel":156562},"http://courses.www.danvega.dev/p/spring-boot-intro",[681]," please do. This course is based on Spring Boot 1.3 and was very much an introduction. I am already planning a curriculum for a Spring 2.0 course is coming and I will be releasing it sometime around SpringOne. ",[22,156565,55354,156566,156569],{},[677,156567,55359],{"href":151865,"rel":156568},[681]," and signup for updates. Anyone on this list will be the first to find out when it’s released and will receive a discount. ",[26,156571,156572],{"id":1498},"CONCLUSION",[22,156574,156575],{},"We are starting to get a real good look at the next version of Spring Boot. I don't know about you but I am looking forward to everything Spring Framework 5 has to offer. ",[22,156577,156578],{},[4534,156579,156580,55372],{},[646,156581,49733],{},{"title":57,"searchDepth":76,"depth":76,"links":156583},[156584,156592,156593],{"id":156487,"depth":76,"text":156488,"children":156585},[156586,156587,156588,156589,156590,156591],{"id":147096,"depth":82,"text":147097},{"id":156522,"depth":82,"text":156523},{"id":156529,"depth":82,"text":156530},{"id":156536,"depth":82,"text":156537},{"id":156543,"depth":82,"text":156544},{"id":156550,"depth":82,"text":156551},{"id":55336,"depth":76,"text":55337},{"id":1498,"depth":76,"text":156572},{"_id":156595,"path":156596,"title":156597,"description":156597,"meta":156598,"body":156603},"content/blog/2017/06/16/migrating-grails-2-x-applications-grails-3-x.md","/blog/2017/06/16/migrating-grails-2-x-applications-grails-3-x","Migrating Grails 2.x Applications to Grails 3.x",{"slug":156599,"date":156600,"published":13,"tags":156601,"author":-1,"cover":156602,"excerpt":-1},"migrating-grails-2-x-applications-grails-3-x","2017-06-16T08:53:25-04:00",[73330],"./pexels-photo-436784-760x507.jpeg",{"type":19,"value":156604,"toc":157047},[156605,156608,156612,156615,156621,156624,156628,156631,156636,156650,156654,156663,156669,156672,156686,156689,156693,156696,156711,156714,156836,156839,156881,156884,156888,156891,156918,156921,156997,157000,157017,157019,157022,157028,157034,157036,157039,157044],[22,156606,156607],{},"At we work we have a number of huge applications running on Grails 2. When we first heard about the Grails 3 release we couldn't wait to upgrade our applications and then we realized we couldn't. About a year ago our team started to do some investigation into what it would take to upgrade all of our applications. What we quickly found out is that our applications weren't the problem, it was all of the plugins. In this article, we are going to walk through the problems that faced us and I am proud to say that those problems no longer exist today. We are in the middle of upgrading all of our applications at work and I will let you know how we are doing it. ",[26,156609,156611],{"id":156610},"grails-plugins","Grails Plugins",[22,156613,156614],{},"When I first go into Grails It was the plugin architecture of the framework that really drew me to it. I remember writing the same applications over and over again and having to spend days on boilerplate project code dealing with things like rest, security and data access. At work, we ended up moving a lot of our projects from ColdFusion over to Groovy & Grails. As I mentioned before about a year ago we decided that we wanted to move all of these applications over to Grails 3.x and ran into some problems.",[22,156616,156617],{},[653,156618],{"alt":156619,"src":156620},"Grails 3 Upgrade Problems","./pexels-photo-52608-1024x685.jpeg",[22,156622,156623],{},"Each Grails application is made up a bunch of plugins, some of which are public plugins and some are built in house. In Grails 3 there were a lot of changes to plugins and what this meant is that plugins that were written for Grails 2.x were not going to be compatible with 3.x We knew that all of the in-house plugins were under our control so as long as we wanted to put in the work we could get those moved over. It was all of the public plugins that our applications and even other plugins used. We found out that of the nearly 45-50 plugins that we were using in our applications that maybe 10 of them were compatible. With that, we would have to delay our migration project to Grails 3. ",[636,156625,156627],{"id":156626},"grails-3-plugins","Grails 3 Plugins",[22,156629,156630],{},"If you look today there are 218 plugins in Grails 3 which isn't anything near the Grails 2 plugins but it is enough to get you going. All of the major plugins have been updated and I would say 98% of all the plugins that we use have been updated. ",[22,156632,156633],{},[653,156634],{"alt":156627,"src":156635},"./2017-06-16_07-37-18-1024x822.png",[22,156637,156638,156639,19931,156644,156649],{},"The other thing you have to remember is that Grails 3 now uses Gradle. This means that a lot of actions that might have been in a plugin before can now be moved to Gradle tasks. I know for us we built a code quality plugin in-house that was mainly made up of ",[677,156640,156643],{"href":156641,"rel":156642},"http://codenarc.sourceforge.net/",[681],"CodeNarc",[677,156645,156648],{"href":156646,"rel":156647},"https://www.atlassian.com/software/clover",[681],"Clover Code Coverage",". There are still no plugins for Grails 3 for either of these but there is a Gradle plugin for Clover and CodeNarc can be moved into a Gradle task. Grails 3 is also built on Spring Boot which gives us spring boot starters that provide functionality. All of these combined with the new features of Grails 3 means there are some plugins that are no longer needed so don't let the raw number of plugins fool you. ",[26,156651,156653],{"id":156652},"migrate-from-grails-2x-to-3x","Migrate from Grails 2.x to 3.x",[22,156655,156656,156657,156662],{},"I am not going to go through every single step of how to migrate an application. The reason I am not going to is there is a great ",[677,156658,156661],{"href":156659,"rel":156660},"http://docs.grails.org/3.0.x/guide/upgrading.html",[681],"step by step migration guide on the Grails website"," that you can and should check out. Before you go jumping in the deep end and into a migration I thought I would just share a little bit of what I have learned throughout this process with you. ",[22,156664,156665],{},[653,156666],{"alt":156667,"src":156668},"Grails 3 Migration","./pexels-photo-207919-1024x683.jpeg",[22,156670,156671],{},"The first thing you will need to focus on is all of your plugins. I would go through each of your applications and plugins and make a list of all the plugins that you use. I divide these up into public plugins and in-house plugins. You will want to make sure all of the public plugins are supported. If they aren't there are a few questions you can ask yourself:",[915,156673,156674,156677,156680,156683],{},[37,156675,156676],{},"Can we move this functionality to our own plugin? ",[37,156678,156679],{},"Is there another plugin that does something similar? ",[37,156681,156682],{},"Is there a Gradle task that replaces this?",[37,156684,156685],{},"Do we even need a plugin for this? ",[22,156687,156688],{},"In the case of that last question, we had a jQuery and jQuery-UI plugin that were no longer compatible. Since all they really did was include the jQuery libraries we knew that we could just do that manually and actually didn't need a plugin. ",[636,156690,156692],{"id":156691},"grails-3-plugin","Grails 3 Plugin",[22,156694,156695],{},"The first step is to create a new Grails 3 plugin using the command line:",[52,156697,156699],{"className":1663,"code":156698,"language":1665,"meta":57,"style":57},"grails create-plugin quartz\n",[59,156700,156701],{"__ignoreMap":57},[62,156702,156703,156705,156708],{"class":64,"line":65},[62,156704,73330],{"class":122},[62,156706,156707],{"class":1675}," create-plugin",[62,156709,156710],{"class":1675}," quartz\n",[22,156712,156713],{},"The next step is to copy the sources from the original Grails 2 plugin to the Grails 3 plugin:",[52,156715,156717],{"className":1663,"code":156716,"language":1665,"meta":57,"style":57},"# first the sources\ncp -rf ../quartz-2.x/src/groovy/ src/main/groovy\ncp -rf ../quartz-2.x/src/java/ src/main/groovy\ncp -rf ../quartz-2.x/grails-app/ grails-app\ncp -rf ../quartz-2.x/QuartzGrailsPlugin.groovy src/main/groovy/grails/plugins/quartz\n# then the tests\ncp -rf ../quartz-2.x/test/unit/\\* src/test/groovy\nmkdir -p src/integration-test/groovy\ncp -rf ../quartz-2.x/test/integration/\\* src/integration-test/groovy\n\n# then templates / other resources\ncp -rf ../quartz-2.x/src/templates/ src/main/templates\n",[59,156718,156719,156724,156738,156749,156761,156773,156778,156792,156802,156815,156819,156824],{"__ignoreMap":57},[62,156720,156721],{"class":64,"line":65},[62,156722,156723],{"class":85},"# first the sources\n",[62,156725,156726,156729,156732,156735],{"class":64,"line":76},[62,156727,156728],{"class":122},"cp",[62,156730,156731],{"class":149}," -rf",[62,156733,156734],{"class":1675}," ../quartz-2.x/src/groovy/",[62,156736,156737],{"class":1675}," src/main/groovy\n",[62,156739,156740,156742,156744,156747],{"class":64,"line":82},[62,156741,156728],{"class":122},[62,156743,156731],{"class":149},[62,156745,156746],{"class":1675}," ../quartz-2.x/src/java/",[62,156748,156737],{"class":1675},[62,156750,156751,156753,156755,156758],{"class":64,"line":89},[62,156752,156728],{"class":122},[62,156754,156731],{"class":149},[62,156756,156757],{"class":1675}," ../quartz-2.x/grails-app/",[62,156759,156760],{"class":1675}," grails-app\n",[62,156762,156763,156765,156767,156770],{"class":64,"line":95},[62,156764,156728],{"class":122},[62,156766,156731],{"class":149},[62,156768,156769],{"class":1675}," ../quartz-2.x/QuartzGrailsPlugin.groovy",[62,156771,156772],{"class":1675}," src/main/groovy/grails/plugins/quartz\n",[62,156774,156775],{"class":64,"line":101},[62,156776,156777],{"class":85},"# then the tests\n",[62,156779,156780,156782,156784,156787,156789],{"class":64,"line":107},[62,156781,156728],{"class":122},[62,156783,156731],{"class":149},[62,156785,156786],{"class":1675}," ../quartz-2.x/test/unit/",[62,156788,66539],{"class":149},[62,156790,156791],{"class":1675}," src/test/groovy\n",[62,156793,156794,156797,156799],{"class":64,"line":113},[62,156795,156796],{"class":122},"mkdir",[62,156798,31726],{"class":149},[62,156800,156801],{"class":1675}," src/integration-test/groovy\n",[62,156803,156804,156806,156808,156811,156813],{"class":64,"line":129},[62,156805,156728],{"class":122},[62,156807,156731],{"class":149},[62,156809,156810],{"class":1675}," ../quartz-2.x/test/integration/",[62,156812,66539],{"class":149},[62,156814,156801],{"class":1675},[62,156816,156817],{"class":64,"line":134},[62,156818,79],{"emptyLinePlaceholder":13},[62,156820,156821],{"class":64,"line":156},[62,156822,156823],{"class":85},"# then templates / other resources\n",[62,156825,156826,156828,156830,156833],{"class":64,"line":161},[62,156827,156728],{"class":122},[62,156829,156731],{"class":149},[62,156831,156832],{"class":1675}," ../quartz-2.x/src/templates/",[62,156834,156835],{"class":1675}," src/main/templates\n",[22,156837,156838],{},"While the above will work we realized this wasn't going to work for us. Like most developers, we use source control and if we just straight copy the files we would lose all revision history. If you're using Git this is probably going to be a little bit easier than we had it in Subversion. Either way, this is something you need to be thinking of if keeping that history is important to you. You will need to add a package declaration to the plugin descriptor. In this case, QuartzGrailsPlugin is modified as follows:",[52,156840,156842],{"className":54,"code":156841,"language":56,"meta":57,"style":57},"// add package declaration\npackage grails.plugins.quartz\nclass QuartzGrailsPlugin {\n}\n",[59,156843,156844,156849,156856,156877],{"__ignoreMap":57},[62,156845,156846],{"class":64,"line":65},[62,156847,156848],{"class":85},"// add package declaration\n",[62,156850,156851,156853],{"class":64,"line":76},[62,156852,69],{"class":68},[62,156854,156855],{"class":72}," grails.plugins.quartz\n",[62,156857,156858,156860,156863,156866,156869,156872,156874],{"class":64,"line":82},[62,156859,11671],{"class":23824},[62,156861,156862],{"class":23824}," Q",[62,156864,156865],{"class":72},"uartz",[62,156867,156868],{"class":23824},"G",[62,156870,156871],{"class":72},"rails",[62,156873,70182],{"class":23824},[62,156875,156876],{"class":72},"lugin {\n",[62,156878,156879],{"class":64,"line":89},[62,156880,379],{"class":72},[22,156882,156883],{},"The rest of the steps can be found in the Grails Migration guide. Some of them will apply to you and some of them won't. Either way, I would make sure you guy step by step and make sure you do everything that they go through. ",[636,156885,156887],{"id":156886},"grails-3-application","Grails 3 Application",[22,156889,156890],{},"Now that we have resolved all of our plugins we can move on to migrating our application. Just like the plugins, it is so much easier to create a new Grails 3 application and then move/merge/migrate code over from your Grails 2.x application. Once the plugins are Grails 3.x compatible you can upgrade the application. To upgrade an application it is again best to create a new Grails 3 application using the \"web\" profile:",[52,156892,156894],{"className":1663,"code":156893,"language":1665,"meta":57,"style":57},"$ grails create-app myapp\n$ cd myapp\n",[59,156895,156896,156909],{"__ignoreMap":57},[62,156897,156898,156900,156903,156906],{"class":64,"line":65},[62,156899,19949],{"class":122},[62,156901,156902],{"class":1675}," grails",[62,156904,156905],{"class":1675}," create-app",[62,156907,156908],{"class":1675}," myapp\n",[62,156910,156911,156913,156916],{"class":64,"line":76},[62,156912,19949],{"class":122},[62,156914,156915],{"class":1675}," cd",[62,156917,156908],{"class":1675},[22,156919,156920],{},"The next step is to copy the sources from the original Grails 2 application to the Grails 3 application:",[52,156922,156924],{"className":1663,"code":156923,"language":1665,"meta":57,"style":57},"# first the sources\ncp -rf ../old_app/src/groovy/ src/main/groovy\ncp -rf ../old_app/src/java/ src/main/groovy\ncp -rf ../old_app/grails-app/ grails-app\n# then the tests\ncp -rf ../old_app/test/unit/ src/test/groovy\nmkdir -p src/integration-test/groovy\ncp -rf ../old_app/test/integration/ src/integration-test/groovy\n",[59,156925,156926,156930,156941,156952,156963,156967,156978,156986],{"__ignoreMap":57},[62,156927,156928],{"class":64,"line":65},[62,156929,156723],{"class":85},[62,156931,156932,156934,156936,156939],{"class":64,"line":76},[62,156933,156728],{"class":122},[62,156935,156731],{"class":149},[62,156937,156938],{"class":1675}," ../old_app/src/groovy/",[62,156940,156737],{"class":1675},[62,156942,156943,156945,156947,156950],{"class":64,"line":82},[62,156944,156728],{"class":122},[62,156946,156731],{"class":149},[62,156948,156949],{"class":1675}," ../old_app/src/java/",[62,156951,156737],{"class":1675},[62,156953,156954,156956,156958,156961],{"class":64,"line":89},[62,156955,156728],{"class":122},[62,156957,156731],{"class":149},[62,156959,156960],{"class":1675}," ../old_app/grails-app/",[62,156962,156760],{"class":1675},[62,156964,156965],{"class":64,"line":95},[62,156966,156777],{"class":85},[62,156968,156969,156971,156973,156976],{"class":64,"line":101},[62,156970,156728],{"class":122},[62,156972,156731],{"class":149},[62,156974,156975],{"class":1675}," ../old_app/test/unit/",[62,156977,156791],{"class":1675},[62,156979,156980,156982,156984],{"class":64,"line":107},[62,156981,156796],{"class":122},[62,156983,31726],{"class":149},[62,156985,156801],{"class":1675},[62,156987,156988,156990,156992,156995],{"class":64,"line":113},[62,156989,156728],{"class":122},[62,156991,156731],{"class":149},[62,156993,156994],{"class":1675}," ../old_app/test/integration/",[62,156996,156801],{"class":1675},[22,156998,156999],{},"_* Again I want to point out that if you are using source control this is a step that you really need to think about. _ As I mentioned earlier one of the best additions to Grails 3 is the addition of Gradle. The repositories and dependencies defined in grails-app/conf/BuildConfig.groovy of the original Grails 2.x application will need to be defined in build.gradle of the new Grails 3.x application. We aren't going to go step by step, that can be found in the official documentation but here are the steps remaining. ",[915,157001,157002,157005,157008,157011,157014],{},[37,157003,157004],{},"Modify Package Imports",[37,157006,157007],{},"Migrate Configuration",[37,157009,157010],{},"Migrate web.xml modifications to Spring",[37,157012,157013],{},"Migrate static assets not handled by Asset Pipeline ",[37,157015,157016],{},"Migrate Tests",[26,157018,4098],{"id":4097},[22,157020,157021],{},"The Official Grails Migration Guide that I have mentioned at least 10x in this article is the best resource you have. Outside of that I found 2 really great presentations from GR8Conf Europe 2016",[22,157023,157024],{},[677,157025,157026],{"href":157026,"rel":157027},"https://www.youtube.com/watch?v=IhehO9aM5bk",[681],[22,157029,157030,67945],{},[677,157031,157032],{"href":157032,"rel":157033},"https://www.youtube.com/watch?v=Qfrp1cbXdVA&t=814s",[681],[26,157035,1499],{"id":1498},[22,157037,157038],{},"If you looked at migrating in the past but felt you couldn't because of the plugin landscape I think it is about time you gave it another look. ",[22,157040,50754,157041,157043],{},[646,157042,49733],{}," If you have already migrated what issues did you come across? If you haven't, what are you waiting for? _",[1527,157045,157046],{},"html pre.shiki code .sZnax, html code.shiki .sZnax{--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#6F42C1}html pre.shiki code .suV6U, html code.shiki .suV6U{--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#032F62}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);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .s4-Ip, html code.shiki .s4-Ip{--shiki-default:#6A737D;--shiki-dark:#6A737D;--shiki-sepia:#6A737D}html pre.shiki code .sECI1, html code.shiki .sECI1{--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#005CC5}html pre.shiki code .sibLe, html code.shiki .sibLe{--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#D73A49}html pre.shiki code .s-uPX, html code.shiki .s-uPX{--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#24292E}html pre.shiki code .shOWo, html code.shiki .shOWo{--shiki-default:#B31D28;--shiki-default-font-style:italic;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic;--shiki-sepia:#B31D28;--shiki-sepia-font-style:italic}",{"title":57,"searchDepth":76,"depth":76,"links":157048},[157049,157052,157056,157057],{"id":156610,"depth":76,"text":156611,"children":157050},[157051],{"id":156626,"depth":82,"text":156627},{"id":156652,"depth":76,"text":156653,"children":157053},[157054,157055],{"id":156691,"depth":82,"text":156692},{"id":156886,"depth":82,"text":156887},{"id":4097,"depth":76,"text":4098},{"id":1498,"depth":76,"text":1499},{"posts":157059,"totalPages":129,"currentPage":89,"totalPosts":16333},[67351,68054,69074,69861,71843],["Reactive",157061],{"$scolor-mode":157062,"$ssite-config":157065},{"preference":157063,"value":157063,"unknown":13,"forced":157064},"system",false,{"_context":157066,"_priority":157069,"env":155337,"name":157073,"url":8472},{"name":157067,"env":157063,"url":157068},"vendorEnv","runtimeEnv",{"name":157070,"env":157071,"url":157072},-5,-15,0,"vega-nuxt",["Set"],["ShallowReactive",157076],{"blog-posts-page-4-limit-5-tag-spring":-1,"blog-posts-all":-1},"/blog/4?tag=spring"]