You are looking at the old Apostrophe 0.5 documentation. It is deprecated for new projects. Check out the latest version!

With all of the options

← Previous: The minimum

Here's an app.js file with most of the options you'd want to include in a large apostrophe project.

var site = require('apostrophe-site')({
  // Allows apostrophe-sites to require stuff
  // on our behalf and also find our root folder
  root: module,

  // Used to name the local mongodb database,
  // if you don't pass a db option with more details
  shortName: 'mysite',

  // Hostname you plan to give your site
  hostName: '',

  // Title of your site. Used as a prefix to page titles and feed titles by default
  title: 'My Site',

  // This defaults to true and delivers HTML, CSS and JS much faster via
  // gzip transfer encoding. But you can set it to false if you must
  compress: true,

  // Apostrophe sizes your images to several awesome sizes right out of the box,
  // but we're greedy and we want something bigger than full (1280)
  addImageSizes: [
      name: 'max',
      width: 1600,
      height: 1280

  // By default the media library shows everyone's media until the user decides to
  // change that with the "uploaded by" filter. Want the default to go the other way?
  // Set the "owner" option as shown commented out below

  mediaLibrary: {
    // owner: 'user'

  // Set up email transport via nodemailer. By default sendmail is used because
  // it requires no configuration, but you may use any valid transport, see the
  // nodemailer module documentation.

  mailer: {
    transport: 'sendmail',
    transportOptions: {}

  // You can always log in at /login as admin, with this password

  redirectAfterLogin: function(user) {
    if (user.permissions.admin) {
      return '/awesomepeople';
    } else {
      return '/coolpeople';

  // Run some middleware on ALL requests. This happens AFTER
  // basics are in place like sessions and users and i18n.
  // Middleware functions may take an initial "site" argument
  // in addition to req, res, next
  middleware: [ /* middleware, functions, galore */ ],


  // Any options accepted by the apostrophe-pages module,
  // such as tabOptions and descendantOptions
  pages: {
    // List all the page types users should be able to add here, including
    // things like "Blog" and "Events" that are powered by modules, so you get
    // to pick the order
    types: [
      // TODO double check this doesn't get ignored if blog is added later and wasn't wanted
      { name: 'default', label: 'Default (Two Column)' },
      { name: 'home', label: 'Home Page' },
      { name: 'blog', label: 'Blog' },
      { name: 'events', label: 'Events' }

    // Load descendants of homepage and current page two levels deep
    // instead of one
    tabOptions: { depth: 2 },
    descendantOptions: { depth: 2 },

    // Do something special if the URL doesn't match anything else
    notfound: function(req, callback) {
      if (req.url === '/special') {
        req.redirect = '/specialer';
      return callback(null);

    // Run some middleware on the route that serves pages.
    // This is not global middleware, see the top-level middleware option
    // Middleware functions may take an initial "site" argument
    // in addition to req, res, next
    middleware: [ /* middleware, functions, galore */ ],

    // Custom page loader functions beyond those automatically
    // provided. Already you have the page with the slug 'global'
    // available at all times, the current page, its tabs, its
    // descendants, and anything loaded on behalf of your modules,
    // like blog posts appearing on the current page
    load: [
      function(req, callback) {
        if (!( && ( === 'fancy'))) {
          // Doesn't concern us
          return callback(null);
        // Set some custom data to be provided to the nunjucks template.
        // Anything in the extras object is pushed as data to the
        // page template.
        // We have a callback here, so we could go get anything
        req.extras.fanciness = true;
        return callback(null);

  // Let's add the blog and events modules. You must npm install them.
  // apostrophe-site will require them for you and pass your options
  modules: {
    'apostrophe-events': {
      widget: true
    'apostrophe-blog': {
      widget: true

  // Custom command line tasks. Run like this:
  // node app project:frobulate
  // argv is powered by optimist
  tasks: {
    project: {
      frobulate: function(site, apos, argv, callback) {
        console.log('Frobulated the hibblesnotz');
        console.log('You passed these arguments: ' + argv._);
        return callback(null);

  locals: {
    // Extra locals visible to every nunjucks template. Functions and
    // data are both fair game. You may also pass a function that takes
    // the site object as its sole argument and returns an object containing
    // the desired locals as properties.
    embiggen: function(s) {
      return s * 1000;

  assets: {
    // Loads site.js from public/js
    scripts: [
      // load this js file all the time, minify it normally
        // Load this JS file only when a user is logged in, never minify it.
        // 'when' could also be 'always'. 'minify' defaults to true
        name: 'fancy',
        when: 'user',
        minify: false
    // Loads site.less from public/css
    stylesheets: [

  // Last best chance to set custom Express routes
  setRoutes: function(callback) {'/wacky', function(req, res) { res.send('wackiness'); });
    return callback(null);

  // Just before apos.endAsset. Last chance to push any assets. Usually the
  // `assets` option above, and calling `pushAsset` from your modules,
  // is good enough.

  beforeEndAssets: function(callback) {
    // Apostrophe already loads these for logged-out users, but we
    // want them all the time in this project.
    site.apos.pushAsset('script', { name: 'vendor/blueimp-iframe-transport', when: 'always' });
    site.apos.pushAsset('script', { name: 'vendor/blueimp-fileupload', when: 'always' });
    return callback(null);

  // Just before listen. Last chance to set up anything
  afterInit: function(callback) {
    return callback(null);

  sanitizeHtml: {
    // Any options that can be passed to the sanitize-html
    // module are valid here. Used to adjust the way we filter
    // HTML saved in the rich text editor. You probably want
    // to stick with our standard set of allowed tags and
    // encourage users to respect your design rather than
    // fighting it

  // A simple way to alter the results of every call to apos.get, and thus
  // every page, snippet, blog post, etc. The retrieved documents will be
  // in results.pages. Be aware that this property does not always exist,
  // as apos.get is sometimes used just to fetch distinct tags or
  // other metadata.
  afterGet: function(req, results, callback) {
    // Do something, then invoke the callback

  // Out of the box, our video and embed widgets trust
  // quality services like wufoo,, youtube and
  // vimeo. Add additional domain names to this list
  // to allow oembed to be used with other services. Even
  // if you don't do this, a very basic embedded view based
  // on safe Open Graph tags (those used for Facebook sharing)
  // is always provided.
  oembedWhitelist: [

Next: Setting up Modules →