Cosmic JS Blog Stay tuned for community news, company announcements and updates from the Cosmic JS team.

How to Build a Node.js User Management App


by Tony Spiro on November 22, 2016


In this tutorial I’m going to show you how to build a user management app using Node.js and the Cosmic JS CMS API.

TL;DR

View the full source code on GitHub
View the Demo

The app will include the following pages:

  • Login
  • Signup
  • Users list (logged in user access only)

Getting Started

Create a file titled app-server.js file and add the following:

// app-server.js
import express from 'express'
import hogan from 'hogan-express'
import http_module from 'http'
import bodyParser from 'body-parser'
import compression from 'compression'
import session from 'express-session'
import config from './config'
import cors from 'cors'
const app = express()
app.use(cors({credentials: true, origin: true}))
app.use(bodyParser.json())
app.use(compression())
app.engine('html', hogan)
app.set('views', __dirname + '/views')
app.set('port', process.env.PORT || 3000)
app.use(express.static(__dirname + '/public'))
app.set('trust proxy', 1) // trust first proxy
app.use(session({
  secret: 'keyboard cat',
  resave: false,
  saveUninitialized: true
}))
app.use((req, res, next) => {
  if (req.url === '/favicon.ico')
    return res.end()
  // Set global variables
  res.locals.year = new Date().getFullYear()
  // Set dev
  if (process.env.NODE_ENV === 'development')
    res.locals.is_dev = true
  next()
})
const partials = {
  header: 'partials/header',
  footer: 'partials/footer'
}
require('./routes')(app, config, partials)
const http = http_module.Server(app)
http.listen(app.get('port'), () => {
  console.info('==> 🌎  Go to http://localhost:%s', app.get('port'));
})

This is the entry point of our app and serves to initialize everything we need for our user management app.  We’re using Express for the Node.js framework, Hogan Express for view rendering from Mustache templates.  Express Session is used to initialize and save sessions.

We are going to have a couple partials available in our templates, header and footer as all of our pages will share these.

Know Your Routes

Next let’s take a look at index.js in our routes folder.

// Routes
module.exports = (app, config, partials) => {
  require('./home')(app, config, partials)
  require('./signup')(app, config, partials)
  require('./users')(app, config, partials)
  require('./auth')(app, config, partials)
  require('./logout')(app, config, partials)
  require('./404')(app, config, partials)
}

Here we have all of our routes.

Logging In

Let’s take a look at home.js which is the login page for our app:

// home.js
import Cosmic from 'cosmicjs'
module.exports = (app, config, partials) => {
  app.get('/', (req, res) => {
    Cosmic.getObjects({ bucket: { slug: config.COSMIC_BUCKET } }, (err, response) => {
      res.locals.cosmic = response
      if (req.query.message === 'unauthorized')
        res.locals.unauthorized_message = true
      return res.render('index.html', {
        partials
      })
    })
  })
}

There is not much going on here, just loading our dynamic content from Cosmic JS, as well as the HTML login form.

Looking at the auth.js file, you’ll see this is where the login form will send the data to log the user in.

// auth.js
import Cosmic from 'cosmicjs'
import async from 'async'
import _ from 'lodash'
import bcrypt from 'bcrypt'
const saltRounds = 10
module.exports = (app, config, partials) => {
  // Submit form
  app.post('/auth', (req, res) => {
    const data = req.body
    Cosmic.getObjectType({ bucket: { slug: config.COSMIC_BUCKET } }, { type_slug: 'users' }, (err, response) => {
      if (err)
        res.status(500).json({ status: 'error', data: response })
      else {
        async.eachSeries(response.objects.all, (user, eachCallback) => {
          if (!_.find(user.metafields, { key: 'email', value: data.email.trim().toLowerCase() }))
            return eachCallback()
          const stored_password = _.find(user.metafields, { key: 'password' }).value
          bcrypt.compare(data.password, stored_password, function(err, correct) {
            if(correct)
              res.locals.user_found = user
            eachCallback()
          })
        }, () => {
          if (res.locals.user_found) {
            req.session.user = {
              first_name: res.locals.user_found.metafield.first_name.value,
              last_name: res.locals.user_found.metafield.last_name.value,
              email: res.locals.user_found.metafield.email.value
            }
            req.session.save()
            return res.json({ status: 'success', data: response })
          }
          return res.status(404).json({ status: 'error', message: 'This user was not found or the email and password are incorrect.' })
        })
      }
    })
  })
}

