KeystoneJS (Node.js CMS & Web Application Platform)
KeystoneJS (Node.js CMS & Web Application Platform)
Introduction of KeystoneJS
KeystoneJS is a generic content management framework, meaning that it can be used for developing a variety of web applications using Javascript. It is especially suitable for developing large-scale applications such as portals, forums, content management systems (CMS), e-commerce projects and RESTful Web services because of its modular architecture and clean separation of various functionality. KeystoneJS is a powerful Node.js content management system and web app framework built on express and mongoose. Keystone makes it easy to create sophisticated websites and apps and comes with a beautiful auto-generated Admin UI.
Why use KeystoneJS?
KeystoneJS provides a standardized set of components that allow for the fast and easy development of web applications that can be quickly developed, maintained and extended. KeystoneJS has a number of key features that make it worth using including:
- Modularity – Keystone will configure express – the de facto web server for node.js – for you and connect to your MongoDB database using Mongoose, which is the leading ODM package.
- Auto-generated Admin UI – Whether you use it while you’re building out your application, or in production as a database content management system, Keystone’s Admin UI will save you time and make managing your data easy.
- Session Management – Keystone comes ready out of the box with session management and authentication features, including automatic encryption for password fields.
- Email Sending – Keystone makes it easy to set up, preview and send template-based emails for your application. It also integrates with Mandrill.
- Form Processing – Want to validate a form, upload an image, and update your database with a single line? Keystone can do that, based on the data models you’ve already defined.
- Database Fields – IDs, Strings, Booleans, Dates, and Numbers are the building blocks of your database. Keystone builds on these with useful, real-world fields types like name, email, password, address, image and relationship fields.
Install the generator
npm install -g generator-keystone
Create a folder for your project
mkdir my-test-project
cd my-test-project
Run the new project generator
yo keystone
Run it in your command line like this:
node keystone
Project Structure
|--lib | Custom libraries and other code |--models | Your application's database models |--public | Static files (css, js, images, etc.) that are publicly available |--routes | |--api | | Your application's api controllers | |--views | | Your application's view controllers | |--index.js | | Initialises your application's routes and views | |--middleware.js | | Custom middleware for your routes |--templates | |--includes | | Common .jade includes go in here | |--layouts | | Base .jade layouts go in here | |--mixins | | Common .jade mixins go in here | |--views | | Your application's view templates |--updates | Data population and migration scripts |--package.json | Project configuration for npm |--web.js | Main script that starts your application
routes/views
and templates/views
directories as much as possible.Models
Before you can start your Keystone app, you’ll need some data models. We’re going to start with the User model, which is special – we need it so that Keystone can do authentication and session management. Create the following two files in the /models
folder models/index.js
require('./users.js');
models/users.js
User
model. It doesn’t need to export anything, but the model must be registered with Keystone.var keystone = require('keystone'),
Types = keystone.Field.Types;
var User = new keystone.List('User');
User.add({
name: { type: Types.Name, required: true, index: true },
email: { type: Types.Email, initial: true, required: true, index: true },
password: { type: Types.Password, initial: true },
canAccessKeystone: { type: Boolean, initial: true }
});
User.register();
Authentication and Session Management
For Keystone to provide authentication and session management to your application, it needs to know a few things (which we’ve now configured).
- The option
user model
must be the name of the Model that Keystone should look in to find your users. If you use a different model name, be sure to set the option correctly. - If you want your application to support session management, set the
session
option to true. Loading sessions incurs a small overhead, so if your application doesn’t need sessions you can turn this off. - Keystone has built-in views for signing in and out. To enable them, set the
auth
option to true. You can also implement custom signin and signout screens in your applications’ views. - Sessions are persisted using an encrypted cookie storing the user’s ID. Make sure you set the
cookie secret
option to a long, random string. - The user model must have a
canAccessKeystone
property (which can be a virtual method or a stored boolean) that says whether a user can access Keystone’s Admin UI or not. *Note* If you choose to use a virtual method setting the value in mongodb directly will not authenticate correctly. A virtual method is useful when the criteria for access is more complex. See Mongoose virtuals
Routes
Our first view controller is going to be very simple – just rendering a template. Create an routes/views/index.js
file like this: routes/views/index.js
var keystone = require('keystone'); exports = module.exports = function(req, res) { var view = new keystone.View(req, res); view.render('index'); }
Templates
Now, for the template our route will render. The render
method looks in he views
directory specified in our web.js
, which we set to /templates/view
. First up, create templates/views/index.jade
: templates/views/index.jade
extends ../layouts/base block content h1 Hello World
Common Route Middleware
routes/middleware.js
file keeps your route index nice and clean. If your middleware file gets too big, it’s a good idea to restructure any significant functionality into custom modules in your projects /lib
folder.Middleware functions
req
– an express request objectres
– an express response objectnext
– the method to call when the middleware has finished running (including any internal callbacks)
Flash message support (no, not that flash)
info
success
warning
error
You can easily support other types of messages by updating your middleware and the .jade template that renders them (which we’ll get to below). To use flash messages in your route controllers, do this: req.flash('info', 'Some information!');
Public Assets
You’ll want to add your own css, javascript, images and other files to your project. In the examples above, we’re including /styles/site.min.css
and /js/lib/jquery-1.10.2.min.js
.
Keystone will serve any static assets you place in the public
directory. This path is specified in web.js
by the static
option.
It will also automatically generate .css
or compressed .min.css
files when a corresponding .less
file is found in the public
folder, as specified in web.js
by the less
option. For more information on LESS, see lesscss.org.
Production vs. Development
Keystone applies different settings in production and development modes. The environment will default to development, so you should set the NODE_ENV environment variable to production on your production servers for better performance. Your app can detect which environment it is running in by calling keystone.get(‘env’). When you deploy your KeystoneJS app to production, be sure to set your ENV environment variable to production. You can do this by setting NODE_ENV=production in your .env file, which gets handled by dotenv.
Creating an admin user
/updates/0.0.1-admins.js
with the following credentials:- email – user@keystonejs.com
- password – admin
The KeystoneJS administration site
Start up our app using the node keystone.js
command and open http://127.0.0.1:3000/keystone/signin in your browser. You should see the administration login page shown below.
Dynamically adding columns to Admin UI
One of the most useful features for looking at data in the admin interface is the ability to dynamically add columns that we are interested in without having to modify the definition of the model in code. The columns dropdown on the top right lists all the fields defined in our model. Columns that have been listed in the default columns option will appear with tick marks next to them. We can then pick any additional columns that we are interested in working with.
If a custom field was chosen, it will be added to the end of the displayed column headers. We can reset the list of displayed columns to the original state by using the ‘Reset to default’ option that will appear in the dropdown once one or more columns have been selected.
Disabling the Admin UI
Services
Google Analytics
ga property String |
Your Google Analytics Property
Will default to
process.env.GA_PROPERTY |
ga domain String |
Your Google Analytics Domain
Will default to
process.env.GA_DOMAIN |
Google Maps
keystone.set('google api key', 'your-browser-key');
keystone.set('google server api key', 'your-server-key');
keystone.set('default region', 'au'); // optional, will limit autocomplete results to Australia
Amazon S3
s3 config
option to an object containing your configuration (see example below). Alternatively, set the S3_BUCKET
,S3_KEY
and S3_SECRET
environment variable.keystone.set('s3 config', { bucket: 'my-bucket', key: 'my-key', secret: 'my-secret' });
Windows Azure Storage
azurefile config
option to an object containing your configuration.keystone.set('azurefile config', { account: 'my storage account', key: 'secret api key' });
Cloudinary
cloudinary secure
option to true
.keystone.set('cloudinary config', { cloud_name: 'my-cloud', api_key: 'abc', api_secret: '123' }); // or keystone.set('cloudinary config', 'cloudinary://api_key:api_secret@cloud_name' ); // optional, will prefix all built-in tags with 'keystone_' keystone.set('cloudinary prefix', 'keystone'); // optional, will prefix each image public_id with [{prefix}]/{list.path}/{field.path}/ keystone.set('cloudinary folders', true); // optional, will force cloudinary to serve images over https keystone.set('cloudinary secure', true);
Embed.ly
embedly api key
option.EMBEDLY_API_KEY
environment variable if it is set.keystone.set('embedly api key', 'your-key');
Mandrill
mandrill api key
and mandrill username
options. These options will be default to the MANDRILL_API_KEY & MANDRILL_USERNAME
environment variable’s if set.keystone.set('mandrill api key', 'your-key');
keystone.set('mandrill username', 'your-username');