From No Stack to Some Stack (Relationships in MVC Architecture)

Shaw Kitajima
6 min readJan 2, 2020

My blog series is quite famous among my bootcamp peers, not because they actually read them, but because I try to force it down their throats. But during our discussions, I was told that it would be a good idea to make my next post about MVC architecture. So without further ado, let’s try to explain MVC relationships. Everything from this point on will be talked about using the Express Framework, so things will be a bit different if you are used to developing with Django, which uses the MVT architecture.

When I set up MVC architecture with Express, I set up files structures like this:

  • server.js

server.js is the brain where the user’s requests will go. This will also include a lot of configuration and middleware.

  • routes directory

user requests are sent as URIs, and server.js will ask the files in the routes directory for the corresponding URIs.

  • controllers directory

the routes files invoke the appropriate functions that are created in the controllers directory

  • models directory

the models configure the database that the controller functions will call

  • Views directory

the controller functions will ultimately render the views that the user will see

A Quick Journey of a User Request

Since the user only sees whatever files are in the views directory, they can interact with your application through HTTP requests that are processed by server.js. In that sense, the relationships above cycle from top to bottom. However, a controller is not guaranteed to render something to the database, and may redirect instead. In that case, the views get skipped, and the URI is processed by the server again, until it eventually invokes a function that renders something to the user from the views.

Now, when a user submits a request to server.js, it has three means of delivering data. It can put data in the URI, like:

https://league-stat-finder.herokuapp.com/summoners/5de972765ee10d0017724d01/matches

In this case, the bolded text is a value that gets placed in as a request parameter. Request parameters are handy for delivering database ids, which could be used to show a specific document in the database, or delete a record from the database.

The second way is by submitting request queries. These are the whatever follows the “?” in a URI, that are set with key value pairs “name=Shaw”.

The third way a user can deliver data is via request bodies. These are usually done by submitting forms. Why don’t we take a look at a real example of the journey that this form submission will take? Everything we are going to look at will be based on this Github project!

Form submissions add data to an object that server.js will interpret as request.body. Here is an example:

Whenever you create a way for a user to input data, whether it be through a “select” or “input” or anything else, you will want to give that tag a name property, and set it equal to whatever you want the dictionary keys in req.body to be. For example, if the user selected “SAN” as seen in line 14, req.body would have an airport property set to “SAN”. This is important because you are most likely going to be submitting this information to a database, and having matching property names will minimize errors on submission.

If we take a peek at the first form tag, you will see that it has an action property and method property. This, along with the form data, is what is going to get sent to server.js.

Now, server.js is going to see the URI as “/flights”, and will execute the code it has here:

It will see that ‘/flights’ corresponds to line 11, and will then look for whatever the flights file in the routes directory shown in line 4 exports. You would be right to be confused if you were directed to any of the regular ‘/’s. If those were set, server.js will ask each of the corresponding files in the routes directory for the right path.

Now it’s the routes directory’s turn at bat. Here is what it will do!

the router files will rely on both the URI path, and the method that was provided in our form. In our case, it was a ‘post’ method that got sent as just regular old ‘/’ (the routes just append whatever server.js had, so the URI is actually /flights/). The route then goes ahead and invokes the callback function which the controller provides.

So let’s take a look at what the controller function wants to do:

The big thing to remember here is that Express router callback functions (like our create function), take request and response, req and res for short, arguments. These objects are what have the req.params and req.body properties, along with the res.render and res.redirect methods that will either send a user the view, or redirect a URI back to server.js

Because req.body is a regular old Javascript object, you can add properties to it or edit them like in line 8. But once our req.body is sorted out, it is ready to submit that information to the database in line 9.

So let’s take a quick look at the models so we can see what is happening, and then go back to the controller

You should see that the properties defined in the userSchema are matched by the “name” properties we had in the form at the top of this journey. By having these properties match, the database knows exactly what to create.

Now back to the controller function:

On line 10, the controller is ready to save the newly created flight model. In this application, our models are all made using a MongoDB object modeling tool called Mongoose. Mongoose models have methods that always (I think) require a callback function, where you should write the rest of the code that gets executed after Mongoose finishes whatever you did (this is because Node.js operates asynchronously). In our case, we are ready to redirect the user if we had no problems.

We should now be going back to server.js to find the /flights URI with the “get” method. Server.js will then look inside the routes directory for the correct path, and method, which will in turn invoke the controller callback. It should get us to this function:

The index function that we are using will query the database for all flight models. The Mongoose callback function will provide an err object and flights array that contains all of the flights it found. The controller can now render the index.ejs file that it has in the views directory, and pass in the flights list for it to use.

The request is now ready to reach its final destination. Here is what the user will see.

This article will not detail how to use the templating engine, EJS, that is being used. But for now, we can just say that EJS allows you to make wild HTML using squids. Squids are the <% characters, and if they are proceeded by a “-”, it means to perform something that is interpreted, but not shown as HTML. If it is proceed by a “=”, it means to display whatever it holds as HTML.

Common Mistakes and Some Tips

This now wraps up a simple example of the journey a user request can take, but I think it would be a good idea to close out the article with some tips and common mistakes that a lot of beginner developers will face.

  • Route parameters are a double edged sword because of this issue:
/users/:id
/users/new //this will never get executed

Express will use the first route that matches. In this case, /users/:id is telling Express to accept anything that comes after ‘/users/’. This could be ‘/users/2ero9u09220398ddglkjsefr’ or ‘/users/new’. The solution to this problem is to put parameters after defined routes like:

/users/new
/users/:id
  • EJS let’s you set up paths with <%=<flight._id>%>, and controller redirects can use template literals like `/flights/${req.params._id}`
  • Synchronous code will not wait for the asynchronous baton unless you force it to. Make sure that you put code that relies on asynchronous operations in the callback functions of asynchronous operations! Alternatively, you can use promises or async awaits.
  • Foreign API consumption will usually return responses in JSON format. Most programming languages will need to parse that information in a way that they can interact with them as their own objects. So if you are using Javascript, make sure you do something like:
const obj = JSON.parse(response.body)

Conclusion

As usual, thank you very much for taking the time to read this article. I am very excited in finally writing code that actually relates to full stack development. Please let me know what you thought about everything, and hit me up if you are in San Diego and want to talk about developing! I’m always happy to hear additional insights.

Humble plug of my Github here

--

--