When the data is sent to /auth the Cosmic NPM module searches for the email first then, if found tries to match the submitted password to the password saved in the Cosmic JS API (using bcrypt).  If both email and password are matched with a user, the session is created and the user is redirected from the login page to the users page.  Let’s take a look at the users list page.

Access Exclusive Content

After the user has logged in successfully, they are redirected to the users list page.  This is how it looks:

app.get('/users', (req, res) => {
  if(!req.session.user)
    return res.redirect('/?message=unauthorized')
  res.locals.user = req.session.user
  async.series([
    callback => {
      Cosmic.getObjectType({ bucket: { slug: config.COSMIC_BUCKET } }, { type_slug: 'users' }, (err, response) => {
        res.locals.users = response.objects.all
        callback()
      })
    },
    callback => {
      Cosmic.getObjects({ bucket: { slug: config.COSMIC_BUCKET } }, (err, response) => {
        res.locals.cosmic = response
        return res.render('users.html', {
          partials
        })
      })
    }
  ])
})

If the user session is not set, they are immediately redirected to the login page with an unauthorized message. If they have a user session, they will be able to see a list of all registered users (from the Users Object Type). This gives you the idea of how to limit user access in our app. By checking for req.session.user on any page, you can restrict access to logged in users only.

Signing Up

Let’s take a look at what happens for new users when they post their info to /users:

app.post('/users', (req, res) => {
  const data = req.body
  async.series([
    callback => {
      let user_found = false
      Cosmic.getObjectType({ bucket: { slug: config.COSMIC_BUCKET } }, { type_slug: 'users' }, (err, response) => {
        _.forEach(response.objects.all, user => {
          if (_.find(user.metafields, { key: 'email', value: data.email.trim() }))
            user_found = true
        })
        if (!user_found)
          return callback()
        // User found
        return res.status(409).json({ status: 'error', message: 'Email already in use' })
      })
    },
    callback => {
      bcrypt.hash(data.password, saltRounds, function(err, hash) {
        res.locals.hash = hash
        callback()
      })
    },
    callback => {
      // Send to Cosmic
      const object = {
        type_slug: 'users',
        title: data.full_name,
        metafields: [
          {
            title: 'First name',
            key: 'first_name',
            type: 'text',
            value: data.first_name
          },
          {
            title: 'Last name',
            key: 'last_name',
            type: 'text',
            value: data.last_name
          },
          {
            title: 'Password',
            key: 'password',
            type: 'text',
            value: res.locals.hash
          },
          {
            title: 'Email',
            key: 'email',
            type: 'text',
            value: data.email.trim().toLowerCase()
          }
        ]
      }
      if (config.COSMIC_WRITE_KEY)
        object.write_key = config.COSMIC_WRITE_KEY
      Cosmic.addObject({ bucket: { slug: config.COSMIC_BUCKET } }, object, (err, response) => {
        if (err)
          res.status(500).json({ status: 'error', data: response })
        else
          res.json({ status: 'success', data: response })
        res.end()
      })
    }
  ])
})

There’s a few things happening here. First, the email is checked against the current list of users.  If a match is not found, the user object is created with each data point of the user: First Name, Last Name, Email and Password stored as metafields in the Cosmic JS API.

Taking this a step further, after you log into your Cosmic JS bucket and go to your Users Object Type you will be able to add more Metafields to the users in your system.  This makes adding dynamic, extended data very easy.  For example, let’s say you needed to add a profile image, address, GitHub username, etc.  All of these can be added as Metafields to the User Object.  See screenshot below:


See Ya Next Time

Now let’s see what happens when a user accesses the /logout route:

// logout.js
module.exports = (app, config, partials) => {
  app.get('/logout', (req, res) => {
    req.session.destroy()
    return res.redirect('/')
  })
}

The session is simply destroyed and the user is redirected back to the login page.

Where's the HTML?

I'm not going to go into too much detail on the HTML, but basically both login and signup forms have JQuery validation and submit AJAX requests to the route endpoints.  Check out the main.js file on GitHub to see how JQuery is used for this and other UI and UX things.

Conclusion

And that’s it.  Cosmic JS is a powerful platform for content creation and app data management.  With your Cosmic JS-powered User Management App you can now extend your user data using the Cosmic JS API or from your Cosmic JS Bucket Dashboard.

