Building a full stack app is no small endeavor, and deploying it comes with its own host of things to consider.

I'm a tabletop game developer, and recently deployed a simple roleplaying game tracker that uses the M-E-V-N stack (you can follow my tutorial to create your own app here).  

In deploying the app, I came across three key takeaways that may be useful as you begin considering the best way to bring your projects from development to production.

You can check out the code to my app on GitHub, and I should mention that it includes Chad Carteret's very cool CSS statblock in prettifying what's otherwise very basic HTML.

If you're thinking of following the same path to deployment as I have here, be sure to check out the official documentation on Heroku, the Vue CLI, and this tutorial by Nick Manning.

You'll also want to take a look at Will Abramson's article on a similar topic.

On to deployment!

Your front end and back end can be deployed together or separately, depending on your app's complexity.

One snag that seems to appear immediately when considering production is the structural question of how to deploy the front and back ends of your app.

Should the client (or static files) live in the same place as the server and database? Or should they be separate, with the front end making HTTP requests from elsewhere to the back end using CORS?

The answer is yes! Or no. Maybe??

For better or worse, there's no one-size-fits-all solution to this question, as it will likely depend on your app's architecture and complexity. In the roleplaying game tracker that I linked to above, I have the whole stack running on a single Heroku dyno, with the following folder structure:

Folder-Structure

All of the front and back end files live in the same place, with the Vue client built for production in a folder located at /client/dist.

In server.js, along with a bunch of database and routing code, there's a little line that says:

server.use(serveStatic(__dirname + "/client/dist"));

In Express, this tells the app to serve my static client files from a particular folder, and enables me to run the front and back ends all within the same environment. If you're deploying a similarly simple app, this type of solution might work for you as well.  

Conversely, and depending on your project's complexity, you may have to separate the front and back ends and treat them as separate applications, which is no big deal. In the app above, my client is making calls to static API endpoints that are handled by the server, like this:

getQuests: function () {
    axios
        .get('https://mevn-rpg-app.herokuapp.com/quests')
        .then(response => (this.questData = response.data))                   
 }

Technically, my client could be making those requests from anywhere - even a static GitHub Pages site. This type of solution can help separate your app into two distinct entities to tackle, which is sometimes better than trying to cram the whole project into one location.

One note: if you're going to be making cross-origin HTTP requests - that is, requests from a client that lives on a separate domain from the API or server - you'll need to become familiar with CORS.  You can read more about it in this article.  

Your code will need to change to support a production environment.

When you're knee deep in the development process, it can be easy to lose track of how much of your code depends on local files or other data.

Consider the following in a locally-running server.js:

server.listen(3000, () => console.log("Server started!"));

On a local machine, the code just asks the server to listen on port 3000 and log to the console that we're ready for liftoff.

In a production environment, the server has no concept of where the "localhost" should be, or to whose port 3000 it should be listening. With this example, you'd have to change your code to something like:

const port = process.env.PORT || 3000;

server.listen(port, () => console.log("Server started!"));

The above instructs the server to instead listen at port 3000 of the process that's currently running, wherever that might be (check out this article for further reading on this topic).

Similarly, in my app, I have several modules that need to be imported by one another to function:

Folder-Structure-Expanded

In /routes/Quests.js, for example, I have a router that tells the server what to do when receiving API requests to interact with quest-related items in the database. The router needs to import a Mongoose schema from /models/quest.js to function properly. If the application were running locally, we could just say:

const Quest = require('../models/quest');

Pretty simple! Yet, unfortunately, our server won't know where to find the root directory of our project once deployed. In Express, we'd change our code to something like:

const path = require('path');
const Quest = require(path.join(__dirname, '../models/quest'));

Your particular case might be different, depending on your language and framework(s), but you'll need to get specific about what your code looks like in a production environment rather than in your local development environment.

Additionally, you're probably already familiar with whatever bundler you're using for your front end (e.g., webpack), and will want to build your client for production to optimize it for deployment.

You have a multitude of deployment platforms from which to choose.

If you've deployed a front end website or some other type of static app, you might be familiar with just pushing your files to some remote repository and calling it a day.

Deploying a full stack app (or even just a back end) is eminently more complex. You'll need a dedicated server, or something that emulates one, to respond to the HTTP requests that it will be receiving and work with an online database.

There are a number of services that will do this very thing for you, and the spectrum ranges based on price, scalability, complexity, and other factors.

There's a bunch of articles out there that compare PaaS options for deployment, but here are some thoughts as you consider platforms for your first project:

  • Heroku: If you have a small project like mine or just want to learn about deployment, a good first step could be Heroku.
  • AWS, Docker, and Kubernetes: If you're seeking a career in full stack web development or DevOps, now's a good time to familiarize yourself with Amazon Web Services and/or container platforms like Docker and Kubernetes.
  • Azure: If you're a C# or .NET developer, Azure appears to be a seamless way to deploy your apps without having to leave the safety of the Microsoft ecosystem.

There are, of course, several other options out there, and your particular use-case scenario might depend on pricing or the specific feature sets that are on offer.

Additionally, you'll want to consider any addons that will be necessary to replicate your app's functionality in a production environment. My roleplaying game tracker, for example, uses MongoDB, but the production version certainly can't use the little database on my local machine! Instead, I've used the mLab Heroku addon to get the live site up and running with the same functionality as in my development environment.

Your app's success, as well as your own progress as a full stack web developer, depend on your ability to consider deployment options and create a successful pipeline for production. With a little research, I'm certain that you can find the best solution that fits all of your app's needs.

Happy coding!

If you enjoyed this article, please consider checking out my games and books, subscribing to my YouTube channel, or joining the Entromancy Discord.

M. S. Farzan, Ph.D. has written and worked for high-profile video game companies and editorial websites such as Electronic Arts, Perfect World Entertainment, Modus Games, and MMORPG.com, and has served as the Community Manager for games like Dungeons & Dragons Neverwinter and Mass Effect: Andromeda. He is the Creative Director and Lead Game Designer of Entromancy: A Cyberpunk Fantasy RPG and author of The Nightpath Trilogy. Find M. S. Farzan on Twitter @sominator.