<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ricardo Gonzalez</title>
    <description>The latest articles on DEV Community by Ricardo Gonzalez (@ricardoagz).</description>
    <link>https://dev.to/ricardoagz</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F826108%2Fdbaab52c-9034-4710-a333-de38a524b0ff.png</url>
      <title>DEV Community: Ricardo Gonzalez</title>
      <link>https://dev.to/ricardoagz</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ricardoagz"/>
    <language>en</language>
    <item>
      <title>Lets build a HackerNews clone in &lt;= 30 minutes</title>
      <dc:creator>Ricardo Gonzalez</dc:creator>
      <pubDate>Thu, 03 Nov 2022 07:49:46 +0000</pubDate>
      <link>https://dev.to/ricardoagz/building-a-hackernews-clone-in-30-minutes-3lh3</link>
      <guid>https://dev.to/ricardoagz/building-a-hackernews-clone-in-30-minutes-3lh3</guid>
      <description>&lt;h2&gt;
  
  
  A MERN Stack Tutorial Using Neutrino
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Preface
&lt;/h3&gt;

&lt;p&gt;I love coding, but building CRUD apps and authentication systems can get pretty repetitive over time.&lt;/p&gt;

&lt;p&gt;Tools like Ruby on Rails and Django have made this process significantly easier and more enjoyable, however, there is still a lot of repetitiveness I would ideally like to avoid.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.neutrinoapp.com/" rel="noopener noreferrer"&gt;Neutrino&lt;/a&gt; makes it significantly faster to code backend architectures. It gives you a nice, intuitive interface to design your backend system and automatically implement most of the code for you using Node/Express best practices and an MVC architecture. &lt;/p&gt;

&lt;p&gt;If you don't know what &lt;a href="https://news.ycombinator.com/" rel="noopener noreferrer"&gt;HackerNews&lt;/a&gt; is, its a Reddit-esque forum built by YCombinator to talk about entrepreneurship.&lt;/p&gt;

&lt;p&gt;This tutorial will be demonstrating how to use Neutrino to build a simplified HackerNews clone in under 30 minutes. &lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  What is the MERN Stack
&lt;/h3&gt;

&lt;p&gt;MERN stack refers to MongoDB, Express, React, and Node: &lt;br&gt;
&lt;strong&gt;Mongo&lt;/strong&gt; - A popular no-sql database program&lt;br&gt;
&lt;strong&gt;Express&lt;/strong&gt; - A backend JavaScript web application framework &lt;br&gt;
&lt;strong&gt;React&lt;/strong&gt; - A front-end JavaScript library for building user interfaces&lt;br&gt;
&lt;strong&gt;Node&lt;/strong&gt; - An open source JavaScript runtime environment&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;
&lt;h3&gt;
  
  
  MVC Architecture
&lt;/h3&gt;

&lt;p&gt;MVC is an architectural pattern for building software and web applications that consists of 3 parts, the Model, the View, and the Controller&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Model&lt;/strong&gt; - handles all data logic and directly interacts with the database. In this case, we will be using MongoDB and Mongoose, which is a library built on top of Mongo that we will use to define our model schema and interact with our Express server&lt;br&gt;
&lt;strong&gt;View&lt;/strong&gt; - handles all client side logic, this is the React side of the application and will be what the user interacts with&lt;br&gt;
&lt;strong&gt;Controller&lt;/strong&gt; - acts as an interface between the Model and View. It processes all requests, fetches data from the Model to send to the View, and takes in information from the View to update the Model&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;
&lt;h3&gt;
  
  
  App Overview
&lt;/h3&gt;
&lt;h4&gt;
  
  
  What we'll be building:
&lt;/h4&gt;

&lt;p&gt;Check out the &lt;a href="https://neutrino-news.web.app/posts" rel="noopener noreferrer"&gt;demo&lt;/a&gt;&lt;br&gt;
We will build a forum where users can sign up, log in/out, create posts, like posts, comment on posts, and reply to comments. &lt;br&gt;
The app should also include a front page where you can view all posts and click on individual posts to view all comments. &lt;br&gt;
This is strictly a backend tutorial but if you want to follow along, feel free to clone the &lt;a href="https://github.com/ricardo-agz/NeutrinoNewsFrontend" rel="noopener noreferrer"&gt;frontend code&lt;/a&gt; from GitHub.&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;
&lt;h3&gt;
  
  
  Getting Started
&lt;/h3&gt;
&lt;h4&gt;
  
  
  Creating a Neutrino account
&lt;/h4&gt;

