<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ mvp - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ mvp - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Wed, 06 May 2026 17:00:29 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/mvp/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Build an MVP for Your Project – and Why You Should Do It ]]>
                </title>
                <description>
                    <![CDATA[ Proof of concept, prototypes, wireframes, mockups… what actually constitutes a Minimum Viable Product (MVP)? Well, it's a product with just enough features to gather comprehensive qualitative feedback. In practice, it's as easy to understand the conc... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/minimum-viable-product-between-an-idea-and-the-product/</link>
                <guid isPermaLink="false">66be14c2b712ab343b0b912f</guid>
                
                    <category>
                        <![CDATA[ app development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ mvp ]]>
                    </category>
                
                    <category>
                        <![CDATA[ software development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Oleh Romanyuk ]]>
                </dc:creator>
                <pubDate>Fri, 24 May 2024 13:00:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2020/02/MVP-as-a-Bicyle.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Proof of concept, prototypes, wireframes, mockups… what actually constitutes a Minimum Viable Product (MVP)?</p>
<p>Well, it's a product with just enough features to gather comprehensive qualitative feedback.</p>
<p>In practice, it's as easy to understand the concept of an MVP as it is to ride a bicycle. Let's do it then.</p>
<p>In this article, I am going to explain:</p>
<ul>
<li><a class="post-section-overview" href="#heading-what-is-an-mvp">What is an MVP?</a></li>
<li><a class="post-section-overview" href="#heading-the-benefits-of-an-mvp">The benefits of an MVP</a></li>
<li><a class="post-section-overview" href="#how-mvp-software-development-is-conducted">How is an MVP developed?</a></li>
<li><a class="post-section-overview" href="#how-would-an-mvp-be-developed-if-it-was-a-bicycle">How would an MVP be developed if it was a bicycle?</a></li>
<li><a class="post-section-overview" href="#heading-why-is-an-mvp-important">Why is it important to build an MVP?</a></li>
<li><a class="post-section-overview" href="#heading-do-you-have-an-idea-for-an-mvp">Do you have an idea for an MVP?</a></li>
<li><a class="post-section-overview" href="#heading-faq">FAQ</a></li>
</ul>
<p>All your customers want to be heard and understood. In the world of software, there is a large number of apps and websites out there, but only a few get the attention and love of the users. So you can start by creating a minimum viable product to find out if your idea has a place in this competitive environment.</p>
<p>Building an MVP doesn’t really cost much. And it helps you investigate the market competition with minimum time and budget loss (in the worst case). You’ll discover the whole story, from benefits and prototype types to the common example of your future developed MVP. So…what are you waiting for?</p>
<h2 id="heading-what-is-an-mvp">What is an MVP?</h2>
<p>The most basic version of a product that can be released to test a business idea is called a minimum viable product (MVP). This concept was popularised by Eric Ries in The Lean Startup. It is part of the ethos of the <a target="_blank" href="https://theleanstartup.com/">Lean Startup</a> methodology. With minimal initial investment, this framework focuses on efficiency and learning from customer feedback.</p>
<p>An MVP is a perfect opportunity to let your potential users voice their opinions and test out a product before its final launch. Gathering and analyzing qualitative feedback is a primary task of MVP development. Based on these findings, you can modify your MVP and test it again.</p>
<p>This process turns into a cycle of MVP product development which takes place over and over again until the ultimate customer satisfaction is reached.</p>
<p>MVPs are all about testing product ideas, analyzing user feedback, and building a full product version based on what you learn. Experts say that it's not an MVP until you can sell it in the B2B world. However, before diving into the process of how to manage an MVP, let’s learn about its main benefits.</p>
<h2 id="heading-the-benefits-of-an-mvp">The Benefits of an MVP</h2>
<p>All these benefits demonstrate a strategic approach that goes far beyond just cost savings and speed to market. It's about smart, customer-centric development, where each iteration brings a product closer to the heart of what users want. </p>
<p>Here are some of the many benefits of building an MVP:</p>
<h3 id="heading-1-product-hypothesis-validation">1. Product Hypothesis Validation</h3>
<p>You can test hypotheses directly with MVPs. MVPs help test the market's response to a product by focusing on building core features. This process helps founders refine their business model with real-world insights rather than guesswork.</p>
<h3 id="heading-2-minimising-capital-investment">2. Minimising Capital Investment</h3>
<p>The development of an MVP requires less investment than the launch of a fully developed end product. This approach aligns with the Lean Startup methodology, which stresses the importance of using just enough features to demonstrate the concept without overburdening resources. It allows startups to allocate their budget more effectively and mitigate financial risks.</p>
<h3 id="heading-3-getting-investors-to-trust-and-get-funded">3. Getting Investors to Trust and Get Funded</h3>
<p>Presenting a well-developed MVP is often crucial to attracting investor interest and raising venture capital funding. The demonstration of a working MVP helps angel investors and venture capitalists visualize the potential of a business idea, making it easier to secure start-up funding.</p>
<h3 id="heading-4-refine-for-product-market-fit">4. Refine for Product-Market Fit</h3>
<p>An MVP is an ideal tool for refining product-market fit, which is a dynamic process. It allows startups to closely align their product with market needs, based on user feedback. This ensures that the final product is well-received by its target audience, increasing the potential for success.</p>
<h3 id="heading-5-gather-customer-feedback">5. Gather Customer Feedback</h3>
<p>The key to the success of any business is gathering feedback from customer research. An MVP provides a platform for engaging early adopters and gathering valuable feedback with a focus on core features.</p>
<p>Such user feedback is critical for startups to understand customer needs and preferences, prioritize the product roadmap, and develop the product for greater market penetration.</p>
<p>Now, you are ready to face the question: “How do you manage MVP software development?”.   </p>
<p><img src="https://lh7-us.googleusercontent.com/baDuR-1JR0cpX5OEJK2ZQL7XY5EK6NKqSDi2eM_MzY91XOscOxKH0BHxGSDbUKc5q_Hi4sWiSNcSm529yP3Vll5v4MDITVXy8iYWCtOvLz0GVutglkk5PD-mOfqZQliFTMgBae01DuaQPP9eC5n93uk" alt="Image" width="600" height="400" loading="lazy">
<em>Stages of MVP development</em></p>
<h2 id="heading-how-to-do-mvp-software-development">How to Do MVP Software Development</h2>
<p>Every product is different, and so is the process of its development. Before we jump into the details of how an MVP is created, I want to point out that it is an individual and iterative process.</p>
<h3 id="heading-product-discovery">Product Discovery</h3>
<p>Your first task is to make your idea come to life. You can start by running product discovery. You need to study the needs, interests, and demographic characteristics of the target audience. Also, analyze the strengths and weaknesses of competitors.</p>
<p>You will then need to go through all the features that can be implemented and select the essential ones. The next thing is to organize this information and present it during the workshops with the help of graphs, charts, tables, or any other form of visualization that you feel best represents the data.</p>
<p>Now, your idea should seem clearer but it is still not presentable.</p>
<h3 id="heading-proof-of-concept">Proof of Concept</h3>
<p>Next, you need to create a Proof of Concept. Basically, this is aimed at summarizing the discovery stage and verifying that your innovative idea can be implemented in real life.</p>
<p>Ok, you know that your idea is feasible and comprehensive. You know that it can be done – but how?</p>
<h3 id="heading-user-journey">User Journey</h3>
<p>Now you need to understand what the user wants to see once they open the application. One of the starting points for an MVP is having a strong sense of your customer. Startups should carefully build a target user profile, taking into account various factors that affect buying decisions and product usage. </p>
<p>To build this target user profile, you have to identify the following:</p>
<ul>
<li>Industry: the type of sector your product serves.</li>
<li>Pain problems: challenges your product aims to solve.</li>
<li>Buying decisions: How they make their decisions and what channels they prefer to use.</li>
<li>Contexts: In which way they would use your product.</li>
</ul>
<p>To do this, you need to map a user journey. User journeys are a visual representation of a potential user and their experience with your app. They cover everything from the moment the user realizes they need this service, to the moment they first find and click through to your app, to the moment they decide to make this service part of their lifestyle.</p>
<p>User journeys resemble a set of statements, which look something like this:</p>
<p><em>As a [user role], I want to do [functions] so that [goal].</em></p>
<p>For example, "As a website admin, I want to be able to add or remove users to keep the app free of spammers". Or, "As an unregistered user, I want to be able to open a menu so I can understand what the application offers before I sign up.”</p>
<h3 id="heading-prototype">Prototype</h3>
<p>After that, it's time to start prototyping. A prototype is a simplified version of the product. It demonstrates the final product design and navigation. Basically, it is a set of pictures of the interface of your future app. If it is clickable, you can navigate between screens by clicking buttons to understand user flows. </p>
<p>Prototypes may even look like a very basic version of your platform or mobile app. It is not a final product and not an MVP because you cannot show it to actual users.</p>
<p>Here, you have your idea implemented. Kind of. It can be shown to all the stakeholders but not to the end user as long as it is just a rough draft. </p>
<p>Now comes the most interesting part: you have to decide the type of MVP you’re going to use. We’ll take a look only at the 5 most popular types (but there are more). So, here we go:</p>
<h4 id="heading-wizard-of-oz-mvp">Wizard of Oz MVP</h4>
<p>This approach gives the user the illusion of a service or product that runs without any human interference – but in reality, behind the scenes, it's all done manually by people. </p>
<p>For example, for developing a digital assistant app, the staff will manually answer the customer instead of complex AI infrastructure. The MVP will allow you to check the standards and collect suggestions from users to make improvements without a big initial input.</p>
<h4 id="heading-concierge-mvp">Concierge MVP</h4>
<p>Similar to the Wizard of Oz MVP, the Concierge MVP is a front for the final automated response of a manually transformed customized service. </p>
<p>For example, if your startup is a food delivery app, you can start by doing order and delivery management manually, rather than building a highly automated platform from the beginning. This is particularly important in customer satisfaction in industries that derive value from personalization and a human touch.</p>
<h4 id="heading-single-feature-mvps">Single-feature MVPs</h4>
<p>In the beginning, you shouldn't try to put together a product with too many features. Instead, develop and release just a single core feature that solves a problem. This will allow you to quickly test the pricing proposition of the product and gather customer feedback for improvements. </p>
<p>In general, prioritizing central functions can speed up development and reduce the likelihood of building redundant skills.</p>
<h4 id="heading-pre-order-mvp">Pre-Order MVP</h4>
<p>This involves placing pre-orders before developing or producing an actual product. With interest and pre-order funding, early sales can be used to help generate visibility and improve the product. This method reduces the chance of developing something that is not going to fit into the market and is perfect for fast iterations based purely on real names.</p>
<h4 id="heading-minimum-viable-demos">Minimum Viable Demos</h4>
<p>You can also put something together that demonstrates your central price proposition but isn’t a totally functional product. This could be a model, a prototype, or an interactive presentation that shows what the product could do, rather than reserving funds for full improvements.</p>
<p>A basic live demo will allow you to make sales to traders, get initial stakeholder comments, and test the market functionality of your idea. This is a very strong way to test concepts before committing any resources to development.</p>
<p>As soon as you have all the info we can continue our journey. This is only a beginning. So, let’s take a look at the next phase.</p>
<h3 id="heading-minimum-viable-product-development">Minimum Viable Product Development</h3>
<p>At this stage, you need to make some ultimate decisions about the UI/UX and finalize the visual design. After that, it's time to start coding the minimum viable product. </p>
<p>The Minimum Viable Product looks like a final app and feels like a final app. But it has fewer features, the design or performance is not necessarily production quality, and the code quality may be lower. Your idea is presented, you've put in the code, and it's partially implemented – it's now ready to meet its first user.</p>
<h3 id="heading-minimum-viable-product-launch-and-testing">Minimum Viable Product Launch and Testing</h3>
<p>After you finish development and launch the MVP, you should present it to a sample set of actual users. Throughout the next few days or weeks, you'll gather customers’ feedback, analyze the results, and modify your MVP accordingly.</p>
<p>Once you see that your customers are fully satisfied, you can start implementing the final product.</p>
<h2 id="heading-example-of-developing-an-mvp-lets-build-a-bicycle">Example of Developing an MVP – Let's Build a Bicycle</h2>
<p>Let’s imagine that you want to create a bicycle. A cool, stylish, hardwearing, and sustainable bike. But what if a potential client can't figure out what you have in mind and you lose all your hard work? What if you need cash and you have to convince your investors that your idea is viable? Well, it's time to build an MVP.</p>
<p>The development process will be pretty similar to what I described earlier. So let's go through it with this example product to see how everything works.</p>
<h3 id="heading-product-discovery-1">Product Discovery:</h3>
<p>You conduct a discovery stage: learn about what a bike is, what parts it consists of, what bikes people like, and what riders complain about. How can you make your bike stand out? </p>
<p>Through studies and marketplace evaluation, you discover insights into customer alternatives and pain points, and lay the groundwork for using new techniques. After that, you answer the most important question: what you can do to make your bicycle stand out among all the others?</p>
<h3 id="heading-proof-of-concept-1">Proof of Concept:</h3>
<p>Let’s say you found out how to create a bicycle chain that never falls off the chain ring. Once you have a clear idea of how to do this, you create your mechanism: a chain, with a chainring and pedals – your proof of concept. </p>
<p>With a clean idea in hand, throw your concept out there to investors. You tell them more about your idea, and receive their approval and support to keep going with the project.</p>
<h3 id="heading-prototype-1">Prototype:</h3>
<p>But it's not quite time to build the final bicycle, as you have not seen it yet in its actual size. So now you create a full-scale copy of the bicycle, carefully choose all the colors and materials, and make it resemble a real product.</p>
<p>Still, the pedals won’t spin yet, and you won't be able to steer or actually ride it. This is your prototype – looks pretty impressive and realistic but does not work yet.</p>
<h3 id="heading-mvp-development">MVP Development:</h3>
<p>Your investors again review the idea and approve your design, but now they need to see the functionality. You again create a full-scale bicycle, and now it has working wheels, pedals, brakes, gears, and a seat. That is going to be your MVP.</p>
<p>At this point, you can let your users try it out. They get on a bike, test it, and share their opinion with you. The more people try it, the more complete your feedback is. Just be sure not to show your bicycle to people you do not trust, or they might leak your idea to a next-door producer who also makes bikes for a living.</p>
<h3 id="heading-mvp-testing">MVP Testing:</h3>
<p>Finally, you modify your product based on what your customers have to say until you are sure that you've got it right. Each improvement brings you closer to perfection, ensuring your bike meets and beats expectations. Through rigorous testing and iteration, you build a product that not only meets your goals but also delights with functionality and format.</p>
<h3 id="heading-final-product-development">Final Product Development:</h3>
<p>Only after all these steps, when you have received financial support from your investors and the approval of your customers, are you ready for the launch.</p>
<p>You change out the wooden seat for a cushioned one, install safety lights on your bicycle, lubricate the bicycle chain, put on stickers and a bell, and start selling your product. A strong advertising and promotional campaign announces the arrival of your product. The culmination of creativity and hard work marks the start of a new technology in the world of cycling.</p>
<h2 id="heading-why-is-an-mvp-important">Why is an MVP Important?</h2>
<p>A Minimum Viable Product has one main advantage, but it's a very important one: it allows you to test your future product in real-life settings with actual users.</p>
<p>This simple benefit has a lot of positive consequences:</p>
<ul>
<li>An MVP lets you adjust your product development plan before it's too late.</li>
<li>It serves as a warning for any mistakes you make or as confirmation for good business decisions.</li>
<li>This approach saves you a great deal of time, effort, and money by optimizing the planning process and reducing risks.</li>
<li>A Minimum Viable Product boosts motivation because the team knows that what they do matters.</li>
<li>MVP development offers a unique experience of testing the product idea, which will definitely come in handy in your professional life in the future.</li>
<li>The MVP approach can and should be used within industries of all sorts. While for manufacturers of traditional goods it's a long and strenuous process, for software developers it can be rather simple and accessible.</li>
</ul>
<p>Some businesses may choose to disregard the minimum viable product stage when creating something innovative – and that may be understandable in certain spaces. But in my opinion, for a software development company it's detrimental.</p>
<p>Once a team has launched a Minimum Viable Product, they can decide whether the venture is on the right track. The most important outcome of an MVP is to get valuable statistics from early users. It's the customers who provide information about the first-class performance of the project. The statistics you get can be used to plan future improvements and prioritize which features should be implemented first.</p>
<p>If you decide to take a risk and implement your idea before checking in with your target audience, you're potentially placing money, time, effort, energy, inspiration, and supporters on the line.</p>
<h2 id="heading-do-you-have-an-idea-for-an-mvp">Do you have an idea for an MVP?</h2>
<p>My company Covent IT is experienced in developing Minimum Viable Products. In case you need a free estimate for a similar project, feel free to <a target="_blank" href="https://keenethics.com/contacts?activeform=estimate">get in touch</a><em>.</em> </p>
<p>If you have enjoyed the article, you should continue with <a target="_blank" href="https://coventit.com/blog/How-IT-Outsourcing-Saves-Costs">How IT Outsourcing Saves Costs for Your Company</a> and <a target="_blank" href="https://coventit.com/blog/risks-of-it-outsourcing">Avoiding Pitfalls in IT Outsourcing: Tips for Minimizing Risks</a>.</p>
<h2 id="heading-faq">FAQ</h2>
<h3 id="heading-how-to-spend-fewer-resources">How to spend fewer resources</h3>
<p>Remember that you should create an MVP with a minimum of time, money, and effort. Think about how you can spend less and still effectively test your business idea. Discussing this question will usually help you choose the MVP functionality to implement in the early stages of new product development.</p>
<h3 id="heading-how-do-you-interact-with-users">How do you interact with users?</h3>
<p>One of the main goals of creating an MVP is to test your hypotheses and determine the need and value of the product. Feedback from the first users of the product helps you achieve this goal. In order not to miss any important information, think about how you will interact with the target audience: through reviews, surveys, direct interviews, and so on.</p>
<h3 id="heading-how-do-you-make-the-first-sales-of-a-product">How do you make the first sales of a product?</h3>
<p>The first sales of the product will give you the means to develop it further and to see if there is interest in your product. You could also consider organizing a pre-sale on a crowdfunding platform such as Kickstarter.</p>
<h3 id="heading-how-do-you-promote-a-product">How do you promote a product?</h3>
<p>Plan the promotion campaign and the channels you will use. Google Adwords is usually the main tool. Then choose social networks (Facebook, Instagram, and so on), create official pages, and start targeting. You can also collect feedback on social networks.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Minimum Viable Product ]]>
                </title>
                <description>
                    <![CDATA[ A minimum viable product (MVP) is a product with just enough features to be usable by early adopters. The goal is to get feedback from these early adopters and learn what features to add or remove from the product before releasing it to a wider audie... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-a-minimum-viable-product/</link>
                <guid isPermaLink="false">66b202ed08bc664c3c097e96</guid>
                
                    <category>
                        <![CDATA[ mvp ]]>
                    </category>
                
                    <category>
                        <![CDATA[ youtube ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Beau Carnes ]]>
                </dc:creator>
                <pubDate>Wed, 20 Jul 2022 20:00:53 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/07/mvp.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>A minimum viable product (MVP) is a product with just enough features to be usable by early adopters. The goal is to get feedback from these early adopters and learn what features to add or remove from the product before releasing it to a wider audience.</p>
<p>In this course, you will learn how to quickly build an MVP for your application.</p>
<p>Ania Kubow teaches this course. Ania works for freeCodeCamp and has her own popular YouTube channel where she creates courses on a variety of technical topics.</p>
<p>In this course, you will learn why MVPs are important and how to easily create one with the Retool platform. You will learn to create an MVP that interacts with a database, uses APIs, and allows users to upload &amp; send messages. And the skills you learn can apply to many other types of applications.</p>
<p>Retool provided a grant to make this course possible. Learn more at <a target="_blank" href="https://retool.com/">https://retool.com</a>.</p>
<p>Here are the sections covered in this course:</p>
<ul>
<li>What is an MVP?</li>
<li>Why is an MVP useful?</li>
<li>Getting started with Retool</li>
<li>Adding components</li>
<li>Adding MongoDB database</li>
<li>Inserting data</li>
<li>Populating feed with data</li>
<li>Updating CSS</li>
<li>Adding a trigger</li>
<li>Create timeline of posts</li>
<li>Add messages feature</li>
<li>Add upload feature</li>
<li>Configure Amazon S3</li>
<li>Create uploader</li>
<li>Conclusion</li>
</ul>
<p>Watch the full course below or <a target="_blank" href="https://www.youtube.com/watch?v=ChTGbmR2NeM">on the freeCodeCamp.org YouTube channel</a> (1-hour watch).</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/ChTGbmR2NeM" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Use Model-View-ViewModel on Android Like a Pro ]]>
                </title>
                <description>
                    <![CDATA[ My goal in this article is to explain why the Model-View-ViewModel architectural pattern presents a very awkward separation of concerns in some situations with regard to the presentation logic of a GUI architecture. We will explore two variants of MV... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/model-view-viewmodel-android-tutorial/</link>
                <guid isPermaLink="false">66d460d1787a2a3b05af43fc</guid>
                
                    <category>
                        <![CDATA[ Android ]]>
                    </category>
                
                    <category>
                        <![CDATA[ android app development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ mvp ]]>
                    </category>
                
                    <category>
                        <![CDATA[ software architecture ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Ryan Michael Kay ]]>
                </dc:creator>
                <pubDate>Mon, 28 Dec 2020 15:21:14 +0000</pubDate>
                <media:content url="https://cdn-media-2.freecodecamp.org/w1280/5fe0dcbae6787e098394168f.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>My goal in this article is to explain why the Model-View-ViewModel architectural pattern presents a very awkward separation of concerns in some situations with regard to the presentation logic of a GUI architecture.</p>
<p>We will explore two variants of MVVM (there is <strong>not</strong> just one way to do it), and the reasons why you may prefer one variant over another, based on project requirements.</p>
<h2 id="heading-mvvm-vs-mvpmvc">MVVM vs MVP/MVC?</h2>
<p>It is quite likely that the most common question I am asked during my live Sunday Q&amp;A sessions, is something like:</p>
<blockquote>
<p>MVVM vs MVP/MVC?</p>
</blockquote>
<p>Whenever I am asked this question, I am quick emphasize the idea that no single GUI architecture works great in all situations.</p>
<p>Why, you may ask? The best architecture (or at least a good choice) for a given application depends strongly on the requirements at hand.</p>
<p>Let us briefly think about what this word <strong>requirements</strong> actually means:</p>
<ul>
<li><p><strong>How complex is your UI?</strong> A simple UI does not generally require complex logic to coordinate it, whereas a complex UI may require extensive logic and fine-grained control to work smoothly.</p>
</li>
<li><p><strong>How much do you care about testing?</strong> Generally speaking, classes which are tightly coupled to frameworks and the OS (especially the <strong>user interface</strong>) require extra work to test.</p>
</li>
<li><p><strong>How much re-usability and abstraction do you wish to promote?</strong> What if you want to share the back end, domain, and even presentation logic of your application across different platforms?</p>
</li>
<li><p>Are you by nature <strong>pragmatic</strong>, <strong>perfectionist</strong>, <strong>lazy</strong>, or all of the above at different times, in different situations?</p>
</li>
</ul>
<p>I would love to write an article where I discuss in fine detail how MVVM works with respect to the requirements and concerns listed above. Unfortunately, some of you have likely been mislead into thinking that there is only one way to do MVVM.</p>
<p>Instead, I will discuss two different approaches to the general idea of MVVM which present very distinct benefits and disadvantages. But first, let us start with the general idea.</p>
<h2 id="heading-thou-shalt-not-reference-thy-view-classes">Thou Shalt Not Reference Thy View Classes</h2>
<p><em>For my friends who cannot read old English:</em> “<strong>You may not reference view classes</strong>."</p>
<p>Apart from using the name ViewModel (which itself is confusing if the class is full of <strong>logic</strong>), the one iron-clad rule of MVVM architecture is that you may never reference a View, from ViewModel.</p>
<p>Now, the first area of confusion can arise from this word “reference,” which I will restate using several different levels of jargon:</p>
<ul>
<li><p>Your ViewModels may not possess any references (member variables, properties, mutable/immutable fields) to any Views</p>
</li>
<li><p>Your ViewModels may not depend on any Views</p>
</li>
<li><p>Your ViewModels may not talk directly to your Views</p>
</li>
</ul>
<p>Now, on the Android platform, the reason for this rule is not simply that breaking it is bad because someone who seems to know about software architecture told you it is bad.</p>
<p>When using the <a target="_blank" href="https://developer.android.com/topic/libraries/architecture/viewmodel">ViewModel</a> class from Architecture Components (which is designed to have its instance <strong>persist</strong> longer than the Fragment/Activity lifecycle <strong>when appropriate</strong>), referencing a View is asking for <strong>SERIOUS MEMORY LEAKS</strong>.</p>
<p>As for why MVVM in general does not allow such references, the goal is <strong>hypothetically</strong> to make both the View and the ViewModel easier to test and write.</p>
<p>Others may also point out that it promotes reusability of ViewModels, but this is <strong>exactly where things break down with this pattern</strong>.</p>
<p>Before we look at the code, please note that <strong>I personally do not use LiveData</strong> in my own production code. I prefer to write my own Publisher-Subscriber Pattern these days, but what I say below applies to any library which allows for the PubSub/Observer Pattern link from the ViewModel to the View.</p>
<p>This article is accompanied by a video tutorial covering many of the same ideas here:</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/j47CSoJ_Hc4" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p> </p>
<h2 id="heading-viewlogic-viewmodel-or-view-viewmodelcontroller">ViewLogic + ViewModel or View + ViewModelController?</h2>
<p>When I said “break down” in the previous section, I do not mean to say that the pattern literally breaks. I mean that it breaks down into (at least) two different approaches which have very distinct appearances, benefits, and consequences.</p>
<p>Let us consider these two approaches, and when you may wish to prefer one over the other.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/12/1_TfbPt5-CcYCjDu2hapFXNg.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Boromir explains that MVVM is not a magic wand that makes your application's presentation logic disappear.</em></p>
<h3 id="heading-first-approach-prioritize-reusable-viewmodels">First Approach: Prioritize Reusable ViewModels</h3>
<p>As far as I can tell, most people who implement MVVM make it a goal to promote re-usability of ViewModels, so that they may be reused for <em>n</em> number of different Views (many-to-one ratio).</p>
<p>In simple terms, there are two ways you can achieve this re-usability:</p>
<ul>
<li><p>By not referencing a specific View. Hopefully this is not news to you at this point.</p>
</li>
<li><p>By <strong>knowing</strong> as little as possible about the details of the <strong>UI</strong> in general</p>
</li>
</ul>
<p>The second point may sound vague or counter-intuitive (how can it know anything about something which it does not reference?), so I think it is time to look at some code:</p>
<pre><code class="lang-kotlin"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">NoteViewModel</span></span>(<span class="hljs-keyword">val</span> repo: NoteRepo): ViewModel(){
    <span class="hljs-comment">//Note: you may also publish data to the View via Databinding, RxJava Observables, and other approaches. Although I do not like to use LiveData in back end classes, it works great with Android front end with AAC</span>
    <span class="hljs-keyword">val</span> noteState: MutableLiveData&lt;Note&gt;()
    <span class="hljs-comment">//...</span>
    <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">handleEvent</span><span class="hljs-params">(event: <span class="hljs-type">NoteEvent</span>)</span></span> {
        <span class="hljs-keyword">when</span> (event) {
            <span class="hljs-keyword">is</span> NoteEvent.OnStart -&gt; getNote(event.noteId)
            <span class="hljs-comment">//...</span>
        }
    }
    <span class="hljs-keyword">private</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">getNote</span><span class="hljs-params">(noteId: <span class="hljs-type">String</span>)</span></span>{
        noteState.value = repo.getNote(noteId)
    }
}
</code></pre>
<p>While this is a very simplified example, the point is that the only thing which this particular ViewModel exposes publicly (other than the handleEvent function), is a simple Note object:</p>
<pre><code class="lang-kotlin"><span class="hljs-keyword">data</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Note</span></span>(<span class="hljs-keyword">val</span> creationDate:String,
                <span class="hljs-keyword">val</span> contents:String,
                <span class="hljs-keyword">val</span> imageUrl: String,
                <span class="hljs-keyword">val</span> creator: User?)
</code></pre>
<p>With this particular approach, the ViewModel is well and truly decoupled from not just a particular View, but also the details, and by extension, <strong>presentation logic</strong> of any particular View.</p>
<p>If what I am saying still seems vague, I promise it will be clear once I describe the other approach.</p>
<p>Although my earlier heading, “<strong>ViewLogic + ViewModel…</strong>” is not meant to be used or taken seriously, I mean that by having very decoupled and re-usable ViewModels, we are now depending on the View itself to do the work of figuring out how to render/bind this Note object on screen.</p>
<p><strong>Some of us do not like filling View classes with Logic.</strong></p>
<p>This is where things get very muddy and dependent on project <strong>requirements</strong>. I am not saying that filling View classes with logic such as…:</p>
<pre><code class="lang-pgsql">private fun observeViewModel() {
    viewModel.notes.observe(
        viewLifecycleOwner,
        Observer { notes: List&lt;Note&gt; -&gt;
            <span class="hljs-keyword">if</span> (notes.isEmpty()) showEmptyState()
            <span class="hljs-keyword">else</span> showNoteList(notes)
        }
    )
   //..
}
</code></pre>
<p>…is <strong>always</strong> a bad thing, but classes which are tightly coupled to the platform (like Fragments) are difficult to test, and classes with logic in them are the most important classes to test!</p>
<p>In a word, it is a failure to apply what I consider to be the golden principle of any good architecture: <strong>Separation of concerns</strong>**.**</p>
<p>My personal opinion is that it is worth it to apply separation of concerns to a very high degree. But make no mistake that plenty of cash cow applications have been written by people who do not have the faintest clue about what that means.</p>
<p>In any case, the approach we will discuss next, while <strong>having its own side effects</strong>, once again removes the presentation logic from the View.</p>
<p>Well, most of it anyways.</p>
<h3 id="heading-second-approach-humble-view-control-freak-viewmodel">Second Approach: Humble View, Control-Freak ViewModel</h3>
<p>Sometimes not having fine-grained control over your Views (which is a consequence of prioritizing re-usability of ViewModels), actually kind of sucks.</p>
<p>To make me even less enthusiastic about applying the previous approach indiscriminately, I find that I <strong>often</strong> <strong>do not</strong> <strong>need to reuse a ViewModel</strong>.</p>
<blockquote>
<p><em>Ironically, “too much abstraction” is a common critique of MVP over MVVM.</em></p>
</blockquote>
<p>With that being said, one cannot simply add a reference back in to the ViewModel in order to regain this fine-grained control over the View. That would basically just be MVP + memory leaks (assuming you are still using ViewModel from AAC).</p>
<p>The alternative then, is to build your ViewModels such that they contain almost all of the <strong>behaviour</strong>, <strong>state</strong>, and <strong>presentation logic</strong> of a given View. The View must still bind to the ViewModel of course, but enough details about the View are present in the ViewModel that the View’s functions are reduced to one liners (with small exceptions).</p>
<p>In Martin Fowler’s naming conventions, this is known as <a target="_blank" href="https://martinfowler.com/eaaDev/PassiveScreen.html">Passive View/Screen</a>. A more generally applicable name for this approach is the <strong>Humble Object Pattern</strong>.</p>
<p>In order to achieve this, you must essentially have your ViewModel possess an observable field (however you achieve that – data binding, Rx, LiveData, whatever) for every control or widget present in the View:</p>
<pre><code class="lang-kotlin"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UserViewModel</span></span>(
    <span class="hljs-keyword">val</span> repo: IUserRepository,
){

    <span class="hljs-comment">//The actual data model is kept private to avoid unwanted tampering</span>
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">val</span> userState = MutableLiveData&lt;User&gt;()

    <span class="hljs-comment">//Control Logic</span>
    <span class="hljs-keyword">internal</span> <span class="hljs-keyword">val</span> authAttemptState = MutableLiveData&lt;<span class="hljs-built_in">Unit</span>&gt;()
    <span class="hljs-keyword">internal</span> <span class="hljs-keyword">val</span> startAnimation = MutableLiveData&lt;<span class="hljs-built_in">Unit</span>&gt;()

    <span class="hljs-comment">//UI Binding</span>
    <span class="hljs-keyword">internal</span> <span class="hljs-keyword">val</span> signInStatusText = MutableLiveData&lt;String&gt;()
    <span class="hljs-keyword">internal</span> <span class="hljs-keyword">val</span> authButtonText = MutableLiveData&lt;String&gt;()
    <span class="hljs-keyword">internal</span> <span class="hljs-keyword">val</span> satelliteDrawable = MutableLiveData&lt;String&gt;()

    <span class="hljs-keyword">private</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">showErrorState</span><span class="hljs-params">()</span></span> {
        signInStatusText.value = LOGIN_ERROR
        authButtonText.value = SIGN_IN
        satelliteDrawable.value = ANTENNA_EMPTY
    }
    <span class="hljs-comment">//...</span>
}
</code></pre>
<p>Subsequently, the View will still need to wire itself up to the ViewModel, but the functions required to do so become trivially simple to write:</p>
<pre><code class="lang-kotlin"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">LoginView</span> : <span class="hljs-type">Fragment</span></span>() {

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">lateinit</span> <span class="hljs-keyword">var</span> viewModel: UserViewModel
    <span class="hljs-comment">//...</span>

    <span class="hljs-comment">//Create and bind to ViewModel</span>
    <span class="hljs-keyword">override</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">onStart</span><span class="hljs-params">()</span></span> {
        <span class="hljs-keyword">super</span>.onStart()
        viewModel = ViewModelProviders.of(
        <span class="hljs-comment">//...   </span>
        ).<span class="hljs-keyword">get</span>(UserViewModel::<span class="hljs-keyword">class</span>.java)

        //start background anim
        (root_fragment_login.background <span class="hljs-keyword">as</span> AnimationDrawable).startWithFade()

        setUpClickListeners()
        observeViewModel()

        viewModel.handleEvent(LoginEvent.OnStart)
    }

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">fun</span> setUpClickListeners() {
      //...
    }

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">fun</span> observeViewModel() {
        viewModel.signInStatusText.observe(
            viewLifecycleOwner,
            Observer {
                //<span class="hljs-string">"it"</span> <span class="hljs-keyword">is</span> the value of the MutableLiveData <span class="hljs-keyword">object</span>, which <span class="hljs-keyword">is</span> inferred to be a String automatically
                lbl_login_status_display.text = it
            }
        )

        viewModel.authButtonText.observe(
            viewLifecycleOwner,
            Observer {
                btn_auth_attempt.text = it
            }
        )

        viewModel.startAnimation.observe(
            viewLifecycleOwner,
            Observer {
                imv_antenna_animation.setImageResource(
                    resources.getIdentifier(ANTENNA_LOOP, <span class="hljs-string">"drawable"</span>, activity?.packageName)
                )
                (imv_antenna_animation.drawable <span class="hljs-keyword">as</span> AnimationDrawable).start()
            }
        )

        viewModel.authAttemptState.observe(
            viewLifecycleOwner,
            Observer { startSignInFlow() }
        )

        viewModel.satelliteDrawable.observe(
            viewLifecycleOwner,
            Observer {
                imv_antenna_animation.setImageResource(
                    resources.getIdentifier(it, <span class="hljs-string">"drawable"</span>, activity?.packageName)
                )
            }
        )
    }