I hope you found this tutorial helpful.   If you have any questions or feedback you can reach out to us on Twitter or chat with us in the Slack community.

You may also like


Hello World!   We are officially in private beta mode.  Lots of work has been done up until this point to make Cosmic JS the best cloud-hosted content platform.  Sign up for an account and please send us some feedback.  Here is a copy of my blog post on the release:

Cosmic JS is a cloud-hosted content platform that makes it easy to manage content for websites and applications.

I built Cosmic JS because I saw a problem with scaling content across platforms and devices.  After years of building applications using installed content management systems, I became tired of rebuilding APIs to handle content distribution from a website to microsites, landing pages and applications.  I decided to build one API to handle all content outlets for easier and faster distribution, and Cosmic JS was born.

Cosmic JS allows for easy content management and distribution by giving you the power to store content as JSON objects in an API that can be delivered anywhere.


Cosmic JS is API-first, meaning your content can be distributed across all devices and platforms from your website, to microsites and landing pages, to native mobile applications which allows for faster development cycles.   And when you use Cosmic JS to serve data and files, application servers can be kept light-weight which saves on server costs.

Get started

Click here to sign up for a Cosmic JS account, it’s completely free to sign up.

Cosmic JS is currently in private beta but you will be notified when your account has been activated.

Start building

Click here to check out the available clients for JavaScript and PHP on GitHub.

Currently there are available clients for JavaScript and PHP to help you get started building scalable, content-ready applications.

View examples

Click here to view code for websites built using Cosmic JS.

I’ll post some tutorials on this blog soon to show you how easy it is to use Cosmic JS to manage content for your next project, so stay tuned.  If you have any questions, please post them below, or reach out to me on twitter or email me.

Thanks,
Tony Spiro

Click here to go to Cosmic JS
Click here to go to Cosmic JS on GitHub

In this installment of the Cosmic JS Developer Spotlight Series, we sat down with Leslie Cohn-Wein, a Front End Developer and Austin native now residing in Dallas, Texas. Leslie most recently worked as a Front End Engineer for Canvas United, a New York City-based digital agency, prior to starting as a Front End Developer at Netlify. Follow Leslie on Twitter or LinkedIn and enjoy the Q/A.

In this installment of the Cosmic JS Developer Spotlight Series, we sat down with John Leider, the founder of Vuetify who recently spoke at the Cosmic JS Vuetiful Holiday Meetup following his presentation at VueConf in Toronto. To see the slides from the Vuetiful Meetup Presentation "The Path to Vuetify 2.0", you can click here. Otherwise, follow John on Twitter, LinkedIn or GitHub, and enjoy the Q/A.

We are excited to introduce the all-new Cosmic JS content editor which is a huge improvement over our old editor.  We think you'll agree it makes adding API-powered content to your websites and apps easier and more enjoyable.  Here’s a list of some of the new functionality...

In our Developer Spotlight Series we shine a light on the creative process of a developer using Cosmic JS to create content-powered apps. This spotlight features Joe Tuson, a front-end web developer out of the UK.

Here I am again, writing late at night with more thoughts on Cosmic JS and what it could do for developers and content creators.  For some reason I keep thinking about Doogie Houser and that theme song...

So here's what I'm thinking: Everything is going the way of subscription services.  Music, movies, tv, enterprise software, you name it there is a company out there that will offer it to you without you having to download software, install an app, populate a database.  Everything is moving toward a service economy.

This is going to continue because the trend of human nature has always been toward better customer service, the less a customer has to do, the better the service.

With this in mind, comes the idea that your website's content doesn't need to be locked into a database.  It doesn't need to be something that has to be installed.  Your content can live freely as a service and can be accessed anywhere a device is needing to connect.  This means your website, your app, your business is allowed to be accessed anywhere and it's content is easily transported to wherever it needs to go.  This is huge.

APIs are now the DNA of the internet.  They connect everything from your favorite blogs to your tv to your facebook news feed to your twitter stream to everything you access on the web and any content in any smart device, period.  APIs are content.  

So here's the question: If APIs are our content platforms, why aren't we managing our content in the APIs?  Well now you can.

You can now manage the content of your website or app in Cosmic JS on a fast and secure content framework independently of any downloaded software or managed system.  

Welcome to the 21st century.  Your content is still king and it is now delivered everywhere...