&lt;p&gt;Register for a Neutrino account &lt;a href="https://app.neutrinojs.dev/register" rel="noopener noreferrer"&gt;here&lt;/a&gt; (don't worry, its free!)&lt;br&gt;
After logging in, you should be prompted to this menu:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbtzau6qnugpwkgtlk755.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbtzau6qnugpwkgtlk755.JPG" alt="Neutrino Homepage"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click on 'start from an empty project'...&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;
&lt;h3&gt;
  
  
  Setting Up the Models
&lt;/h3&gt;

&lt;p&gt;Let's start the project by defining the models we're going to use. In the context of &lt;del&gt;HackerNews&lt;/del&gt; NeutrinoNews, we're going to need Users, Posts, and Comments.&lt;/p&gt;
&lt;h4&gt;
  
  
  User
&lt;/h4&gt;

&lt;p&gt;Users will have a username (string), email (string), and password (string). &lt;/p&gt;
&lt;h4&gt;
  
  
  Post
&lt;/h4&gt;

&lt;p&gt;Posts should have a title (string), url (string), and content (string).&lt;/p&gt;
&lt;h4&gt;
  
  
  Comment
&lt;/h4&gt;

&lt;p&gt;Users to have content (string) and a user (Object Id).&lt;/p&gt;

&lt;p&gt;ℹ️ Make sure to leave 'initialize with with controller' checked when adding the Models.&lt;/p&gt;

&lt;p&gt; &lt;br&gt;
Your Models page should look something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh8alepdpxqx55bk8yrue.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh8alepdpxqx55bk8yrue.JPG" alt="Model parameters"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;
&lt;h4&gt;
  
  
  Model Relationships
&lt;/h4&gt;

&lt;p&gt;We want to define our database relationships to handle features such as likes and posting. &lt;/p&gt;

&lt;p&gt;A user should be able to make posts, therefore User &lt;em&gt;has many&lt;/em&gt; Posts and a Post &lt;em&gt;belongs to&lt;/em&gt; User.&lt;/p&gt;

&lt;p&gt;A user should be able to comment on posts. With this, there are a couple implementations, but for this tutorial, Post &lt;em&gt;has many&lt;/em&gt; Comment and Comment &lt;em&gt;belongs to&lt;/em&gt; Post. &lt;br&gt;
Remember we added a &lt;em&gt;user&lt;/em&gt; parameter of type Object Id for Comment. &lt;/p&gt;
&lt;h4&gt;
  
  
  Implementing Likes
&lt;/h4&gt;

&lt;p&gt;Likes will be implemented as a many-to-many relationship between User and Post. &lt;br&gt;
That is, a User &lt;em&gt;has many&lt;/em&gt; Post (likedPosts), and Post &lt;em&gt;has many&lt;/em&gt; User (likedBy).&lt;/p&gt;
&lt;h4&gt;
  
  
  Implementing Replies
&lt;/h4&gt;

&lt;p&gt;We will add an additional, optional parameter to Comment called &lt;em&gt;replyParent&lt;/em&gt;, which will be the Object Id of the parent Comment if the comment is a reply.&lt;/p&gt;

&lt;p&gt; &lt;br&gt;
Your models page should now look something like this...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fem572rz3xmhy2ganq1vs.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fem572rz3xmhy2ganq1vs.JPG" alt="Models page with relations"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;
&lt;h3&gt;
  
  
  API Routes
&lt;/h3&gt;

&lt;p&gt;Navigate to the Routes page.&lt;br&gt;
If you left the checkbox selected when creating the models through the modal, then Neutrino should have automatically implemented all the CRUD routes for User, Post, and Comment. That being said, there are still a couple of changes we need to make. &lt;/p&gt;

&lt;p&gt;Your Routes page should look something like this...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7acckd7wiods1hst3non.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7acckd7wiods1hst3non.JPG" alt="Routes page initial"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;
&lt;h4&gt;
  
  
  Liking Posts
&lt;/h4&gt;

&lt;p&gt;We want to create 2 functions for the User controller:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;likePost&lt;/li&gt;
&lt;li&gt;unlikePost&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;likePost&lt;/strong&gt;&lt;br&gt;
HTTP method: POST&lt;br&gt;
url: /users/:id/like-post/:post_id&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;unlikePost&lt;/strong&gt;&lt;br&gt;
HTTP method: POST&lt;br&gt;
url: /users/:id/unlike-post/:post_id&lt;/p&gt;

&lt;p&gt;You can implement these functions by clicking on &lt;em&gt;add route&lt;/em&gt;_ as follows:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz2i6ktvomu268sziuht6.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz2i6ktvomu268sziuht6.JPG" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;...Do the same for unlikePost.&lt;/p&gt;

&lt;p&gt;We can now write out the logic functions by clicking on the orange gear icon by the route.&lt;/p&gt;

&lt;p&gt; &lt;br&gt;
&lt;strong&gt;Logic&lt;/strong&gt;&lt;br&gt;
Liking posts works as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;We find the User with _&lt;em&gt;id = id&lt;/em&gt;, and push &lt;em&gt;post_id&lt;/em&gt; to its &lt;em&gt;likedPosts&lt;/em&gt; array.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We then find the Post with _&lt;em&gt;id = post_id&lt;/em&gt; and push &lt;em&gt;id&lt;/em&gt; to its &lt;em&gt;likedBy&lt;/em&gt; array.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We then return a success message or an error if anything didn't work. &lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can do this pretty easily using the code blocks as follows:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnkrkg9ic26jfh37thhqh.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnkrkg9ic26jfh37thhqh.JPG" alt="logic blocks for likePost"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;...or if you'd rather code it yourself, this is what the code would look like using Mongoose:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;likePost: async (req, res) =&amp;gt; {
    try {
      const { id, post_id } = req.params;
      User.findByIdAndUpdate(id,
      {
        $push: { likedPosts: post_id }
      },
      (err, data) =&amp;gt; {
        if (err) {
          return res.status(500).send({ message: "Error updating user" });
        };
        Post.findByIdAndUpdate(post_id,
        {
          $push:  { likedBy: id },
        },
        (err2, data2) =&amp;gt; {
          if (err2) {
            return res.status(500).send({ message: "Error updating post" });
          };
          return res.status(200).send({ message: "Successfully liked post and added user to post likes" });
        });
      });
    } catch(e) {
      console.error(`server error in UserController likePost() : ${e}`);
    };
  },
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt; &lt;br&gt;
&lt;strong&gt;unlikePost&lt;/strong&gt;&lt;br&gt;
This function will be exactly the same except that instead of pushing, you will be pulling.&lt;/p&gt;

&lt;p&gt;i.e.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$push:  { likedBy: id },
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;will instead be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$pull:  { likedBy: id },
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt; &lt;/p&gt;

&lt;h4&gt;
  
  
  Creating Replies
&lt;/h4&gt;

&lt;p&gt;We will write two functions for the Comment Controller:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;createReply&lt;/li&gt;
&lt;li&gt;viewReplies&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;createReply&lt;/strong&gt;&lt;br&gt;
HTTP method: POST&lt;br&gt;
url: /comments/:id/create-reply&lt;br&gt;
Here, we want to create a new Comment, and pass in the additional &lt;em&gt;replyParent = id&lt;/em&gt; parameter&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyfb6x6fbgcp9ul8lo9su.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyfb6x6fbgcp9ul8lo9su.JPG" alt="createReply code blocks"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;viewReplies&lt;/strong&gt;&lt;br&gt;
HTTP method: GET&lt;br&gt;
url: /comments/:id/replies&lt;br&gt;
Here, we want to find all comments where &lt;em&gt;replyParent == id&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffzzt730isccf50rgf406.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffzzt730isccf50rgf406.JPG" alt="viewReplies code blocks"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt; &lt;br&gt;
If you are interested in coding them yourself, the code using Mongoose would look as follows...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/*
   * createReply
   * url: /comments/:id/create-reply
   * params: ['id', 'reply_id']
   */
  createReply: async (req, res) =&amp;gt; {
    try {
      const { id } = req.params;
      const { content, user, post } = req.body;
      const newReply = await new Comment({
        content,
        user,
        post,
        replyParent: id,
      }).save((err, data) =&amp;gt; {
        if (err) {
          return res.status(500).send({ message: "Error creating comment" });
        };
        return res.status(200).send({ message: "Successfuly created reply comment" });
      });
    } catch(e) {
      console.error(`server error in CommentController createReply() : ${e}`);
    };
  },

  /*
   * view replies
   * url: /comments/:id/replies
   */
  viewReplies: async (req, res) =&amp;gt; {
    const { id } = req.params
    try {
      const replies = await Comment.find({ replyTo: id });
      return res.status(200).send(replies);
    } catch(e) {
      console.error(`server error in CommentController index() : ${e}`);
    };
  },
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  Authentication
&lt;/h3&gt;

&lt;p&gt;Neutrino makes it very simple to include JWT authentication out of the box.&lt;/p&gt;

&lt;p&gt;Navigate to &lt;em&gt;Auth&lt;/em&gt; and click on Simple Authentication&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdrubtcnqqziz9ardwhcp.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdrubtcnqqziz9ardwhcp.JPG" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt; &lt;br&gt;
This will generate an Auth Controller as well as &lt;em&gt;logIn&lt;/em&gt;, &lt;em&gt;register&lt;/em&gt; routes, and a &lt;em&gt;verifyJWT&lt;/em&gt; middleware.&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  Security
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Middlewares
&lt;/h4&gt;

&lt;p&gt;Let's say we want to reserve a few features for only users that have signed in. &lt;/p&gt;

&lt;p&gt;We can go back to the Routes page and add the &lt;em&gt;verifyJWT&lt;/em&gt; middleware to the route to block a non-signed in user from calling the function.&lt;/p&gt;

&lt;p&gt;ex.&lt;br&gt;
For creating, editing, and deleting posts, we can add the &lt;em&gt;verifyJWT&lt;/em&gt; middleware as follows...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjq5o8gqdlbp5c9bh6nv9.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjq5o8gqdlbp5c9bh6nv9.JPG" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  Exporting your project
&lt;/h3&gt;

&lt;p&gt;By navigating to the &lt;em&gt;Export&lt;/em&gt; tab, you can download your code as a Node, MongoDB, Express project following an MVC architecture, that should hopefully feel a bit intuitive to work on top off. &lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  Finishing Thoughts
&lt;/h3&gt;

&lt;p&gt;Okay there's a chance after reading everything and probably making some changes, this project took a little more than 30 minutes.&lt;br&gt;
But really, we had the bulk of our backend functionality up and running in only a couple of minutes using Neutrino.&lt;/p&gt;

&lt;p&gt;There's obviously a lot more that can be done fixing up the app and adding more features, but hopefully after following these examples, you gathered enough on your own to get started with the rest.&lt;/p&gt;

&lt;p&gt;Happy Coding!&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>tooling</category>
      <category>react</category>
      <category>mongodb</category>
    </item>
    <item>
      <title>Lets Build a Twitter Clone in &lt;= 30 minutes</title>
      <dc:creator>Ricardo Gonzalez</dc:creator>
      <pubDate>Mon, 04 Apr 2022 07:18:29 +0000</pubDate>
      <link>https://dev.to/ricardoagz/lets-build-a-twitter-clone-in-30-minutes-1851</link>
      <guid>https://dev.to/ricardoagz/lets-build-a-twitter-clone-in-30-minutes-1851</guid>
      <description>&lt;h2&gt;
  
  
  A MERN Stack Tutorial Using Neutrino JS
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Preface
&lt;/h2&gt;

&lt;p&gt;I've been building web and mobile apps for a couple of years now, and what I hate the most has consistently been the hassle of starting a new project. &lt;br&gt;
It takes hours of coding and debugging just to get to a pretty generic point of in the application's process. &lt;/p&gt;

&lt;p&gt;I came across Ruby on Rails around a year and a half ago and it pretty quickly became my go-to when creating new web apps. &lt;br&gt;
Its CLI and generators are pretty unmatched when trying to build a prototype as quickly as possible, while completely eliminating the need to write any boilerplate code.&lt;/p&gt;

&lt;p&gt;I recently came across this pretty cool dev tool called Neutrino, that's basically an online web builder for MERN stack web apps. &lt;br&gt;
I'd call it a glorified GUI for what would be the equivalent of Rails's generators, but it would really be doing it a disservice since it really is quite a bit more powerful. &lt;/p&gt;

&lt;p&gt;This tutorial will be demonstrating how to use Neutrino to build a simplified Twitter clone in under 30 minutes. &lt;/p&gt;

&lt;p&gt; &lt;/p&gt;
&lt;h3&gt;
  
  
  What is the MERN Stack
&lt;/h3&gt;

&lt;p&gt;MERN stack refers to MongoDB, Express, React, and Node: &lt;br&gt;
&lt;strong&gt;Mongo&lt;/strong&gt; - A popular no-sql database program&lt;br&gt;
&lt;strong&gt;Express&lt;/strong&gt; - A backend JavaScript web application framework &lt;br&gt;
&lt;strong&gt;React&lt;/strong&gt; - A front-end JavaScript library for building user interfaces&lt;br&gt;
&lt;strong&gt;Node&lt;/strong&gt; - An open source JavaScript runtime environment&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;
&lt;h3&gt;
  
  
  MVC Architecture
&lt;/h3&gt;

&lt;p&gt;MVC is an architectural pattern for building software and web applications that consists of 3 parts, the Model, the View, and the Controller&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Model&lt;/strong&gt; - handles all data logic and directly interacts with the database. In this case, we will be using MongoDB and Mongoose, which is a library built on top of Mongo that we will use to define our model schema and interact with our Express server&lt;br&gt;
&lt;strong&gt;View&lt;/strong&gt; - handles all client side logic, this is the React side of the application and will be what the user interacts with&lt;br&gt;
&lt;strong&gt;Controller&lt;/strong&gt; - acts as an interface between the Model and View. It processes all requests, fetches data from the Model to send to the View, and takes in information from the View to update the Model&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;
&lt;h3&gt;
  
  
  Getting Started
&lt;/h3&gt;

&lt;p&gt;Neutrino uses MongoDB to power its database, in this tutorial, we will be using MongoDb Atlas, but you could run MongoDB locally as well if you wanted.&lt;br&gt;
We won't go over how to set up a new MongoDB Atlas cluster and database, but you can follow &lt;a href="https://www.mongodb.com/basics/mongodb-atlas-tutorial" rel="noopener noreferrer"&gt;this tutorial&lt;/a&gt; to get started.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Once you've created your cluster and set up your database, you're going to want to get your &lt;a href="https://docs.mongodb.com/guides/cloud/connectionstring/" rel="noopener noreferrer"&gt;connection string&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You're also going to want to make sure you have Node js installed to run your application.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt; &lt;/p&gt;
&lt;h3&gt;
  
  
  Setting Up the Models
&lt;/h3&gt;

&lt;p&gt;First, let's start a new Neutrino project at &lt;a href="https://app.neutrinojs.dev/" rel="noopener noreferrer"&gt;app.neutrinojs.dev&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you're curious and want to check out their documentation, you can find it at &lt;a href="https://neutrinojs.dev/docs" rel="noopener noreferrer"&gt;neutrinojs.dev/docs&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  User
&lt;/h4&gt;

&lt;p&gt;We want users to be able to have a name, username, and bio, as well as be able to register to our app, which will require us to define email and password parameters as well.&lt;/p&gt;

&lt;p&gt;So, our params will be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;name - string&lt;/li&gt;
&lt;li&gt;username - string&lt;/li&gt;
&lt;li&gt;email - string&lt;/li&gt;
&lt;li&gt;password - string&lt;/li&gt;
&lt;li&gt;bio - text&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;note:&lt;/strong&gt; Here, we differentiate string from text, but text is nothing more than a string without character limits. It will also default to a &lt;em&gt;textarea&lt;/em&gt; component in React.&lt;/p&gt;
&lt;h4&gt;
  
  
  Post
&lt;/h4&gt;

&lt;p&gt;We want users to be able to make posts, and posts to contain a few different parameters, such as the number of likes, title, content, and comments.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We could build comments right into the post, but it would be much better practice to separate them out into their own model with their own CRUD functionality.&lt;/li&gt;
&lt;li&gt;Similarly for likes, we could build them right into a number parameter for post, but what if we want to access the people who liked the post? Or get a list of all the posts a user has liked? We'd need more than a simple number keeping track of the number of times a user has pressed 'like'. We will go over this later.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Therefore, our data parameters will look like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;title - string&lt;/li&gt;
&lt;li&gt;content - text&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  Comment
&lt;/h4&gt;

&lt;p&gt;We want users to be able to comment on different posts, and we want these comments to be associated with the user that posted them.&lt;/p&gt;

&lt;p&gt;Therefore, out data parameters will look like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;content - string&lt;/li&gt;
&lt;li&gt;user - string (actually, this will be a Mongoose id, but we will discuss this later) &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt; &lt;/p&gt;
&lt;h3&gt;
  
  
  Defining Model Relations
&lt;/h3&gt;

&lt;p&gt;When we discuss model relations, we're going to bring up terminology such as one-to-many or many-to-many, these are terms typically used in SQL databases, and the meaning doesn't really apply in the same way as it would in an SQL database. Nevertheless, they are still effective at conveying the hierarchy of our models and how they will interact with each other. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;We want users to be able to make posts, therefore, a user will be able to have many posts, but a post will belong to only one user. This is a one-to-many relationship between users and posts.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We also want users to be able to comment on different posts. Therefore, a post can have many comments, but a comment can only belong to one post. This again, is a one-to-many relationship between posts and comments. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;By extension, we can also represent a one-to-many relationship between users and comments, however, Neutrino currently doesn't support a multiple one-to-many relationships for the same 'many' model, so we will just have to do this manually.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;To summarize:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a user &lt;em&gt;has many&lt;/em&gt; posts&lt;/li&gt;
&lt;li&gt;a post &lt;em&gt;belongs to&lt;/em&gt; a user&lt;/li&gt;
&lt;li&gt;a post &lt;em&gt;has many&lt;/em&gt; comments&lt;/li&gt;
&lt;li&gt;a comment &lt;em&gt;belongs to&lt;/em&gt; a post&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt; &lt;/p&gt;
&lt;h3&gt;
  
  
  Implementing in Neutrino
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Step 1)&lt;/strong&gt; Create a new model and name it User (by convention Neutrino requires you to name your models as singular nouns)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzysg2l1t2oszlj6vo78p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzysg2l1t2oszlj6vo78p.png" alt="new model page"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;
&lt;h3&gt;
  
  
  Adding Data Parameters
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Step 2)&lt;/strong&gt; Click on 'authentication', which will automatically create the username, email, and password parameters, and manually pass in the &lt;em&gt;name:string&lt;/em&gt; and &lt;em&gt;bio:text&lt;/em&gt; params by clicking on 'ADD PARAM'&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnmntyv82won3z5v78783.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnmntyv82won3z5v78783.png" alt="add param to model"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3)&lt;/strong&gt; Create the Post and Comment models and pass in their required data parameters that we specified before. &lt;br&gt;
So for Post, it would be &lt;em&gt;title:string&lt;/em&gt; and &lt;em&gt;content:text&lt;/em&gt;, and for Comment, it would be &lt;em&gt;content:string&lt;/em&gt; and &lt;em&gt;user:string&lt;/em&gt;. &lt;br&gt;
After doing this, your models page should look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9li2sh8v5528kyp44qnx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9li2sh8v5528kyp44qnx.png" alt="models page with data params"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;
&lt;h3&gt;
  
  
  Implementing Model Relations
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Step 4)&lt;/strong&gt; We said we wanted two one-to-many relationships, one between User and Post, and one between Post and Comment.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We can do this by passing a &lt;em&gt;has_many: Post&lt;/em&gt; param for User and a &lt;em&gt;belongs_to: User&lt;/em&gt; param for Post.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5ireb5pfu60kr6vszcoc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5ireb5pfu60kr6vszcoc.png" alt="add relationship to model"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After doing this for Post and Comment, your models page should look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbrstjyeo04suz1iwqbte.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbrstjyeo04suz1iwqbte.png" alt="models page with relationships"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And well, the Relations page doesn't really do much yet, but if you did everything correctly, it should look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6z394w765ymfw705dede.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6z394w765ymfw705dede.png" alt="relations page"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;
&lt;h3&gt;
  
  
  Routing
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Step 5)&lt;/strong&gt; We enabled authentication by defining User as an &lt;em&gt;authObject&lt;/em&gt; in the Models page, now we want to specify which routes we actually want and which ones we want to protect.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lets head to the Routes page, which should originally look like this:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsuau6ygveqt4eny2yit9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsuau6ygveqt4eny2yit9.png" alt="routes page"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Neutrino scaffolds create all of the RESTful routes for each model by default, so for user it would be index, show, create, update, destroy, etc. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Note that the &lt;em&gt;new&lt;/em&gt;, and &lt;em&gt;edit&lt;/em&gt; routes are created only in the frontend, they simply render a form and don't actually call the backend until you hit submit. 
(with the exception that &lt;em&gt;edit&lt;/em&gt; actually makes a GET request to load all the current model info).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt; &lt;/p&gt;
&lt;h4&gt;
  
  
  Disabling Unnecessary Routes
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 5a)&lt;/strong&gt; We clearly don't want each route available for every model, so let's start out by disabling a couple. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We don't really want users to be able to access a list of all comments ever created so lets disable the &lt;em&gt;index&lt;/em&gt; route for Comment&lt;/li&gt;
&lt;li&gt;We also don't need an individual page to display a singular comment so we can go ahead and disable the &lt;em&gt;show&lt;/em&gt; route for for Comment&lt;/li&gt;
&lt;li&gt;And finally, let's say we don't want Users to be able to modify a comment after commenting, so let's disable the &lt;em&gt;update&lt;/em&gt; route for Comment (note that this automatically disables the &lt;em&gt;edit&lt;/em&gt; route too).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your Comment routes should now look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkvwsgd1mqygjhjjmnm7m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkvwsgd1mqygjhjjmnm7m.png" alt="comment routes"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;
&lt;h4&gt;
  
  
  Route Protection
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 5b)&lt;/strong&gt; By enabling route protection, we are enabling two things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;em&gt;verifyJWT&lt;/em&gt; middleware in the backend, which will make sure the user is authenticated before enabling them access to the route.&lt;/li&gt;
&lt;li&gt;The &lt;em&gt;PrivateRoute&lt;/em&gt; component in the frontend, which will automatically redirect the user to the login page if they're not authenticated.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can split all routes into two main categories: public routes, accessible to anyone regardless of whether or not they're signed in, and private routes, which should only be accessible to logged in users.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We want users to be able to see all posts and be able to click on a post to see its comments even if they're not logged in, so we can leave both the Post &lt;em&gt;index&lt;/em&gt; and &lt;em&gt;show&lt;/em&gt; routes as public. &lt;/li&gt;
&lt;li&gt;We also want unauthenticated users to be able to create a new user (by registering an account), so we can leave User &lt;em&gt;create&lt;/em&gt; as public too.&lt;/li&gt;
&lt;li&gt;However, we want Users to be authenticated to do anything else.&lt;/li&gt;
&lt;li&gt;Let's protect all other routes by clicking on the &lt;em&gt;protected&lt;/em&gt; lock icon.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your routes should look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffcjpw31q6aecrajflxzn.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffcjpw31q6aecrajflxzn.JPG" alt="users routes protected"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvr4xzrbyzw8ul1zj11ys.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvr4xzrbyzw8ul1zj11ys.JPG" alt="posts routes protected"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvk2igyk3l1rkb24call1.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvk2igyk3l1rkb24call1.JPG" alt="comments routes protected"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;
&lt;h4&gt;
  
  
  Route Logic
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 5c)&lt;/strong&gt; Neutrino has a pretty neat feature of offering route logic templates for certain routes, these can be anything from hiding certain parameters such as passwords on GET requests, to verifying to see if a user is trying to modify another user's content. &lt;/p&gt;