</code></pre>
<p>You can find the full code for this example <a target="_blank" href="https://github.com/BracketCove/JetpackNotesMvvmKotlin/tree/master/app/src/main/java/com/wiseassblog/jetpacknotesmvvmkotlin/login">here</a>.</p>
<p>As you have probably noticed, we are probably not going to be re-using this ViewModel <strong>anywhere else</strong>. Also, our View has become sufficiently humble (depending on your standards and preferences for code coverage), and very easy to write.</p>
<p>Sometimes you will run in to situations where you must find some kind of half-measure between the distribution of <strong>presentation logic</strong> between Views and ViewModels, which does not strictly follow either of these approaches.</p>
<p>I am not advocating one approach over another, but rather encouraging you to be flexible in your approach, based on the requirements at hand.</p>
<h2 id="heading-choose-your-architecture-based-on-preferences-and-requirements">Choose Your Architecture Based on Preferences And Requirements</h2>
<p>The point of this article was to look at two different approaches which a developer can take in terms of constructing a MVVM style GUI architecture on the Android Platform (with some carry over to other platforms).</p>
<p>In truth, we could get more specific about small differences even within these two approaches.</p>
<ul>
<li><p>Should the View observe a field for every individual widget/control it possesses, or should it observe one field which publishes a single <strong>model</strong> to render the entire View anew each time?</p>
</li>
<li><p>Maybe we could avoid having to make our ViewModels one-to-one, while keeping our Views as Humble Objects, simply by adding something like a Presenter or Controller to the mix?</p>
</li>
</ul>
<p>Talk is cheap, and I strongly advise you to try and learn these things <strong>in the code</strong> so that you do not need to rely on people like me to tell you what to do.</p>
<p>Ultimately, I think the two elements which make for a great architecture come down to the following considerations:</p>
<p>Firstly, play with several approaches until you find one which you <strong>prefer</strong>. This is best done by actually building an application (it can be simple) in each style, and seeing what <strong>feels right</strong>.</p>
<p>Secondly, understand that preferences aside, different styles will tend to emphasize different benefits in exchange for different deficits. Eventually, you will be able to pick good choices based on your understanding of the project requirements rather than <strong>blind faith</strong>.</p>
<h3 id="heading-learn-more-about-software-architecture">Learn More About Software Architecture:</h3>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/B_C41SF0KbI" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p> </p>
<h4 id="heading-social">Social</h4>
<p><a target="_blank" href="https://www.instagram.com/wiseassbrand/">https://www.instagram.com/rkay301/</a><br><a target="_blank" href="https://www.facebook.com/wiseassblog/">https://www.facebook.com/wiseassblog/</a><br><a target="_blank" href="https://twitter.com/wiseass301">https://twitter.com/wiseass301</a><br><a target="_blank" href="http://wiseassblog.com/">http://wiseassblog.com/</a></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How I Built and Shipped My First MVP ]]>
                </title>
                <description>
                    <![CDATA[ By JavaScript Joe On June 29th, I shared the MVP of mentored.dev on Twitter–my first "real" project that was bigger than anything I'd built before and something I was excited for other people to use.  https://twitter.com/jsjoeio/status/11449945802002... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-i-built-shipped-my-first-mvp/</link>
                <guid isPermaLink="false">66d45f63706b9fb1c166b983</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ lessons learned ]]>
                    </category>
                
                    <category>
                        <![CDATA[ mvp ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Mon, 15 Jul 2019 13:15:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2019/07/Screen-Shot-2019-07-11-at-8.05.29-PM.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By JavaScript Joe</p>
<p>On June 29th, I shared the MVP of <a target="_blank" href="https://mentored.dev">mentored.dev</a> on Twitter–my first "real" project that was bigger than anything I'd built before and something I was excited for other people to use. </p>
<div class="embed-wrapper">
        <blockquote class="twitter-tweet">
          <a href="https://twitter.com/jsjoeio/status/1144994580200210432?s=20"></a>
        </blockquote>
        <script defer="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script></div>
<p>After sharing this, I received some bits of positive feedback, including a shout-out in the <a target="_blank" href="https://t.co/7sCziRMC9f?amp=1">npm weekly newsletter</a>. </p>
<p>I thought I'd share the story behind the whole process.</p>
<h2 id="heading-origin-of-the-idea">Origin of the Idea</h2>
<p>I can't remember when I first had the idea but a while back when I was introduced to <a target="_blank" href="https://www.twilio.com/quest">TwilioQuest</a>, I thought to myself, </p>
<blockquote>
<p>Wouldn't it be cool to build a "gamified" learning platform that taught you how to code?</p>
</blockquote>
<p>Like many other people, I have these ideas at random times throughout my life. I keep a list of these ideas in a <a target="_blank" href="https://trello.com/en-US">Trello</a> board called "IDEAS". Looking here, I can see I notated this on January 21st, 2019. </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/07/trello-card1.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Trello board with original idea from Jan. 21st</em></p>
<p>I knew a few things:</p>
<ul>
<li>I wanted it to be interactive</li>
<li>I wanted it to feel like a game</li>
<li>I wanted it to have quick exercises</li>
</ul>
<hr>
<h2 id="heading-where-to-start">Where to Start?</h2>
<p>Around that same time, I was wrapping up a freelance project (porting a Jekyll theme to a Gatsby site) so I didn't feel like I was ready to start it just yet. Then, I had a conversation with <a target="_blank" href="https://twitter.com/signalnerve">@signalnerve</a> on Twitter that sparked my motivation:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/07/Screen-Shot-2019-07-09-at-6.20.11-PM.png" alt="Image" width="600" height="400" loading="lazy">
<em>Screenshot of Twitter conversation that motivated me to start.</em></p>
<blockquote>
<p>Build a small app–a real MVP–validate your idea and then decide if you should keep building. </p>
</blockquote>
<p>So I thought, "What the heck, why not start it?"</p>
<hr>
<h2 id="heading-march-2019">March 2019</h2>
<p>I used a Gatsby/TypeScript starter to kickstart the first and pushed my <a target="_blank" href="https://github.com/jsjoeio/mentored.dev/commit/0e38821f30d1f6f1bca804315fe24ccd5d5baf05">first commit</a>. Originally, I named the repo "Life of Code" because that's what came to mind but later I renamed it after buying the mentored.dev domain. </p>
<h4 id="heading-initial-wireframes">Initial Wireframes</h4>
<p>After creating the repo, I sketched up some elementary wireframes in <a target="_blank" href="https://figma.com">Figma</a></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/07/figma.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Initial Wireframes in Figma</em></p>
<p>Once I had all this, I felt over the initial "where-do-I-begin-paralysis" and knew I needed to keep the momentum going. </p>
<h4 id="heading-taking-input">Taking Input</h4>
<p>One of the first things I tried was asking for user input and showing that in a message. This would be useful for the dialog between the narrator and the user. </p>
<div class="embed-wrapper">
        <blockquote class="twitter-tweet">
          <a href="https://twitter.com/jsjoeio/status/1103860530605780998?s=20"></a>
        </blockquote>
        <script defer="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script></div>
<h3 id="heading-basic-dialog-working">Basic Dialog Working</h3>
<p>Even though it didn't look pretty, the logic for the dialog was working! This felt like a good milestone because most of the hard stuff was done. </p>
<div class="embed-wrapper">
        <blockquote class="twitter-tweet">
          <a href="https://twitter.com/jsjoeio/status/1106779197614088192?s=20"></a>
        </blockquote>
        <script defer="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script></div>
<h3 id="heading-narrator-character-talking">Narrator Character Talking</h3>
<p>I struggled a lot figuring out the best way to get the narrator talking but after finding <code>[react-keyframes](https://github.com/zeit/react-keyframes)</code>, I was able to figure out a solution. This was huge because previously I hadn't done much with animation.</p>
<div class="embed-wrapper">
        <blockquote class="twitter-tweet">
          <a href="https://twitter.com/jsjoeio/status/1107812366891180032?s=20"></a>
        </blockquote>
        <script defer="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script></div>
<h3 id="heading-getting-feedback-on-dialog">Getting Feedback on Dialog</h3>
<p>As stated earlier, I think it's important to get input from others. I don't know if Twitter is the best place to do it but fortunately for me, the people who responded to my request for feedback were kind.</p>
<div class="embed-wrapper">
        <blockquote class="twitter-tweet">
          <a href="https://twitter.com/jsjoeio/status/1108190126876680193?s=20"></a>
        </blockquote>
        <script defer="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script></div>
<h3 id="heading-migrating-to-typescript">Migrating to TypeScript</h3>
<p>I used a Gatsby-TypeScript starter for this project because I had been meaning to learn TS. However, up until this point, I wasn't actually using TS. The files just had .ts or .tsx endings.</p>
<p>Before the 30th, I had mentioned wanting to learn TS and <a target="_blank" href="https://twitter.com/TejasKumar_">@TejasKumar_</a> offered to teach me by migrating the mentored.dev codebase over to TS on a Google Hangouts livestream. This was one of the coolest moments of this project. And I learned a ton. </p>
<div class="embed-wrapper">
        <blockquote class="twitter-tweet">
          <a href="https://twitter.com/jsjoeio/status/1112088320182370304?s=20"></a>
        </blockquote>
        <script defer="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script></div>
<hr>
<h2 id="heading-april-2019">April 2019</h2>
<h3 id="heading-adding-a-profile-card-component">Adding a "Profile Card" Component</h3>
<p>Next up after finishing the dialog part of the project, I decided to focus on the Dashboard - or the page after you logged in. I created a simple "Profile Card" that will eventually show your experience, any code-cash you have, etc.</p>
<div class="embed-wrapper">
        <blockquote class="twitter-tweet">
          <a href="https://twitter.com/jsjoeio/status/1113644342172774400?s=20"></a>
        </blockquote>
        <script defer="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script></div>
<h3 id="heading-designing-the-dashboard">Designing the Dashboard</h3>
<p>In hindsight, I may have gotten ahead of myself here because I designed way more than I could implement in the MVP but at least it gave me an idea for the future. I first added it as hard-coded components but later commented out to maintain a healthy UX.</p>
<div class="embed-wrapper">
        <blockquote class="twitter-tweet">
          <a href="https://twitter.com/jsjoeio/status/1114009915545141249?s=20"></a>
        </blockquote>
        <script defer="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script></div>
<h3 id="heading-designing-the-campus-map">Designing the Campus Map</h3>
<p>This took way longer than I thought. I wanted it to feel like a university campus but drew heavily from <a target="_blank" href="https://bulbapedia.bulbagarden.net/wiki/Pallet_Town">Pallet Town</a> in Pokemon. The completed version has more but at least I had something I could add to the Dashboard. I designed all of this in Figma and exported it as SVG. Working with SVGs in React has proven to be a delightful experience. </p>
<div class="embed-wrapper">
        <blockquote class="twitter-tweet">
          <a href="https://twitter.com/jsjoeio/status/1114635991191396352?s=20"></a>
        </blockquote>
        <script defer="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script></div>
<h3 id="heading-adding-gameplay-music">Adding Gameplay Music</h3>
<p>I never realized how hard it is to create or find music for a game. I ended up finding this amazing sound artist named <a target="_blank" href="https://www.soundimage.org">Eric Matyas</a> who makes music and sounds royalty-free. I wanted the audio to start automatically (because that's how most games do it) but unfortunately that is <a target="_blank" href="https://a11yproject.com/posts/never-use-auto-play/">not accessible</a> so it does not auto-play. </p>
<p>However, if you turn it on at the start menu or when you're playing the game, it adds a nice touch.</p>
<div class="embed-wrapper">
        <blockquote class="twitter-tweet">
          <a href="https://twitter.com/jsjoeio/status/1115436705346019328?s=20"></a>
        </blockquote>
        <script defer="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script></div>
<h3 id="heading-changing-maps">Changing Maps</h3>
<p>This has to be my favorite feature I added–being able to change the map. At first, I had no idea how I was going to do this, then I thought, "why not just swap the map with another map?"</p>
<p>So that's exactly what I did and it worked! </p>
<p>I extracted the parts of the map that were clickable (like the buildings) and made it so they open up different maps. I don't know how well my solution will scale but hey, it's working right now and that's what matters. </p>
<div class="embed-wrapper">
        <blockquote class="twitter-tweet">
          <a href="https://twitter.com/jsjoeio/status/1119834245013196801?s=20"></a>
        </blockquote>
        <script defer="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script></div>
<h3 id="heading-courses-page">Courses Page</h3>
<p>One of the other challenges I faced was figuring out where and how to show the courses (i.e. the dialog with the narrator).</p>
<p>Same thing–I struggled with this for a bit then decided, "Let's show it in an Overlay component!"</p>
<p>That ended up working out well. Again, I don't know if that will scale well but it works for now.</p>
<div class="embed-wrapper">
        <blockquote class="twitter-tweet">
          <a href="https://twitter.com/jsjoeio/status/1123063970468786176?s=20"></a>
        </blockquote>
        <script defer="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script></div>
<hr>
<h2 id="heading-may-2019">May 2019</h2>
<p>In May, I took a little bit of a break. I was getting married so I wanted to focus on prepping for that rather than my game. I still had ideas for things here and there but I didn't put in nearly as much time as March or April. </p>
<p>Even though it's difficult for me to take breaks and step away, I think it's healthy to go outside, change what you're doing, mediate, etc. As my mother always says, </p>
<blockquote>
<p>Everything in moderation. </p>
</blockquote>
<hr>
<h2 id="heading-june-2019">June 2019</h2>
<p>Looking at the Dashboard I created, there was still so much left to do. </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/07/Dashboard-v1.1.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>I felt overwhelmed.</p>
<p>"How am I going to finish all this?"</p>
<h3 id="heading-a-realization-at-the-phoenix-reactjs-meetup">A Realization at the Phoenix ReactJS Meetup</h3>
<p>I hadn't been to the <a target="_blank" href="https://www.meetup.com/Phoenix-ReactJS/">Phoenix ReactJS Meetup</a> in a while. My two coworkers and I decided to go hear the lightning talks.</p>
<p>Before the talks, we were crowded around a table, chatting about our side projects. I said I wanted to finish the MVP for mentored.dev by the end of the year.</p>
<p>"How much more do you have to finish?" </p>
<p>"A decent amount. Everything on the Dashboard page is hard-coded at the moment."</p>
<p>"Drop all that. Finish the core features and ship it."</p>
<p>Those were the wise words from my coworkers. That's when I realized they were right. I decided to cut scope and implement two last features–the streak tracker and the lesson progress.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/07/Screen-Shot-2019-07-10-at-8.06.01-PM.png" alt="Image" width="600" height="400" loading="lazy">
<em>Screenshot of the streak tracker</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/07/Screen-Shot-2019-07-10-at-8.06.13-PM.png" alt="Image" width="600" height="400" loading="lazy">
<em>Screenshot of lesson progress</em></p>
<p>The streak tracker logic was buggy when I first implemented it and didn't work at all. I wasn't sure if I should only increment the streak after 24-48 hours, or just do it by the day, or what. It <a target="_blank" href="https://github.com/jsjoeio/mentored.dev/issues/93">seemed a lot more complicated</a> than it should have been.</p>
<p>I still don't know if I'm happy with the implementation. But again, it's out the door and the basic functionality works. </p>
<p>The lesson progress (completed - 1/3) is also rudimentary at best. Again, my focus was to get it out the door. I'll style it in the future. </p>
<h3 id="heading-ship-it">Ship It</h3>
<p>June 29th. The big day.</p>
<div class="embed-wrapper">
        <blockquote class="twitter-tweet">
          <a href="https://twitter.com/jsjoeio/status/1144994580200210432?s=20"></a>
        </blockquote>
        <script defer="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script></div>
<p>As I say in the tweet thread, </p>
<blockquote>
<p>...It's nowhere near complete but I think this is a good stopping point to share the MVP.</p>
</blockquote>
<p>A while back, I read <em><a target="_blank" href="http://theleanstartup.com/">Lean Startup</a></em> by Eric Ries. One thing that always stuck with me was something he said along the lines of, "You should be embarrassed putting your product out there. That's when you know it's an MVP."</p>
<p>And that's how I felt! So much left to do. It's hard to even consider it a "game"–most real gamers probably wouldn't.</p>
<p>But that's the point–it helped lift a burden off my shoulders and step back to hear what people think.</p>
<p>Most people I've talked to think it's a good start and a neat concept. They're excited to see where it goes.</p>
<hr>
<h2 id="heading-what-i-think-worked">What I Think Worked</h2>
<p>Reflecting on what helped me launch this MVP, a few things come to mind. </p>
<h3 id="heading-accountability-friends-amp-twitter-community">Accountability - Friends &amp; Twitter Community</h3>
<p>As we all know, it's very easy to silo yourself and work alone. This might work for some people and that's fine. But in my case, I think sharing this project with my coworkers held me more accountable than if I hadn't told anyone. Each week on Monday mornings, one of them would ask, "Hey Joe. Did you work on your game?"</p>
<p>Their interest and support meant a lot to me. They wanted to see it succeed as much as I did. That kept me going.</p>
<p>The other part that kept me accountable was sharing it with people on Twitter. Sometimes people would comment and other times they wouldn't. Either way, people were following along. A few would DM me asking how it was coming along.</p>
<p>By sharing it in public, I felt a bit of pressure (in a good way) to finish it.</p>
<h3 id="heading-using-github-projects-issues-and-milestones">Using GitHub Projects, Issues, and Milestones</h3>
<p>I treated this project like we treat client/company applications at work. I didn't use sprints per se but I did keep a list of tasks in a <a target="_blank" href="https://github.com/jsjoeio/mentored.dev/projects/3">GitHub Project board</a> and then select a few and create a milestone. This made the work feel more achievable and less overwhelming. </p>
<p>I set up a staging environment at <a target="_blank" href="https://staging.mentored.dev">https://staging.mentored.dev</a> (thanks to <a target="_blank" href="https://www.netlify.com/">Netlify</a>, this was straightforward). Then, each issue I finished, I submitted a PR to merge into staging. I reviewed and merged myself (yes, a bit silly, but good practice). </p>
<p>Once a <a target="_blank" href="https://github.com/jsjoeio/mentored.dev/milestones?state=closed">milestone was complete</a>, I merged staging into master and created a new release. This process set me up for success. I kept milestones small (something I could finish in 1-3 weeks). </p>
<p>Having some type of project management in place for your side project I believe will help you reach the finish line sooner. </p>
<h3 id="heading-cutting-scope">Cutting Scope</h3>
<p>I wouldn't have finished this MVP if I hadn't cut a lot of features. For instance, I really wanted to create a repository called "mentored-dev" after the user logged in and store the lesson progress there. But that was going to take more time than I anticipated so I cut it. </p>
<p>Instead, I store the progress in localstorage. Yes, it's short-term but again, I had to cut scope to ship. If I hadn't, I wouldn't have finished this phase of the project.</p>
<hr>
<h2 id="heading-closing-thoughts">Closing Thoughts</h2>
<p>Overall, I feel thankful for all the support. I'm proud of the small project I built and the feedback I have received, so thank you. As for the next steps, I've already created the <a target="_blank" href="https://github.com/jsjoeio/mentored.dev/milestone/6">next milestone</a>. The main thing is to finish all the lessons for the basics of the command line and then share that to see what people think.</p>
<p>As far as actual features–I wouldn't promise anything but I'd love to add experience points (XP) which you accumulate based on your score in the lessons or how many times you take each lesson, how often you login, etc.</p>
<p>It would also be nice to give XP for doing things outside the game (i.e. writing a blog post, tweeting something you learned, contributing to open source, helping someone, etc). We'll see what happens though.</p>
<p>Thank you for listening to the journey. </p>
<p>###</p>
<p>If you enjoyed this article or found it interesting, please share it with others or let me know on <a target="_blank" href="https://twitter.com/jsjoeio">Twitter</a>.</p>
<p>To stay up to date on mentored.dev or other things I'm working on, I have a newsletter you can <a target="_blank" href="https://github.com/jsjoeio/mentored.dev/milestone/6">sign up for here</a>.</p>
<p>Happy coding! </p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to go from MVP to Production Server in a Day ]]>
                </title>
                <description>
                    <![CDATA[ By Yisroel Yakovson This article follows two others about creating Full Graph Stacks. Check out How To Build A Cutting Edge Server Now for a conceptual introduction to the approach. Launch Your MVP Server In An Hour guides you through building a stac... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/making-your-full-graph-stack-production-quality-ec231a938551/</link>
                <guid isPermaLink="false">66c35af771e87702d4e5b703</guid>
                
                    <category>
                        <![CDATA[ GraphQL ]]>
                    </category>
                
                    <category>
                        <![CDATA[ mvp ]]>
                    </category>
                
                    <category>
                        <![CDATA[ General Programming ]]>
                    </category>
                
                    <category>
                        <![CDATA[ servers ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tech  ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Mon, 20 Aug 2018 08:29:58 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/1*IBwh1zdiKEN7OdkOoUJC8w.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Yisroel Yakovson</p>
<p>This article follows two others about creating Full Graph Stacks. Check out <a target="_blank" href="https://medium.freecodecamp.org/meet-the-full-graph-stack-d32150308a87">How To Build A Cutting Edge Server Now</a> for a conceptual introduction to the approach. <a target="_blank" href="https://medium.freecodecamp.org/building-a-full-graph-stack-f95590ade5af">Launch Your MVP Server In An Hour</a> guides you through building a stack of development quality. This article explains how to convert that stack to a robust, permanent application.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/xMv8-fJ-bBLSQJDiVRVDBAkJPEB1UFdoMrLz" alt="Image" width="560" height="400" loading="lazy"></p>
<h3 id="heading-whats-wrong-with-the-development-stack">What’s Wrong with the Development Stack?</h3>
<p>The steps in the second article mirrored the <a target="_blank" href="https://github.com/grand-stack/grand-stack-starter">GRANDstack video:</a></p>
<ul>
<li>The database is a <a target="_blank" href="https://neo4j.com/sandbox-v2/">Neo4j Sandbox</a></li>
<li>The APIC server runs on <a target="_blank" href="https://zeit.co/now">Now</a>.</li>
</ul>
<p>That’s enough for an MVP or for the initial prototyping, but you’ll have to move on soon. The biggest initial limitation is the database. The sandbox will live at most ten days.</p>
<p>Even after you spin up a permanent database, Now would make a difficult permanent home. In theory, it would be possible if you didn’t modify your server frequently. You could set up your permanent API with a separate host and alias it to your Now endpoint. The problem is that the Now endpoint changes each time you upload a revision. That means that you have to keep updating your alias.</p>
<p>Security also poses a challenge. It’s common to create a white list of IPs permitted to access your database and to limit the list to your API server. So if that server IP changes frequently, you have to update the list with each revision. I don’t see a viable way to do that with Now as of this writing. (Please reach out to me if you do!)</p>
<p>So the question is, what’s the best approach to solve these limitations?</p>
<h3 id="heading-on-the-cutting-edge">On the Cutting Edge</h3>
<p>The truth is, this article has been on hold for a few weeks. The problem was that <a target="_blank" href="https://github.com/neo4j-graphql/neo4j-graphql-js">neo4j-graphql-js</a> was not quite ready to support production. But in the past few weeks, that all changed. The team solved a few fundamental problems:</p>
<ul>
<li><a target="_blank" href="https://expressjs.com/en/guide/using-middleware.html">Middleware</a> became supportable two weeks ago. The team added to the generated reducers support for throwing middleware errors. That enables your server to run general authentication and authorization functionality as middleware.</li>
<li>Modification of <a target="_blank" href="https://graphql.org/learn/queries/#mutations">mutations</a> became possible a week ago. Until then, you were stuck with the generated mutations. Now you can add necessary business logic or side effects.</li>
<li>Autogenerated keys are becoming available. The team reported yesterday a new @autogenerate directive. Placing the directive after a key will result in an autogenerated UUID. Before, the front end had to pass values in for keys, which is most unusual for a production server.</li>
</ul>
<p>This writing, on August 19, 2018, comes in the midst of many other expected fixes. Of particular interest, the team plans to release some directives to simplify auth. Also, I hope that we will soon see <a target="_blank" href="https://github.com/neo4j-graphql/neo4j-graphql-js/issues/89">nested mutations</a>. Much of what I write here will soon become outdated. But I’ve decided that it’s worth describing this package now, because it’s already useful. Please post updates or corrections below as comments.</p>
<h3 id="heading-options">Options</h3>
<p>When you leave the world of sandboxes, you have choices. I think that the general rule is that today most are fine. The important thing is to move forward.</p>
<p>But the three basic steps that I discuss below are probably universal needs. They may be all that you need for your back end.</p>
<p>Two introductory observations:</p>
<ul>
<li>You don’t need to get things perfect from day 1. There was a time when decisions like the size of your machine or your host had long-term significance. Today any sensible team works in the cloud. Most decisions are reversible. Auth may an exception, but even that may be changing. Get something live and start pivoting!</li>
<li>If you followed the steps in <a target="_blank" href="https://medium.freecodecamp.org/building-a-full-graph-stack-f95590ade5af">Launch Your MVP Server In An Hour</a>, you have an amazingly small back end. You will need three components: your tiny server app, your database, and an auth service. You don’t even have to store them with the same host.</li>
</ul>
<h3 id="heading-contents">Contents</h3>
<ol>
<li><a class="post-section-overview" href="#0241">Set up Auth</a></li>
<li><a class="post-section-overview" href="#6511">Spin your Database</a></li>
<li><a class="post-section-overview" href="#6f99">Create Your Server</a></li>
</ol>
<h3 id="heading-set-up-auth">Set up Auth</h3>
<p>Every project’s auth needs are different, but some basics have emerged. You need two things:</p>
<ol>
<li>En external authentication service. Two common choices are Cognito and Auth0.</li>
<li>Most servers also need authorization, or access control. You have to decide whether a particular user is allowed to do something.</li>
</ol>
<h4 id="heading-setting-up-middleware-capability">Setting up Middleware Capability</h4>
<p>The preferred way to handle Auth is <a target="_blank" href="https://graphql.org/graphql-js/authentication-and-express-middleware/">through middleware</a> or directives. As of the current writing, auth is not included in the GRANDstack starter.</p>
<p>The server used in <code>api/src/index.jx</code> in the starter package is currently <code>ApolloServer</code>. But you can replace that with <code>graphalExpress</code> from <code>apollo-server-express</code>.</p>
<p>You will have to change 2 files:</p>
<ul>
<li><code>api/src/index.js</code></li>
<li><code>api/package.json</code></li>
</ul>
<p>You should also add an <code>auth.js</code> file.</p>
<p>Here’s a version of <code>index.js</code> that currently works with middleware:</p>
<pre><code><span class="hljs-keyword">import</span> express <span class="hljs-keyword">from</span> <span class="hljs-string">'express'</span>;<span class="hljs-keyword">import</span> { graphqlExpress, graphiqlExpress } <span class="hljs-keyword">from</span> <span class="hljs-string">'apollo-server-express'</span>;<span class="hljs-keyword">import</span> cors <span class="hljs-keyword">from</span> <span class="hljs-string">'cors'</span>;<span class="hljs-keyword">import</span> { makeExecutableSchema } <span class="hljs-keyword">from</span> <span class="hljs-string">'graphql-tools'</span><span class="hljs-keyword">import</span> expressPlayground <span class="hljs-keyword">from</span> <span class="hljs-string">'graphql-playground-middleware-express'</span>;<span class="hljs-keyword">import</span> bodyParser <span class="hljs-keyword">from</span> <span class="hljs-string">'body-parser'</span>;<span class="hljs-built_in">require</span>(<span class="hljs-string">'dotenv'</span>).config();<span class="hljs-keyword">import</span> { v1 <span class="hljs-keyword">as</span> neo4j } <span class="hljs-keyword">from</span> <span class="hljs-string">"neo4j-driver"</span>;<span class="hljs-keyword">import</span> { augmentSchema } <span class="hljs-keyword">from</span> <span class="hljs-string">"neo4j-graphql-js"</span>;<span class="hljs-keyword">import</span> { typeDefs, resolvers } <span class="hljs-keyword">from</span> <span class="hljs-string">"./graphql-schema"</span>;<span class="hljs-keyword">import</span> { authenticateUser, authorize } <span class="hljs-keyword">from</span> <span class="hljs-string">'./auth'</span>;<span class="hljs-comment">// augmentSchema will add autogenerated mutations based on types in schemaconst schema = makeExecutableSchema({  typeDefs,  resolvers});const augmentedSchema = augmentSchema(schema);const driver = neo4j.driver(  process.env.NEO4J_URI,  neo4j.auth.basic(    process.env.NEO4J_USER,    process.env.NEO4J_PASSWORD  ));const app = express();app.use(bodyParser.json()); // support json encoded bodiesapp.use(cors());app.use('/graphql i apollo-server-expressql', graphiqlExpress({  endpointURL: '/graphql'}));app.get('/', expressPlayground({ endpoint: '/graphql' }));// app.use('/', authenticateUser, authorize);app.use('/graphql', bodyParser.json(), graphqlExpress(req =&gt; {  return {    context:  {      auth: req.auth,      driver    },    endpointURL: '/graphql',    schema: augmentedSchema  }}));app.listen(process.env.GRAPHQL_LISTEN_PORT, '0.0.0.0');console.log(`GraphQL Playground at ${process.env.GRAPHQL_LISTEN_PORT}`);</span>
</code></pre><p>Note that:</p>
<ol>
<li>We are using apollo-server-express, which supports middleware</li>
<li>Two functions are called as middleware: authenticateUser and authorize. Both definitions appear in auth.js.</li>
<li>I also added cors, which we needed to resolve some CORS issues.</li>
</ol>
<p>The project.json file must contain the proper dependencies. As of this writing, here are the versions I’m using:</p>
<pre><code>{  <span class="hljs-string">"name"</span>: <span class="hljs-string">"grand-stack-express"</span>,  <span class="hljs-string">"version"</span>: <span class="hljs-string">"0.0.1"</span>,  <span class="hljs-string">"description"</span>: <span class="hljs-string">"API app for GRANDstack with express"</span>,  <span class="hljs-string">"main"</span>: <span class="hljs-string">"src/index.js"</span>,  <span class="hljs-string">"license"</span>: <span class="hljs-string">"MIT"</span>,  <span class="hljs-string">"dependencies"</span>: {    <span class="hljs-string">"apollo-server-express"</span>: <span class="hljs-string">"^1.3.6"</span>,    <span class="hljs-string">"babel-cli"</span>: <span class="hljs-string">"^6.26.0"</span>,    <span class="hljs-string">"babel-core"</span>: <span class="hljs-string">"^6.26.3"</span>,    <span class="hljs-string">"babel-polyfill"</span>: <span class="hljs-string">"^6.26.0"</span>,    <span class="hljs-string">"babel-preset-env"</span>: <span class="hljs-string">"^1.7.0"</span>,    <span class="hljs-string">"babel-preset-stage-0"</span>: <span class="hljs-string">"^6.24.1"</span>,    <span class="hljs-string">"babel-watch"</span>: <span class="hljs-string">"^2.0.7"</span>,    <span class="hljs-string">"body-parser"</span>: <span class="hljs-string">"^1.18.3"</span>,    <span class="hljs-string">"cors"</span>: <span class="hljs-string">"^2.8.4"</span>,    <span class="hljs-string">"dotenv"</span>: <span class="hljs-string">"^6.0.0"</span>,    <span class="hljs-string">"express"</span>: <span class="hljs-string">"^4.16.3"</span>,    <span class="hljs-string">"express-graphql"</span>: <span class="hljs-string">"^0.6.12"</span>,    <span class="hljs-string">"graphql-playground-middleware-express"</span>: <span class="hljs-string">"^1.7.1"</span>,    <span class="hljs-string">"graphql-tag"</span>: <span class="hljs-string">"^2.9.2"</span>,    <span class="hljs-string">"graphql-tools"</span>: <span class="hljs-string">"^3.0.4"</span>,    <span class="hljs-string">"neo4j-driver"</span>: <span class="hljs-string">"^1.6.3"</span>,    <span class="hljs-string">"neo4j-graphql-js"</span>: <span class="hljs-string">"^0.1.32"</span>,    <span class="hljs-string">"node-fetch"</span>: <span class="hljs-string">"^2.1.2"</span>,    <span class="hljs-string">"nodemon"</span>: <span class="hljs-string">"^1.17.5"</span>  },  <span class="hljs-string">"resolutions"</span>: {    <span class="hljs-string">"neo4j-graphql-js/graphql"</span>: <span class="hljs-string">"v14.0.0-rc.2"</span>  },  <span class="hljs-string">"scripts"</span>: {    <span class="hljs-string">"test"</span>: <span class="hljs-string">"echo \"Error: no test specified\" &amp;&amp; exit 1"</span>,    <span class="hljs-string">"dev"</span>: <span class="hljs-string">"babel-watch --exec babel-node --presets env,stage-0 src/index.js"</span>,    <span class="hljs-string">"start"</span>: <span class="hljs-string">"nodemon --exec babel-node --presets env,stage-0 src/index.js"</span>  },  <span class="hljs-string">"devDependencies"</span>: {    <span class="hljs-string">"nodemon"</span>: <span class="hljs-string">"^1.17.5"</span>  }}
</code></pre><p>Here’s a starter auth.js file to add to your project and complete:</p>
<pre><code><span class="hljs-keyword">import</span> gql <span class="hljs-keyword">from</span> <span class="hljs-string">'graphql-tag'</span><span class="hljs-keyword">import</span> { v1 <span class="hljs-keyword">as</span> neo4j } <span class="hljs-keyword">from</span> <span class="hljs-string">'neo4j-driver'</span>;<span class="hljs-keyword">import</span> { INSPECT_MAX_BYTES } <span class="hljs-keyword">from</span> <span class="hljs-string">'buffer'</span>;<span class="hljs-built_in">require</span>(<span class="hljs-string">'dotenv'</span>).config();<span class="hljs-keyword">const</span> driver = neo4j.driver(  process.env.NEO4J_URI,  neo4j.auth.basic(    process.env.NEO4J_USER,    process.env.NEO4J_PASSWORD  ));<span class="hljs-keyword">const</span> resolveUser = <span class="hljs-function">() =&gt;</span> {  <span class="hljs-comment">//a placeholder  return 0;}/* * middleware functions follow */export const authenticateUser = async (req, res, next) =&gt; {  req.auth={};  try {    const response = await resolveUser();    req.auth.user=response  } catch(err) {    req.error=err.message  }  next();}export const authorize = async (req, res, next) =&amp;gt; {  if (req.error) {    console.log(req.error);    next();    return;  }  // placeholder, allows every authenticated request  next();}</span>
</code></pre><ol>
<li>start by committing your old version to git and probably creating a new branch. <code>git add .</code> then <code>git commit -m "works without auth"</code> and then <code>git checkout -b auth</code>. You will then see a response of<br><code>Switched to a new branch ‘auth’</code></li>
<li>replace your <code>api/src/index.js</code> and <code>api/project.json</code> files with the versions above, and add the <code>auth.js</code> file to <code>api/src</code>.</li>
<li>move your current node_modules: <code>mv node_modules node_modules.old</code></li>
<li>run <code>npm install</code> again, then <code>npm start</code>. Make sure that it works by running a query.</li>
</ol>
<p><img src="https://cdn-media-1.freecodecamp.org/images/ya3u8WaXGfR4VcOTOJXpA8SNJUrAOAoiUlEG" alt="Image" width="800" height="260" loading="lazy"></p>
<h4 id="heading-add-your-authentication-service">Add Your Authentication Service</h4>
<p>You can fill in the details based on the SDK for whatever auth software you use.</p>
<p>You may have to study up on <a target="_blank" href="https://dev.to/geoff/writing-asyncawait-middleware-in-express-6i0">using async/await functions in an express app</a>, but it’s not too difficult to learn.</p>
<ol>
<li>Uncomment the line <code>app.use(‘/’, authenticateUser, authorize);</code> in <code>index.js</code>.</li>
<li>Install the proper Node js SDK for your Authentication service. Then add the needed code and rewrite the function <code>authenticateUser</code> in <code>auth.js</code> to call it. Remember to set <code>req.error</code> to an appropriate error message when authentication fails.</li>
<li>Practice by adding a valid token to the HTTP Header. Note that in Playground the HTTP HEADERS panel makes this quite simple.</li>
</ol>
<h4 id="heading-add-authorization">Add Authorization</h4>
<p>The file <code>auth.js</code> includes a database driver. The driver supports querying to determine a user’s access rights to specific data. For instance, your database could store read/write privileges for data. Whenever a request fails your test, you can specify what failed by setting <code>req.error</code>. The <code>neo4jgraphql</code> resolver function will return the error message.</p>
<p>Note that you can add more functions to the middleware. For instance, you might add a check whether the current user is fully paid.</p>
<h4 id="heading-enhancing-augmentschema">Enhancing AugmentSchema</h4>
<p>The call to <code>augmentSchema</code> in the <code>index.js</code> file generates the mutations in your schema. As stated above, a recent enhancement lets you add mutations. You can also overwrite the generated ones.</p>
<p>One simple way is with the @cypher directive in your mutation declaration:</p>
<pre><code>type Mutation { UpdateFoo(id: ID, <span class="hljs-attr">name</span>: <span class="hljs-built_in">String</span>): Foo @cypher(statement:”MATCH (f:Foo {<span class="hljs-attr">id</span>: $id}) SET f.name = $name”}
</code></pre><p>Then, for the resolver just use <code>neo4jgraphql</code>:</p>
<pre><code>Mutation: { <span class="hljs-attr">UpdateFoo</span>: neo4jgraphql}
</code></pre><h3 id="heading-database-hosting">Database Hosting</h3>
<p>You have some options. First, there are some <a target="_blank" href="https://neo4j.com/developer/neo4j-cloud-hosting-providers/">hosting services</a>. I wasn’t so impressed, because it’s not that hard to create a database server yourself. But, the cost is a drop in the bucket compared to your team’s time. If you pay a few hundred dollars a month to get something up and running, and you have no worries, you could do a lot worse.</p>
<h4 id="heading-basic-hosting">Basic Hosting</h4>
<p>I decided to spin up an AWS EC2 instance instead. I’m going to write what I did there.</p>
<ol>
<li>Decide your region. Might not matter much, but if you are centered somewhere then go for it.</li>
<li>Get the AWS CLI working working on your computer.</li>
<li>Get hold of an appropriate AMI. You can search on the AWS store, or check out what’s available.</li>
<li>Do the steps (until the last one) <a target="_blank" href="https://neo4j.com/developer/neo4j-cloud-aws-ec2-ami/">here</a>. Make sure to save your key file, as you’ll need it for setting up APOC.</li>
<li>Then you need to go to the page, log in and change the password. But which port to go to and what password might not be the same as there. For my AMI, the password was “neo4j” (like the user name). I had to go to <a target="_blank" href="https://[IP]:7473/browser/">https://[IP]:7473/browser/</a> (not http as in the example given on the page, and not 7434).</li>
<li>Change your code on your local to be sure that you can connect to it. Check it on your localhost:4000.</li>
</ol>
<h4 id="heading-setting-up-apoc">Setting up APOC</h4>
<p>To use <code>@cypher</code> directives, or any of the <a target="_blank" href="https://neo4j-contrib.github.io/neo4j-apoc-procedures/">APOC</a> functions, you’ll have to to install the APOC jar file on the server itself. You’ll need to ssh in to your database server. As of 2 months ago, it wasn’t included in the AMIs.</p>
<ol>
<li>You’ll need to use the command shown at <a target="_blank" href="https://neo4j.com/developer/neo4j-cloud-aws-ec2-ami/">Hosting Neo4j on EC2 on AWS</a>: <code>ssh -i $KEY_NAME.pem ubuntu@[PublicDnsName]</code></li>
<li>You’ll have to add the jar file to the neo4j plugins directory. <a target="_blank" href="https://github.com/neo4j-contrib/neo4j-apoc-procedures#manual-installation-download-latest-release">Follow the manual instructions</a> to find the latest release and figure out where to insert it.</li>
<li>You also need to find your neo4j.conf file and insert into it permission to call the functions. The following works: <code>dbms.security.procedures.unrestricted=apoc.*</code></li>
<li>Then you’ll have to restart: <code>sudo systemctl restart neo4j.</code></li>
</ol>
<h3 id="heading-api-deployment">API Deployment</h3>
<p>You can deploy on any system that supports a NODE js app. I used <a target="_blank" href="https://aws.amazon.com/elasticbeanstalk/">AWS Elastic Beanstalk</a>:</p>
<ol>
<li>Run the command <code>zip -r api.zip . -x node_modules/**\*</code>in the <code>api</code> directory. That creates a zip without the directory itself and without the node_modules.</li>
<li>Go to <a target="_blank" href="https://console.aws.amazon.com/elasticbeanstalk/home?region=us-east-1#/welcome">AWS Elastic Beanstalk</a> in the console and make sure you’re in your region of choice.</li>
<li>If you can get to the welcome page, I find it the easiest. You click on <strong>Get Started</strong>, and follow the instructions. They set me up right away with what I needed. You just upload the file that you zipped above.</li>
<li>nder <strong>Configuration for the Elastic Beanstalk</strong> environment, go to <strong>Modify Software</strong>. Enter <strong>Container Options</strong>, and set <strong>Node Command</strong> to <code>npm start</code>.</li>
<li>After about 10 minutes, it’s ready. You can click on the <strong>endpoint URL</strong> to see it.</li>
<li>Use Route53 to alias your own api domain to the Elastic Beanstalk endpoint, and you’re all set.</li>
</ol>
<h3 id="heading-conclusions">Conclusions</h3>
<p>If you’ve followed this far, congratulations. You know more about how to build a <a target="_blank" href="https://medium.com/p/d32150308a87/edit">full graph stack</a> than many seasoned back end designers.</p>
<p>The process described in these articles might take an hour for someone who has done them a few times. For anyone else, these articles hopefully minimize the extra time to research. The development time should fall even more as these tools improve.</p>
<p>But simplicity and ease are just one benefit. The full graph stack is also technologically sound.</p>
<ul>
<li>The server created here is robust.</li>
<li>A graph database in general scales brilliantly.</li>
<li>The stack uses very few resources.</li>
<li>You can port the back end to different hosts or services, reducing lock-in and fees.</li>
</ul>
<p>Let us work together to keep this technology moving. Please leave comments or reach out with other improvements. Good luck with your own projects!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Launch Your MVP Server in an Hour ]]>
                </title>
                <description>
                    <![CDATA[ By Yisroel Yakovson Building A Full Stack Server This article guides you through creating a live, development quality API and back end. It should take you about an hour. And, by the way, it will be free! This is the second of a series of 3 articles a... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/building-a-full-graph-stack-f95590ade5af/</link>
                <guid isPermaLink="false">66c34694ec912a556094db98</guid>
                
                    <category>
                        <![CDATA[ database ]]>
                    </category>
                
                    <category>
                        <![CDATA[ GraphQL ]]>
                    </category>
                
                    <category>
                        <![CDATA[ mvp ]]>
                    </category>
                
                    <category>
                        <![CDATA[ General Programming ]]>
                    </category>
                
                    <category>
                        <![CDATA[ technology ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Mon, 20 Aug 2018 08:29:44 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/1*IBwh1zdiKEN7OdkOoUJC8w.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Yisroel Yakovson</p>
<h3 id="heading-building-a-full-stack-server">Building A Full Stack Server</h3>
<p>This article guides you through creating a live, development quality API and back end. It should take you about an hour. And, by the way, it will be free!</p>
<p>This is the second of a series of 3 articles about full graph stacks. Check out <a target="_blank" href="https://medium.freecodecamp.org/meet-the-full-graph-stack-d32150308a87">How To Build A Cutting Edge Server Now</a> for an introduction.</p>
<p>I’m assuming that you know how to code, but not that you’re advanced. This is a full stack project. It touches on a lot of new tools, so you need not feel intimidated if you don’t understand everything. You’ll have some learning curves ahead, but not too steep.</p>
<p>The idea, as explained in the <a target="_blank" href="https://medium.freecodecamp.org/meet-the-full-graph-stack-d32150308a87">first article</a>, is to focus on specifying your app’s graph of data types. Then to use new tools that generate an app from it.</p>
<p>As I wrote in the first article, I went with the <a target="_blank" href="http://grandstack.io/">GRANDstack</a> project. I don’t claim that it’s the only viable approach, but I recommend that you at least check it out. My team decided on our own that we wanted to go with React, GraphQL, Apollo, and a graph database. The GRANDstack consists of these exactly, so the discovery excited me.</p>
<p>But the real excitement begins with the package <a target="_blank" href="https://github.com/neo4j-graphql/neo4j-graphql-js">neo4j-graphql-js</a>. It generates (Prisma style) a set of mutations and resolvers from TypeDefs. If you don’t know what that means, read on.</p>
<p>On the <a target="_blank" href="http://grandstack.io/docs/getting-started-grand-stack-starter.html">GRANDstack Kickstarter</a> page, a video shows how to create a full stack from start to finish. That was where I started.</p>
<p>One problem with writing these articles is that featured packages are under development. Many things that you still need to do today may be automatic within weeks. I want to summarize the steps and explain how to do some of the things you need that are still missing. But the staff at Neo4j is working on them. If you see that this info is outdated at any point, please post an update below.</p>
<h3 id="heading-alternative-approaches">Alternative Approaches</h3>
<p>Before we start, let me repeat that I’m not calling GRANDstack the only or even the best approach to a Full Graph Stack.</p>
<p>Many alternatives use Prisma to convert a GraphQL schema to an underlying database. Make sure to check out <a target="_blank" href="https://www.graph.cool/">GraphCool</a>. As of this writing, these systems are more developed and leave less work for the developer. A good example is auth.</p>
<p>The Prisma approach also has less lock-in to a particular database. In practice, changing the database wouldn’t be that hard with GRANDstack. But the cipher queries and the directives are unique to Neo4j.</p>
<p>That said, the GRANDstack Project is explicitly focused on the idea of a Full Graph Stack. I recommend them because they are responsive and devoted to the vision. It is also very straightforward.</p>
<h3 id="heading-setup">Setup</h3>
<h4 id="heading-create-a-project">Create a Project</h4>
<ol>
<li>Download the starter. Click <code>DOWNLOAD STARTER</code> from <a target="_blank" href="http://grandstack.io/">GRANDstack</a>, or download it from their <a target="_blank" href="https://github.com/grand-stack/grand-stack-starter">GitHub</a> page. I used the GitHub page to be sure I had the latest version (it should be anyway).</li>
<li>Unzip to a folder and name it for your app project</li>
<li>It is wise to move the new app folder to a general <strong>project</strong> folder.</li>
</ol>
<h4 id="heading-set-up-git">Set up Git</h4>
<p>I recommend that you not do a thing until you start using Git to handle version control.</p>
<ol>
<li><a target="_blank" href="https://www.linode.com/docs/development/version-control/how-to-install-git-on-linux-mac-and-windows/">Install Git</a> if you don’t have it.</li>
<li>In a text editor, modify <code>.gitignore</code> in the root project directory to include under <code>#dependencies</code> a line for <code>api/node_modules</code> and a line for <code>ui/node_modules</code>. As of this writing, <code>.gitignore</code> contained only <code>node_modules</code>.</li>
<li>Move to the root directory of the project in a terminal, and create a new git project:</li>
</ol>
<pre><code>git init git add .   # adds the relevant filesgit status  # optional to view the files <span class="hljs-keyword">if</span> you are interestedgit commit -m “Initial App Files” #or whatever name you<span class="hljs-string">'d like</span>
</code></pre><p>You can perform <code>git add .</code> and <code>git commit -m "some description"</code> as often as you like. See the endless documentation about Git online. You can learn about branches, commits, reverting, and everything about version control.</p>
<h3 id="heading-make-the-sample-back-end">Make the Sample Back End</h3>
<p>Note that your project code has two directories: <code>api</code> and <code>ui</code>. These articles only focus on the <code>api</code> directory, for creating an API server and back end. For more about the <code>ui</code>, check out the <a target="_blank" href="http://grandstack.io/docs/getting-started-grand-stack-starter.html">GRANDstack Kickstarter</a> video. You can also view the tutorials at GRANDstack.</p>
<h4 id="heading-build-the-back-end">Build the Back End</h4>
<p>You need to build the code. You can use npm for that. In the terminal, move to <code>api</code>, and perform these steps:</p>
<pre><code>npm install # pulls <span class="hljs-keyword">in</span> all <span class="hljs-keyword">of</span> the node modules needed <span class="hljs-keyword">for</span> the apinpm start # begins the app
</code></pre><p>That should initiate the app. You should see something like this in your terminal:</p>
<pre><code>$ npm start
</code></pre><pre><code>&gt; grand-stack-starter-api@<span class="hljs-number">0.0</span><span class="hljs-number">.1</span> start /home/israel/projects/events2/api&gt; nodemon --exec babel-node src/index.js
</code></pre><pre><code>[nodemon] <span class="hljs-number">1.18</span><span class="hljs-number">.3</span>[nodemon] to restart at any time, enter <span class="hljs-string">`rs`</span>[nodemon] watching: *.*[nodemon] starting <span class="hljs-string">`babel-node src/index.js`</span>GraphQL API ready at http:<span class="hljs-comment">//0.0.0.0:4000/</span>
</code></pre><p>Open that link, and you should see a GraphQL Playground page.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/p287pjNAiabrD4eyZBNr-GdewMT88YQSWyTq" alt="Image" width="800" height="419" loading="lazy"></p>
<p>You can click the green SCHEMA tab on the right, and you’ll see a schema for a sample database. The only thing you’re missing is the database itself.</p>
<h3 id="heading-set-up-a-sample-database">Set up a sample database</h3>
<p>You’ll have to register with <a target="_blank" href="https://neo4j.com/lp/try-neo4j-sandbox/">Neo4j</a> to use their sandbox in the steps given below. I recommend getting onto their <a target="_blank" href="https://neo4j-users.slack.com/">Slack channel</a> from the beginning. In particular, if you follow the steps below I’d join the <strong>#grand-stack</strong> channel so that you can ask questions. The project is evolving quickly, so it’s important to stay connected. They are pretty receptive to ideas and responsive to problems. In the past few weeks, they’ve implemented several ideas that I and others have proposed.</p>
<h4 id="heading-spin-up-a-blank-database">Spin up a Blank Database</h4>
<ol>
<li>Login at <a target="_blank" href="https://neo4j.com/sandbox-v2/">Neo4j Sandbox</a>. (As stated at the beginning, you need to create an account if you don’t have one.)</li>
<li>Find “Blank Database” and click on it. It should generate and show up after a minute under <strong>Your Current Sandboxes</strong>.</li>
<li>Click the <strong>Details</strong> tab, and you’ll see something like this:</li>
</ol>
<p><img src="https://cdn-media-1.freecodecamp.org/images/g2WzC42HBX1fuPW6kr-3i9Z9HfE83F3yIkpf" alt="Image" width="800" height="430" loading="lazy"></p>
<p>You’ve now generated a live database. You can visit it in the browser at the top link.</p>
<h4 id="heading-connect-your-project-to-the-database">Connect Your Project to the Database</h4>
<p>Open the <code>api/.env</code> file, which contains some global variables used in the API:</p>
<pre><code>NEO4J_URI=bolt:<span class="hljs-comment">//localhost:7687NEO4J_USER=neo4jNEO4J_PASSWORD=letmeinGRAPHQL_LISTEN_PORT=4000GRAPHQL_URI=http://localhost:4000</span>
</code></pre><p>You need to set some of these from the Details tab of your sandbox</p>
<ol>
<li>Copy the IP Address (e.g. in the example shown above it is <code>174.129.54.148</code>), and then the bolt port e.g. <code>33199</code>. Use them together to create a new value for <code>NEO4J_URI</code> in the <code>api/.env</code> file, replacing <code>bolt://localhost:7687</code>. Note that a colon separates them. E.g. the first line in this case would be <code>NEO4J_URI=bolt://174.129.54.148:33199</code>.</li>
<li>Also, copy from Details the sandbox db password (e.g. in this case <code>adhesives-casualties-loads</code>) and replace the default password <code>letmein</code>.</li>
<li>Of course, make sure you’ve saved your changes. Then return to your terminal, hit <code>ctrl-c</code>, once again enter <code>npm start</code>, and return to <a target="_blank" href="http://0.0.0.0:4000/">http://0.0.0.0:4000/</a> and reload.</li>
<li>Now enter the following mutation in the left pane:</li>
</ol>
<pre><code># Try to write your query heremutation {  CreateUser (id: <span class="hljs-string">"borris"</span>, <span class="hljs-attr">name</span>: <span class="hljs-string">"borris the spider"</span>) {    id  }}
</code></pre><p>Hit the arrow button in the middle to execute, and you should see the results of the mutation in the right button:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/19KFekShbwlleukGojgH-4FQ59ZHswV3jrtg" alt="Image" width="800" height="251" loading="lazy"></p>
<p>If so, congratulations! You already have a functional API server running locally.</p>
<p>Don’t forget to commit your changes to Git. Back in the root directory of your app (not in API), enter:</p>
<pre><code>git add .git commit -m <span class="hljs-string">"Working Sandbox Db"</span>
</code></pre><h3 id="heading-inspect-the-data-directly">Inspect the Data Directly</h3>
<p>Now, click the link to <code>Neo4j Browser</code> at the top of the Details in your Neo4j Sandbox. (In the case shown above, it is <code>[https://10-0-1-68-33200.neo4jsandbox.com/](https://10-0-1-68-33200.neo4jsandbox.com/.))</code><a target="_blank" href="https://10-0-1-68-33200.neo4jsandbox.com/.">.)</a>)</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/2IMvSnlT0Bwd5Q7dOwHYMk06rmkTJTBLcGst" alt="Image" width="800" height="420" loading="lazy"></p>
<p>You can read about how to use it at Neo4j. But for our purposes, enter the following query at the prompt: <code>MATCH (n) RETURN n</code>. That query returns all of the nodes in the database. Click the arrow button on the right to execute, and you should see the new node that you created:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/iY4PdL88TEOKKOkNlJ3LDZXwkxk5io-ibdal" alt="Image" width="800" height="258" loading="lazy"></p>
<p>You can get back to a terminal in your <code>api</code> directory and enter <code>npm seedDb</code> if you like. After a minute or so you’ll see the seed data that comes with the starter package for their sample database. You can then play with Playground making queries like this:</p>
<pre><code>{  users(name: <span class="hljs-string">"Will"</span>) {    id    name  }}
</code></pre><p>Or, you can just move on to the next step.</p>
<h3 id="heading-add-your-own-graphql-schema">Add your Own GraphQL Schema</h3>
<p>Click on the green SCHEMA button in the Playground interface. You will see a schema that came with the sample database:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/oYUqCcTIe7TNBPn7lXpnUMPm8BhIqR6XfnLw" alt="Image" width="800" height="326" loading="lazy"></p>
<p>Now it’s time to replace that with your own schema.</p>
<ol>
<li>Open the current sample schema <code>api/src/graphql-schema.js</code>, and see the source for that data. It’s in the <code>typeDefs</code> declaration.</li>
<li>Learn minimally what you must about <a target="_blank" href="https://graphql.org/learn/schema/">GraphQL Type Schemas.</a> You must be able to implement your own needed types and some queries. It’s straightforward.</li>
<li>Start with one or two types and experiment, building it up gradually. To create a query, you’re going to have to change both <code>typeDefs</code> and <code>resolvers</code> (beneath <code>typeDefs</code> in the same file). Fortunately, your resolvers will be simple. Just call <code>neo4jgraphql</code> from the <a target="_blank" href="https://github.com/neo4j-graphql/neo4j-graphql-js">neo4j-graphql-ps package</a>, as in the sample database.</li>
<li>Save changes to the file, and confirm that npm is updating. Then refresh the Playground tab and confirm that the new Schema is showing up.</li>
</ol>
<p>The <a target="_blank" href="https://medium.freecodecamp.org/meet-the-full-graph-stack-d32150308a87">introductory article</a> about full graph stacks mentioned a sample app for events. Here was its sample app data type graph:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/-DvKBsPHFILwdawQmLAXeBgrGNfdYHvk1K-O" alt="Image" width="800" height="258" loading="lazy"></p>
<p>That’s a lot to add all at once, but it’s not hard to start. We work from the top down, and GraphQL is extremely forgiving.</p>
<h4 id="heading-understanding-neo4j-graphql-js">Understanding neo4j-graphql-js</h4>
<p>It would help to understand a bit about how the server uses <code>neo4j-graphql-js</code>.</p>
<p>The call to <code>augmentSchema</code> in <code>index.js</code> is what is creating all the mutations. That includes the CRUD functions (Create, Update and Delete) for each of the types created. Also, the Add and Remove functions for creating relationships.</p>
<p>Relationships between types generate relationship functions. When a type X returns Type Y as a field, that indicates a relationship. Add and Remove functions are generated when the precise relationship is defined using:</p>
<ol>
<li>a <code>@cypher</code> directive or</li>
<li>a <code>@relation</code> directive with a direction of “OUT”.</li>
</ol>
<p>The other important function is <code>neo4jgraphql</code>, which implements a resolver. The <a target="_blank" href="https://grandstack.io/docs/neo4j-graphql-js.html">documentation of the package</a> explains the details.</p>
<p>The documentation was updated just last week on the <a target="_blank" href="https://grandstack.io/docs/neo4j-graphql-js.html">GRANDstack documentation page</a>. (This writing is August 19, 2018). The documentation is still a bit minimal. For instance, the critical <code>augmentSchema</code> function is not discussed in the documentation. They do discuss mutations and they show an example using it. But the documentation is helpful. So are the examples contained in the starter package and in the examples brought there.</p>
<p>You might also check the documentation for <code>[neo4j-graphql](https://github.com/neo4j-graphql/neo4j-graphql)</code><a target="_blank" href="https://github.com/neo4j-graphql/neo4j-graphql">.</a> but some things there may not have made it into the <code>ps</code> version yet.</p>
<h4 id="heading-initial-changes">Initial Changes</h4>
<p>You can remove all the types in the example, but note that you should not entirely remove the Query type. You will need it for your queries, as you can see in GraphQL documentation.</p>
<p>Here’s an example of the simplest possible beginning for the events app schema:</p>
<pre><code><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> typeDefs = <span class="hljs-string">`type Event {  id: ID!  name: String}type Query {    events(id: ID, name: String): [Event]}`</span>;<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> resolvers = {  <span class="hljs-attr">Query</span>: {    <span class="hljs-attr">events</span>: neo4jgraphql,  }};
</code></pre><p>After saving, check for a reassuring message in the terminal where <code>npm start</code> is running. Something like this:</p>
<pre><code>[nodemon] restarting due to changes…[nodemon] starting <span class="hljs-string">`babel-node src/index.js`</span>GraphQL API ready at http:<span class="hljs-comment">//0.0.0.0:4000/</span>
</code></pre><p>Refresh Playground in the browser and clicking the SCHEMA buttons. You should see the new schema:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/I5Htv0-2MxJQ2B5i8HJfJKUQTmq061wCM01J" alt="Image" width="800" height="274" loading="lazy"></p>
<p>You can then start adding more types.</p>
<h4 id="heading-add-your-own-data">Add your own data</h4>
<ol>
<li>At your Sandbox prompt, enter <code>MATCH (n) DETACH DELETE n</code>. That will delete any data you added for the sample schema that came with the project.</li>
<li>Use the generated mutations in your new schema to create some data. For instance:</li>
</ol>
<pre><code>mutation {  CreateEvent(id: <span class="hljs-string">"my event"</span>, <span class="hljs-attr">name</span>: <span class="hljs-string">"The Big Event"</span>) {    id    name  }}
</code></pre><h3 id="heading-go-live">Go Live</h3>
<p>The only thing left is to move your server to a live site so that your front end can call it. (Oh, and you will have to create the front end — go to the <code>ui</code> directory of your app folder to deal with that). The video in the <a target="_blank" href="http://grandstack.io/docs/getting-started-grand-stack-starter.html">GRANDstack Kickstart page</a> recommends using <a target="_blank" href="https://zeit.co/now">Now</a>.</p>
<p>Go to <a target="_blank" href="https://zeit.co/download">Now Desktop</a> and click download. <strong>But</strong>: the desktop app doesn’t currently work for Linux. I’m on Ubuntu, so I just used their <a target="_blank" href="https://zeit.co/download#now-cli">command line interface</a>. That’s what I use in this instructions.</p>
<ol>
<li>Once you’ve installed on your machine, log in. You should be able to return to the <a target="_blank" href="https://zeit.co/now">Now site</a> and see your name or picture at the top right.</li>
<li>Go to the <code>api</code> directory and type <code>now</code>. You’ll be prompted a few times to enter things. If all goes well, you’ll get a long series of output in the terminal ending with a success message:</li>
</ol>
<pre><code>$ now&gt; Read more about how to update here: https:<span class="hljs-comment">//zeit.co/update-cli&gt; Deploying ~/projects/events2/api under xxxxxxxxxx@gmail.com&gt; Your deployment's code and logs will be publicly accessible because you are subscribed to the OSS plan.&gt; <span class="hljs-doctag">NOTE:</span> You can use `now --public` or upgrade your plan (https://zeit.co/account/plan) to skip this prompt&gt; Upload [=============-------] 66% 0.1s (192.48KB) [4 files]&gt; Using Node.js 8.11.3 (default)&gt; https://grand-stack-starter-api-qibrvosvuh.now.sh [in clipboard] (bru1) [7s]&gt; Synced 4 files (192.48KB) [7s]&gt; Building…&gt; ▲ npm install&gt; ✓ Using "package-lock.json"&gt; ⧗ Installing 13 main dependencies…&gt; Building "nodemon@1.18.1" remotely&gt; Building "nodemon@1.18.1" remotely&gt; Building "nodemon@1.18.1" remotely&gt; Building "nodemon@1.18.1" remotely&gt; Error: Error parsing `package.json` for nodemon-1.18.1.tar&gt;     at extract (/snapshot/ace/lib/extract.js:36:11)&gt;     at process._tickCallback (internal/process/next_tick.js:188:7)&gt;     at &lt;anonymous&gt;&gt; ▲ npm install&gt; &gt; protobufjs@6.8.6 postinstall /home/nowuser/src/node_modules/protobufjs&gt; node scripts/postinstall&gt; &gt; &gt; nodemon@1.18.1 postinstall /home/nowuser/src/node_modules/nodemon&gt; node bin/postinstall || exit 0&gt; &gt; Love nodemon? You can now support the project via the open collective:&gt;  &gt; https://opencollective.com/nodemon/donate&gt; &gt; npm WARN grand-stack-starter-api@0.0.1 No repository field.&gt; npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@1.2.4 (node_modules/fsevents):&gt; npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.4: wanted {"os":"darwin","arch":"any"} (current: {"os":"linux","arch":"x64"})&gt; &gt; added 564 packages in 9.426s&gt; ▲ Snapshotting deployment&gt; ▲ Saving deployment image (9.0M)&gt; Build completed&gt; Verifying instantiation in bru1&gt; [0] &gt; [0] grand-stack-starter-api@0.0.1 start /home/nowuser/src&gt; [0] nodemon --exec babel-node src/index.js&gt; [0] &gt; [0] [nodemon] 1.18.1&gt; [0] [nodemon] to restart at any time, enter `rs`&gt; [0] [nodemon] watching: *.*&gt; [0] [nodemon] starting `babel-node src/index.js`&gt; ✔ Scaled 1 instance in bru1 [13s]Open that url, and you should see your playground. Try it to be sure that it’s working:</span>
</code></pre><p>The most important thing is a line indicating your live endpoint:</p>
<pre><code>https:<span class="hljs-comment">//grand-stack-starter-api-qibrvosvuh.now.sh [in clipboard]</span>
</code></pre><p>Open that URL, and you should see your playground. Try it to be sure that it’s working:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/D2UG0F8A2zauoEePjiO-9qkNzyJ48cp88KKL" alt="Image" width="800" height="245" loading="lazy"></p>
<h3 id="heading-what-you-have-achieved">What You Have Achieved</h3>
<p>That’s it — a live development server, with very little time and no money down! You have a full stack. But unlike a LAMP stack, this stack includes the API itself. Note that all you had to modify was was <code>typeDefs</code> and <code>resolvers</code>.</p>
<p>Understand that this is not yet production quality:</p>
<ul>
<li>Your time is very limited to use this without setting up a permanent database. You can now play with this on your front end until your Neo4j Sandbox expires. That will be 3 days, but you can request an extra seven. And of course, you can always create a new Sandbox. If you like, you can also store a set of mutations at <code>api/src/seed/seed-mutations.js</code>. You can run it with the command <code>npm seedDb</code> whenever you change to a new sandbox.</li>
<li>Whenever you want to change your version on now, the URL will change. You can <a target="_blank" href="https://zeit.co/docs/features/aliases">set up an alias</a> to allow your code to handle that, but it’s not ideal long-term.</li>
</ul>
<p>But for an MVP, and for initial development, it is enough and is free. If you have a domain, you can use any hosting service set up a <a target="_blank" href="https://www.linode.com/docs/networking/dns/dns-records-an-introduction/#cname">CNAME record</a> to run your Now page from it.</p>
<p>Check out <a target="_blank" href="https://medium.com/@yisroelyakovson/making-your-single-graph-stack-production-quality-ec231a938551">Making Your Full Graph Stack Production Quality</a> to learn how to deploy for release.</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
