Getting Started With Express, VueJS & PostgreSQL

11 Feb 2018

We have all heard of the MEAN (MongoDB Express Angular NodeJS) stack or more lately the MERN (MongoDB Express React and NodeJS) stack.

There are plenty of starter kits utilizing those stacks although I was looking for something similar but with a couple of changes. I wanted to switch out MongoDB with PostgresSQL because it’s a workhorse that can do just about anything and switch out React with VueJS because I find Vue much more approachable and beginner friendly.

I didn’t find anything like that out there, so I ended up creating one myself. Lets call it the PEVN (PostgreSQL Express VueJS NodeJS) stack, I know…0 for creativity!

I spent couple of hours getting everything working like how I wanted it to. I documented the process to save the trouble for anyone looking to do the same which you’ll find below.

Before we can get started, let’s make sure we have NodeJS installed. I’ve found the easiest way to do so is via nvm. Rails developers will find this very similar to rvm. To install, run the following commands which will install nvm and the latest version of NodeJS:

Now that we have a basic app, let’s get to the fun part. We will treat each Express view as it’s own VueJS app (MVVM pattern) which will grab the data it needs from the DOM and/or making AJAX requests to the server.

So for this example, given we have views/index.jade we will want to place it’s associated VueJS app and styles in client/css/index.css, client/js/index.js and /client/js/Index.vue such that when that Jade view is rendered, it will run the Index Vue app.

So we’ll have to tell our view in views/index.jade to load our packaged asset file:

importVuefrom'vue'importIndexfrom'./Index.vue'newVue({el:'#index',data:{visitors:[]},render(createElement){returncreateElement(Index)},beforeMount(){this.visitors=JSON.parse(this.$el.dataset.visitorsJson)//Grab this data from the DOM}})

Our Vue app lives in client/js/Index.vue:

<template><div><h1>Hello World</h1><p>Welcome to PostgreSQL, Express, VueJS, NodeJS starter</p><p>Here are the last 10 visitors:</p><table><thead><th>ID</th><th>IP</th><th>User Agent</th></thead><trv-for="(visitor, index) in visitors":key="index"><td></td><td></td><td></td></tr></table></div></template><script>exportdefault{data(){return{visitors:[]}},methods:{},created(){this.visitors=this.$parent.visitors;//Grab this data from the parent}}</script>

Don’t worry about the logic to display the list of visitors just yet. We’ll get to that in a minute.

Webpack

In order to create a packaged index.js asset file for our view, we will need to install Webpack, VueJS and its associated dependencies:

For the purpose of this example, we will just create a Visitors table which will log the user’s IP and UserAgent string every time you visit the home page and spit out the last 10 records:

varexpress=require('express');varmodels=require('../models');varrouter=express.Router();/* GET home page. */router.get('/',function(req,res,next){models.Visitor.create({user_agent:req.get('User-Agent'),ip:req.ip,}).then(()=>{models.Visitor.findAll({limit:10,order:[['createdAt','DESC']]}).then((visitors)=>{res.render('index',{title:'PEVN Stack!',visitors:visitors});})});});module.exports=router;

Conclusion

With that we come full circle and close the loop. If everything worked, you should now be able to run your app on port 4000 with:

$ npm start

One thing you might notice is that the app will require a restart everytime you change the server code which can get pretty annoying. We can switch to using nodemon instead so the app can restart automatically when the code changes:

$ npm install --save-dev nodemon

In our nodemon.json we can configure it to restart when it detects changes to our server side logic:

There are a few more interesting things we could do which I left out for this quick start. Like for example, we could run our NodeJS server logic through Babel so we can use ES6 syntax on the server as well. I’ll look forward to pull requests for those kind of enhancements from the community! :)