&lt;p&gt;Lets look at these route by route:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User &lt;em&gt;show&lt;/em&gt;: 

&lt;ul&gt;
&lt;li&gt;A GET request to User will return all of the user's parameters by default (the password will be hashed but we still don't need other users to see this).&lt;/li&gt;
&lt;li&gt;Lets enable the &lt;em&gt;protect info&lt;/em&gt; logic template by clicking on the gear button and then on &lt;em&gt;protect info&lt;/em&gt; to automatically hide the password field for the logged in user and the password and email field for anyone else (even if a user is signed in, we don't want them to access another user's email).&lt;/li&gt;
&lt;li&gt;You could also hide other parameters if you wanted, so if you didn't want other users to access the &lt;em&gt;name&lt;/em&gt; parameter, you could pass that into &lt;em&gt;hide&lt;/em&gt; as well.&lt;/li&gt;
&lt;li&gt;Make sure to hit 'SAVE'.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhttneuqos6jkcra9ux84.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhttneuqos6jkcra9ux84.JPG" alt="user show logic"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User &lt;em&gt;update&lt;/em&gt;:

&lt;ul&gt;
&lt;li&gt;We clearly don't want users to edit other users' information so lets enable logic and click on the &lt;em&gt;protect update&lt;/em&gt; template.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgczv5c515ohymw23vtlz.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgczv5c515ohymw23vtlz.JPG" alt="user update logic"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User &lt;em&gt;delete&lt;/em&gt;:

