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

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.


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.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
  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
const partials = {
  header: 'partials/header',
  footer: 'partials/footer'
require('./routes')(app, config, partials)
const http = http_module.Server(app)
http.listen(app.get('port'), () => {'==> 🌎  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', {

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'/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: }))
            return eachCallback()
          const stored_password = _.find(user.metafields, { key: 'password' }).value
, stored_password, function(err, correct) {
              res.locals.user_found = user
        }, () => {
          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,
            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) => {
    return res.redirect('/?message=unauthorized')
  res.locals.user = req.session.user
    callback => {
      Cosmic.getObjectType({ bucket: { slug: config.COSMIC_BUCKET } }, { type_slug: 'users' }, (err, response) => {
        res.locals.users = response.objects.all
    callback => {
      Cosmic.getObjects({ bucket: { slug: config.COSMIC_BUCKET } }, (err, response) => {
        res.locals.cosmic = response
        return res.render('users.html', {

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:'/users', (req, res) => {
  const data = req.body
    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: }))
            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 => {
      // 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',
      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 })
          res.json({ status: 'success', data: response })

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) => {
    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.


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

A new Developer Hero joins us in our latest installment of the Cosmic JS Developer Spotlight Series. We sat down with Brian Mullis, a developer in Portland who  lead the charge on innovative app development for his interactive agency.

It's now faster and easier to make the content on your webpages dynamic.  The Official Cosmic JS JavaScript Client now comes with a minified browser version that allows you to add the power of Cosmic JS cloud-hosted content into your HTML page with a single file.  Download the Cosmic JS browser file then copy and paste this real-working browser example into an html file to check it out:

<!DOCTYPE html>
  <title>Cosmic JS Easy Browser Example</title>
<h1 id="title">If you see this, something isn't working...</h1>
<div id="content"></div>
<div id="metafields"></div>
<script src="cosmicjs.browser.min.js"></script>
var config = {
  bucket: {
    slug: 'easy-browser-example'
  object: {
    slug: 'home'
Cosmic.getObject(config, config.object, function(err, res) {
  var object = res.object;
  document.getElementById('title').innerHTML = object.title;
  document.getElementById('content').innerHTML = object.content;
  var metafields = object.metafields;
  var images = '';
    images += '<h2>' + metafield.title + '</h2>';
    images += '<img width="300" src="' + metafield.value + '"/>';
    images += '<br><br>';
  document.getElementById('metafields').innerHTML = images;

Sign in to your Cosmic JS account and connect this example to one of your buckets to see how easy it is to create powerful CMS-powered websites and app with Cosmic JS.

Introducing the Developer / Editor Toggle View to your Cosmic JS Dashboard. 

In this article I’m going to show you how to quickly and easily create a cross-platform blog that will connect your content across Web, iOS and Android.  With content powered but Cosmic JS, you will be able to edit your content once and deliver the update simultaneously across all devices and platforms.  This is powerful stuff.  Let’s get started.

In an effort to make the data modeling of Metafields even easier, you can now add Metafields at the top, bottom and inline between Metafields.

In this installment of the Cosmic JS Developer Spotlight Series, we sat down with Rick Hanlon, a Front End Engineer  and recent New Yorker that is now residing in London, England working on the front end team at Facebook. Rick is also on the core team of Jest, a JavaScript testing platform. Follow Rick on Twitter or LinkedIn and enjoy the Q/A.