&lt;ul&gt;
&lt;li&gt;We don't want users to be able to delete other users' accounts so lets enable logic and click on the &lt;em&gt;protect action&lt;/em&gt; template.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7ppkz7q9nqrdvd7n4goj.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7ppkz7q9nqrdvd7n4goj.JPG" alt="user delete logic"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's understand what we just did:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;req.user.id&lt;/strong&gt;: Refers to the ID that is associated with the currently authenticated user making the request. This only works if VerifyJWT was enabled for this particular route.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;data._id.toString()&lt;/strong&gt;: The data object is the object that we are trying to access from the database. We are then accessing the data's (which is of type User) _id parameter. Lastly we have to convert the _id object into a string, so we use toString().&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;hide&lt;/strong&gt;: Refers to a special shorthand that removes certain keys from the response object. In our example in the if statement we try to hide password and email, so on the user side when the response object is received the response will never contain the password, as it is sensitive information, and will only include the email if the user fetched is the same user making the request.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;error&lt;/strong&gt;: Error is a special shorthand to send a 500 response to the user with the given Error message after the = sign. So if we wanted to send an error with a different message, “Not Nice”, we could replace the error line with error=Not Nice.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now for Post:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Post: &lt;em&gt;create&lt;/em&gt;

&lt;ul&gt;
&lt;li&gt;When a user creates a new Post, we don't want them to be able to modify the id of the user that created it as this would essentially be impersonating another user. So let's enable route logic and click on the &lt;em&gt;protect create&lt;/em&gt; template.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwlf7lkjejiqub10s69eh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwlf7lkjejiqub10s69eh.png" alt="create post logic"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Post: &lt;em&gt;update&lt;/em&gt;

&lt;ul&gt;
&lt;li&gt;We obviously don't want users editing other users' posts.&lt;/li&gt;
&lt;li&gt;We also don't want a user to be able to modify the user parameter for a post (even if it is their own) because this would essentially be impersonating another user. Let's enable route logic and click on the &lt;em&gt;protect update&lt;/em&gt; template.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv9jyoi9yzts3jbru32nq.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv9jyoi9yzts3jbru32nq.JPG" alt="post update logic"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Post: &lt;em&gt;delete&lt;/em&gt;

&lt;ul&gt;
&lt;li&gt;We don't want users to be able to delete another user's post, so lets pass in some route logic.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwz5yh8n6cfmvpbw20o9f.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwz5yh8n6cfmvpbw20o9f.JPG" alt="post delete logic"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now for Comment&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Comment: &lt;em&gt;create&lt;/em&gt;

&lt;ul&gt;
&lt;li&gt;Neutrino actually doesn't provide any templates for this route since we didn't specify a one to many with the &lt;em&gt;authObject&lt;/em&gt; (User), but we can use what we just learned about routes to do the same thing.&lt;/li&gt;
&lt;li&gt;Since we don't want users to be able to make comments on behalf of another user.
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (req.user.id != req.body.user) {
    error=Incorrect parameters
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcdvzmdtcl7163hpsqsfx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcdvzmdtcl7163hpsqsfx.png" alt="Comment create logic"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Comment: &lt;em&gt;delete&lt;/em&gt;

&lt;ul&gt;
&lt;li&gt;Since we don't want users to be able to delete other users' comments.
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (req.user.id != data.user) {
    error=Cannot delete another users comment
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0czkznppt0fqpv9vvzsu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0czkznppt0fqpv9vvzsu.png" alt="comment delete logic"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Lets understand what this is doing:&lt;/p&gt;

&lt;p&gt;Remember that we're passing user:String as a parameter when creating a comment. That means that we're storing the id of the user that created the comment. As such, we can compare it with the id of the user making the request through &lt;em&gt;req.user.id&lt;/em&gt; to see if the user making the request is the same user that created the comment.&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;
&lt;h4&gt;
  
  
  Rewind
&lt;/h4&gt;

&lt;p&gt;We still haven't discussed how we will implement likes. &lt;br&gt;
This is partially by design since I didn't want to intimidate beginners with too much information, but now you've made it this far so let's implement likes. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Likes will be a many-to-many relationship between User and Post (Even though we previously declared them to have a one-to-many relationship, they now have both).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;That is, a user can &lt;em&gt;like&lt;/em&gt; many posts, and a post can have likes from many users.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt; &lt;/p&gt;
&lt;h3&gt;
  
  
  Implementing Likes
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Step 6)&lt;/strong&gt; Lets go back to the Models page and add another &lt;em&gt;has many: Post&lt;/em&gt; param for User and a &lt;em&gt;has many: User&lt;/em&gt; param for Post&lt;/p&gt;

&lt;p&gt;Your Models page should look like this:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0vmfz2nd559nf348i5pg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0vmfz2nd559nf348i5pg.png" alt="new models page"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Your Relations page should look like this:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F557962ppbgfvfnsh0pn5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F557962ppbgfvfnsh0pn5.png" alt="new relations page"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note that Neutrino automatically adds two new routes for Many-to-Many relationships:&lt;/p&gt;

&lt;p&gt;addPost and dropPost&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnk6bg2f4amswes50a1yi.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnk6bg2f4amswes50a1yi.JPG" alt="add and drop Post routes"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;addUser and dropUser&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6idb0a99w04l83922qp6.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6idb0a99w04l83922qp6.JPG" alt="add and drop User routes"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These routes will be helpful since they automatically provide the logic to add a post to the user's &lt;em&gt;liked&lt;/em&gt; array and a user to a post's &lt;em&gt;liked_by&lt;/em&gt; array (we can change the name of the route methods later if we want).&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;
&lt;h3&gt;
  
  
  Adding Mongo Connection String
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Step 7)&lt;/strong&gt; Go to the Settings page and add your MongoDB Connection string if you have it.&lt;br&gt;
You could also do this later, you'll just have to insert it in the &lt;em&gt;index.js&lt;/em&gt; page of your server before you can run your application.&lt;/p&gt;

&lt;p&gt;For help accessing your MongoDB Atlas Connection string, follow &lt;a href="https://docs.mongodb.com/guides/cloud/connectionstring/" rel="noopener noreferrer"&gt;this guide&lt;/a&gt;&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;
&lt;h3&gt;
  
  
  Saving Your Projects
&lt;/h3&gt;

&lt;p&gt;Neutrino lets you create an account to save your projects which may be helpful in debugging or adding new features as your project grows. However, this is fully optional.&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;
&lt;h3&gt;
  
  
  Export
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Step 8)&lt;/strong&gt; Click on the EXPORT button on the sidebar and add a project name and your email and you're done!&lt;br&gt;
If you followed along correctly, Neutrino should download a zip folder containing all of your project's code&lt;/p&gt;
&lt;h2&gt;
  
  
  You're Done (almost)!
&lt;/h2&gt;

&lt;p&gt; &lt;/p&gt;
&lt;h3&gt;
  
  
  Running Your Code
&lt;/h3&gt;

&lt;p&gt;Extract the zip folder and open it up in your editor. Run the following commands in this order.&lt;br&gt;
&lt;code&gt;cd server&lt;/code&gt;&lt;br&gt;
&lt;code&gt;npm i&lt;/code&gt;&lt;br&gt;
&lt;code&gt;node index.js&lt;/code&gt;&lt;br&gt;
&lt;strong&gt;note:&lt;/strong&gt; If you haven't added a Mongo Connection String, you'll get the following error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;throw new MongoParseError('Invalid scheme, expected connection string to start with "mongodb://" or "mongodb+srv://"');
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On a new terminal, run:&lt;br&gt;
&lt;code&gt;cd client&lt;/code&gt;&lt;br&gt;
&lt;code&gt;npm i&lt;/code&gt;&lt;br&gt;
&lt;code&gt;npm run start&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;If everything went correctly, you should see the following page:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fifftycb7ks06e0zpcqtf.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fifftycb7ks06e0zpcqtf.JPG" alt="welcome page"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nothing quite too interesting yet, but you can see that you can register a new user then log in with the specified username and password.&lt;br&gt;
You can also try to create a new post and comment (if you try to pass in anything other than your own user's id for the &lt;em&gt;user&lt;/em&gt; parameter when creating a new comment you should get an error).&lt;/p&gt;

&lt;p&gt;However, the whole frontend is pretty generic and we'll get around to fixing it. Let's fix up a couple of things in the backend first though.&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;
&lt;h3&gt;
  
  
  Model Files
&lt;/h3&gt;

&lt;p&gt; &lt;/p&gt;
&lt;h4&gt;
  
  
  User Model
&lt;/h4&gt;
&lt;h5&gt;
  
  
  /server/models/User.js
&lt;/h5&gt;

&lt;p&gt;Your code should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const mongoose = require('mongoose');

const UserSchema = new mongoose.Schema({
    username: {
        type: String,
        required: true
    },
    email: {
        type: String,
        required: true
    },
    password: {
        type: String,
        required: true
    },
    name: {
        type: String,
        required: true
    },
    bio: {
        type: String,
        required: true
    },
    likes: [
        {
            type: mongoose.Schema.Types.ObjectId,
            ref: 'Post'
        }
    ]
})

UserSchema.virtual('posts', {
        ref: 'Post',
        localField: '_id',
        foreignField: 'user'
});

UserSchema.set('toObject', { virtuals: true });
UserSchema.set('toJSON', { virtuals: true });

const User = mongoose.model('User', UserSchema);
module.exports = User;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each object in the schema represents a parameter for the object, &lt;em&gt;likes&lt;/em&gt; represents the Many-to-Many association we created with Posts, which is simply an array of Object IDs for different posts.&lt;/p&gt;

&lt;p&gt;The latter code in &lt;em&gt;UserSchema.virtual&lt;/em&gt; specifies our One-to-Many relationship with Post. &lt;br&gt;
Mongoose virtuals allow us to fetch the posts associated with the given user without actually storing them in the User document in the database, which will help performance. &lt;/p&gt;

&lt;p&gt;You can read more about Mongoose virtuals &lt;a href="https://mongoosejs.com/docs/tutorials/virtuals.html" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;
&lt;h4&gt;
  
  
  Post Model
&lt;/h4&gt;
&lt;h5&gt;
  
  
  /server/models/Post.js
&lt;/h5&gt;

&lt;p&gt;Your code should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const mongoose = require('mongoose');

const PostSchema = new mongoose.Schema({
    title: {
        type: String,
        required: true
    },
    content: {
        type: String,
        required: true
    },
    user: {
        type: mongoose.Schema.Types.ObjectId,
        ref: 'User',
        required: true
    },
    liked_by: [
        {
            type: mongoose.Schema.Types.ObjectId,
            ref: 'User'
        }
    ]
})

PostSchema.virtual('comments', {
    ref: 'Comment',
    localField: '_id',
    foreignField: 'post'
});

PostSchema.set('toObject', { virtuals: true });
PostSchema.set('toJSON', { virtuals: true });

const Post = mongoose.model('Post', PostSchema);
module.exports = Post;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt; &lt;/p&gt;

&lt;h4&gt;
  
  
  User Controller
&lt;/h4&gt;

&lt;h5&gt;
  
  
  /server/controllers/UserController.js
&lt;/h5&gt;

&lt;p&gt;Neutrino sometimes messes up the route methods whenever you have different relationships between the same two models (remember how we had both a One-to-Many and a Many-to-Many between User and Post), so make sure your User Controller has these two methods:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;addPost: async (req, res) =&amp;gt; {
    const { user_id, post_id } = req.params;
    UserModel.findByIdAndUpdate(
      user_id, 
      { $push: { likes: post_id } },
      (err, data) =&amp;gt; {
        if (err) {
          res.status(500).send(err);
          console.log(err);
        } else {
          res.status(200).send(data);
          console.log('Post added!');
        }
      }
    )
  },

  dropPost: async (req, res) =&amp;gt; {
    const { user_id, post_id } = req.params;
    UserModel.findByIdAndUpdate(
      user_id, 
      { $pull: { likes: post_id } },
      (err, data) =&amp;gt; {
        if (err) {
          res.status(500).send(err);
          console.log(err);
        } else {
          res.status(200).send(data);
          console.log('Post dropped!');
        }
      }
    )
  },
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's also fix up the &lt;em&gt;.populate()&lt;/em&gt; function in &lt;strong&gt;find()&lt;/strong&gt; as Neutrino may have written a slight bug.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;First, we need to populate posts since Mongoose virtuals only gives us the ids of the posts belonging to the given user. &lt;br&gt;
The &lt;em&gt;populate&lt;/em&gt; function replaces this id with an object containing the actual posts' information, specifically the parameters defined in &lt;em&gt;select&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We also need to populate &lt;em&gt;likes&lt;/em&gt; with the objects corresponding to actual post data&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can read more about Mongoose's &lt;em&gt;populate&lt;/em&gt; function &lt;a href="https://mongoosejs.com/docs/populate.html" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Your &lt;em&gt;find&lt;/em&gt; function should look as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;find: async (req, res) =&amp;gt; {
    const { id } = req.params;
    try {
      const data = await UserModel.findById(id)
                .populate({ path: 'posts', select: 'title' })
        .populate({ path: 'likes', select: 'title content' })
            if (req.user.id != data._id.toString()) {
              data.password = undefined;
              data.email = undefined;
            } else {
              data.password = undefined;
            }
      res.status(200).send(data);
    } catch (err) {
      res.status(400).send(err.message);
      console.log(err);
    }
  },
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt; &lt;/p&gt;

&lt;h4&gt;
  
  
  Post Controller
&lt;/h4&gt;

&lt;h5&gt;
  
  
  /server/controllers/PostController.js
&lt;/h5&gt;

&lt;p&gt;Lets rename some variables in the addUser and dropUser methods.&lt;br&gt;
In &lt;em&gt;$push&lt;/em&gt; and &lt;em&gt;$pull&lt;/em&gt;, rename &lt;em&gt;users&lt;/em&gt; to &lt;em&gt;liked_by&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;addUser: async (req, res) =&amp;gt; {
    const { post_id, user_id } = req.params;
    PostModel.findByIdAndUpdate(
      post_id, 
      { $push: { liked_by: user_id } },
      (err, data) =&amp;gt; {
        if (err) {
          res.status(500).send(err);
          console.log(err);
        } else {
          res.status(200).send(data);
          console.log('User added!');
        }
      }
    )
  },

  dropUser: async (req, res) =&amp;gt; {
    const { post_id, user_id } = req.params;
    PostModel.findByIdAndUpdate(
      post_id, 
      { $pull: { liked_by: user_id } },
      (err, data) =&amp;gt; {
        if (err) {
          res.status(500).send(err);
          console.log(err);
        } else {
          res.status(200).send(data);
          console.log('User dropped!');
        }
      }
    )
  },
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;note:&lt;/strong&gt; Since we renamed the &lt;em&gt;users&lt;/em&gt; array to &lt;em&gt;liked_by&lt;/em&gt; in the Post model, we'll run into a few errors if we don't also change the naming in PostController.&lt;/p&gt;

&lt;p&gt;Make sure &lt;em&gt;find()&lt;/em&gt; and &lt;em&gt;index()&lt;/em&gt; look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;find: async (req, res) =&amp;gt; {
    const { id } = req.params;
    try {
      const data = await PostModel.findById(id)
                .populate({ path: 'comments', select: 'content user' })
                .populate({ path: 'liked_by', select: 'username name' })

      res.status(200).send(data);
    } catch (err) {
      res.status(400).send(err.message);
      console.log(err);
    }
  },

  all: async (req, res) =&amp;gt; {
    try {
      const data = await PostModel.find()
                .populate({ path: 'comments', select: 'content user' })
                .populate({ path: 'liked_by', select: 'username name' })

      res.status(200).send(data);
    } catch (err) {
      res.status(400).send(err.message);
      console.log(err);
    }
  }, 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  Server Index
&lt;/h3&gt;

&lt;p&gt;The server index page defines all of our RESTful routes and pointes them to the appropriate controller method.&lt;/p&gt;

&lt;p&gt;It also includes &lt;em&gt;verifyJWT&lt;/em&gt;, a middleware function that checks for a valid JWT token to ensure the user is authenticated.&lt;/p&gt;

&lt;p&gt;Including &lt;em&gt;verifyJWT&lt;/em&gt; in a route will require the user to be authenticated before calling the controller function.&lt;/p&gt;

&lt;h5&gt;
  
  
  /server/index.js
&lt;/h5&gt;

&lt;p&gt;Make sure to include &lt;em&gt;verifyJWT&lt;/em&gt; for the following routes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;addPost&lt;/li&gt;
&lt;li&gt;dropPost&lt;/li&gt;
&lt;li&gt;addUser&lt;/li&gt;
&lt;li&gt;dropUser&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your code should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const express = require('express');
const mongoose = require('mongoose');
const cors = require('cors');
const jwt = require("jsonwebtoken")

const app = express();
const PORT = 8080;
const corsOptions = {
  origin: "*"
}

app.use( express.json() );
app.use( cors(corsOptions) );

mongoose.connect('&amp;lt;YOUR OWN CONNECT STRING HERE&amp;gt;', {
    useNewUrlParser: true,
});


function verifyJWT(req, res, next) {
  if (!req.headers["authorization"]) {
    return res.status(400).json({ message:"No Token Given", isLoggedIn: false });
  }

  const token = req.headers["authorization"].split(' ')[1];
  if (token) {
    jwt.verify(token, "pleasechange", (err, decoded) =&amp;gt; {
      if (err) return res.status(500).json({ message: "Failure to Auth", isLoggedIn: false });
      req.user = {};
      req.user.id = decoded.id;
      req.user.username = decoded.username;
      next();
    })
  } else {
    return res.status(400).json({ message: "Incorrect Token Given", isLoggedIn: false });
  }
}


// CONTROLLERS
const UserController = require('./controllers/UserController');
const PostController = require('./controllers/PostController');
const CommentController = require('./controllers/CommentController');


// ROUTES
app.get('/users', verifyJWT, UserController.all);
app.get('/users/:id', verifyJWT, UserController.find);
app.post('/users', UserController.register);
app.put('/users/:id/edit', verifyJWT, UserController.update);
app.delete('/users/:id', verifyJWT, UserController.delete);
app.post('/users/:user_id/add-post/:post_id', verifyJWT, UserController.addPost);
app.post('/users/:user_id/drop-post/:post_id', verifyJWT, UserController.dropPost);

app.get('/posts', PostController.all);
app.get('/posts/:id', PostController.find);
app.post('/posts', verifyJWT, PostController.create);
app.put('/posts/:id/edit', verifyJWT, PostController.update);
app.delete('/posts/:id', verifyJWT, PostController.delete);
app.post('/posts/:post_id/add-user/:user_id', verifyJWT, PostController.addUser);
app.post('/posts/:post_id/drop-user/:user_id', verifyJWT, PostController.dropUser);

app.post('/comments', verifyJWT, CommentController.create);
app.delete('/comments/:id', verifyJWT, CommentController.delete);

// AUTH
app.post('/login', UserController.login);
app.post('/register', UserController.register);

app.listen(
    PORT,
    console.log("Server running on port 8080...")
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  Fixing Up the Front End
&lt;/h3&gt;

&lt;p&gt;Each Model comes with 4 pages built in corresponding to each of the CRUD functions&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;[ModelA]s.js&lt;/strong&gt; : an index page containing a list of all [ModelA]s created&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;[ModelA]Show.js&lt;/strong&gt; : a page displaying all information corresponding to a single [ModelA]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;[ModelA]Edit.js&lt;/strong&gt; : a page rendering a form to update a specific [ModelA]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;[ModelA]New.js&lt;/strong&gt; : a page rendering a form to create a new [ModelA]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h4&gt;
  
  
  Display User Page
&lt;/h4&gt;

&lt;h5&gt;
  
  
  /client/src/Pages/User/UserShow
&lt;/h5&gt;

&lt;p&gt;UserShow.js renders a pretty generic page, lets change a few things to make it look more like a profile page.&lt;/p&gt;

&lt;h5&gt;
  
  
  Displaying Params
&lt;/h5&gt;

&lt;p&gt;You can change the header to greet the user with their username rather than id, also, since we added logic for hiding the user's &lt;em&gt;email&lt;/em&gt; and &lt;em&gt;password&lt;/em&gt;, you can delete the &lt;em&gt;password&lt;/em&gt; parameter and add a conditional to only render &lt;em&gt;email&lt;/em&gt; if its not null.&lt;/p&gt;

&lt;h5&gt;
  
  
  Conditional Rendering
&lt;/h5&gt;

&lt;p&gt;As for the &lt;em&gt;EDIT&lt;/em&gt; and &lt;em&gt;DELETE&lt;/em&gt; buttons, we only want to display them if the currently authenticated user is the same user we're displaying. &lt;/p&gt;

&lt;p&gt;To do so, first import &lt;em&gt;useContext&lt;/em&gt; from react and include the following lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { UserContext } from '../../hooks/UserContext';

...
export default function UserShow(props) {
  const { authUser } = useContext(UserContext);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we can access the signed in user if it exists by simply calling &lt;em&gt;authUser&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Wrap both buttons with the following conditional:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{ authUser._id === id &amp;amp;&amp;amp; 
          &amp;lt;div&amp;gt;
            &amp;lt;Button variant="outlined" style={{marginRight: 15}}
              onClick={() =&amp;gt; navigate(`/users/${id}/edit`)}&amp;gt;edit
            &amp;lt;/Button&amp;gt;
            &amp;lt;Button variant="contained" color="error" 
              onClick={handleDelete}&amp;gt;delete
            &amp;lt;/Button&amp;gt;
          &amp;lt;/div&amp;gt;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  Displaying Liked Posts
&lt;/h5&gt;

&lt;p&gt;We can display liked posts by simply calling the &lt;em&gt;user.likes&lt;/em&gt; array.&lt;/p&gt;

&lt;p&gt;It might look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div className='displayContainer'&amp;gt;
    &amp;lt;h3&amp;gt;Liked Posts&amp;lt;/h3&amp;gt;
    &amp;lt;ul&amp;gt;
    {user.likes &amp;amp;&amp;amp; user.likes.map((post, i) =&amp;gt; (
        &amp;lt;div className='listItem' key={i}&amp;gt;
            &amp;lt;li&amp;gt;{post.title}&amp;lt;/li&amp;gt;
            &amp;lt;Button variant='outlined' size='small'
        onClick={() =&amp;gt; navigate(`/posts/${post._id}`)}&amp;gt;show&amp;lt;/Button&amp;gt;
        &amp;lt;/div&amp;gt;
    ))}
    &amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt; &lt;/p&gt;

&lt;h4&gt;
  
  
  Display Post Page
&lt;/h4&gt;

&lt;h5&gt;
  
  
  /client/src/Pages/Post/PostShow
&lt;/h5&gt;

&lt;p&gt;Again, this page is currently pretty generic, but we can fix it up a bit by changing the header and how we display some of the params.&lt;/p&gt;

&lt;p&gt;What's a bit more interesting though is how we're dealing with likes.&lt;/p&gt;

&lt;h5&gt;
  
  
  Liking Posts
&lt;/h5&gt;

&lt;p&gt;Change the &lt;em&gt;addUser&lt;/em&gt; and &lt;em&gt;dropUser&lt;/em&gt; functions to the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function likePost() {
    try {
      axios.post(`http://localhost:8080/posts/${id}/add-user/${authUser &amp;amp;&amp;amp; authUser._id}`,
                {}, { headers: authHeader() });
      axios.post(`http://localhost:8080/users/${authUser &amp;amp;&amp;amp; authUser._id}/add-post/${id}`,
                {}, { headers: authHeader() });
    } catch (e) {
      console.log(e);
    };
    window.location.reload();
  }

  function unlikePost(droppedId) {
    try {
      axios.post(`http://localhost:8080/posts/${id}/drop-user/${authUser &amp;amp;&amp;amp; authUser._id}`,
                {}, { headers: authHeader() });
      axios.post(`http://localhost:8080/users/${authUser &amp;amp;&amp;amp; authUser._id}/drop-post/${id}`,
                {}, { headers: authHeader() });
    } catch (e) {
      console.log(e);
    };
    window.location.reload();
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All we're doing is changing the name of the function for readability and changing the user id to the id of the currently authenticated user (This will require you to import &lt;em&gt;useContext&lt;/em&gt; &lt;em&gt;UserContext&lt;/em&gt; define &lt;em&gt;authUser&lt;/em&gt; like we did in UserShow).&lt;/p&gt;

&lt;h5&gt;
  
  
  Conditional Rendering
&lt;/h5&gt;

&lt;p&gt;If we only want to display the edit and delete buttons if the post belongs to the authenticated user, wrap the buttons in the following conditional:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{ post.user === authUser._id &amp;amp;&amp;amp;
          &amp;lt;div&amp;gt;
            &amp;lt;Button variant="outlined" style={{marginRight: 15}}
              onClick={() =&amp;gt; navigate(`/posts/${id}/edit`)}&amp;gt;edit
            &amp;lt;/Button&amp;gt;
            &amp;lt;Button variant="contained" color="error" 
              onClick={handleDelete}&amp;gt;delete
            &amp;lt;/Button&amp;gt;
          &amp;lt;/div&amp;gt;
          }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  Displaying Like/Unlike Button
&lt;/h5&gt;

&lt;p&gt;This button will render depending on whether or not the currently authenticated user has already liked the post.&lt;/p&gt;

&lt;p&gt;Therefore, we can create two new buttons for liking and unliking and wrap them in the following ternary opertor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{ (post.liked_by &amp;amp;&amp;amp; post.liked_by.some(user =&amp;gt; user._id === authUser._id)) ?
          &amp;lt;Button variant="contained" color="error" 
            onClick={unlikePost}&amp;gt;unlike
          &amp;lt;/Button&amp;gt;
          :
          &amp;lt;Button variant="contained" color="success" 
            onClick={likePost}&amp;gt;like
          &amp;lt;/Button&amp;gt;
        }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lets understand what this is doing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;post.liked_by is the array of users that have liked this post&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;.some((user) =&amp;gt; condition)&lt;/em&gt; returns true if any user matches the following condition

&lt;ul&gt;
&lt;li&gt;In this case, we want to return true if the currently authenticated user has liked the post, that is, if &lt;em&gt;authUser&lt;/em&gt; is included in the posts &lt;em&gt;liked_by&lt;/em&gt; array&lt;/li&gt;
&lt;li&gt;If true, we want to display the &lt;em&gt;unlike&lt;/em&gt; button, otherwise, display the &lt;em&gt;like&lt;/em&gt; button&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  Finishing Thoughts
&lt;/h2&gt;

&lt;p&gt;Okay there's a chance after reading everything and making the slight changes this project took a little over 30 minutes.&lt;br&gt;
But really, we had the bulk of our functionality up and running in only a couple of minutes due to Neutrino.&lt;/p&gt;

&lt;p&gt;There's obviously a lot more that can be done fixing up the frontend and customizing it to look more like an actual blog app, but hopefully after following these examples with &lt;em&gt;UserShow&lt;/em&gt; and &lt;em&gt;PostShow&lt;/em&gt;, you gathered enough on your own to get started with the rest.&lt;/p&gt;

&lt;p&gt;Happy Coding!&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>tooling</category>
      <category>react</category>
      <category>mongodb</category>
    </item>
  </channel>
</rss>
