Application Development with Rapid Application Development Framework AllcountJS

View all articles

The idea of Rapid Application Development (RAD) was born as a response to traditional waterfall development models. Many variations of RAD exist; for example, Agile development and the Rational Unified Process. However, all such models have one thing in common: they aim to yield maximum business value with minimal development time through prototyping and iterative development. To accomplish this, the Rapid Application Development model relies on tools that ease the process. In this article, we shall explore one such tool, and how it can be used to focus on business value and optimization of the development process.

AllcountJS is an emerging open source framework built with rapid application development in mind. It is based on the idea of declarative application development using JSON-like configuration code that describes the structure and behavior of the application. The framework has been built on top of Node.js, Express, MongoDB and relies heavily on AngularJS and Twitter Bootstrap. Although it is reliant on declarative patterns, the framework still allows further customization through direct access to API where needed.

AllcountJS as your RAD Framework

Why AllcountJS as Your RAD Framework?

According to Wikipedia, there are at least one hundred tools that promise rapid application development, but this raises the question: how rapid is “rapid.” Do these tools allow a particular data-centric application to be developed in a few hours? Or, perhaps, it is “rapid” if the application can be developed in a few days or a few weeks. Some of these tools even claim that a few minutes is all it takes to produce a working application. However, it is unlikely that you could build a useful application in under five minutes and still claim to have satisfied every business need. AllcountJS doesn’t claim to be such a tool; what AllcountJS offers is a way to prototype an idea in a short period of time.

With AllcountJS framework, it is possible to build an application with a themeable auto-generated user interface, user management features, RESTful API and a handful of other features with minimal effort and time. It is possible to use AllcountJS for a wide variety of use cases, but it best suits applications where you have different collections of objects with different views for them. Typically, business applications are a good fit for this model.

AllcountJS has been used to build allcountjs.com, plus a project tracker for it. It is worth noting that allcountjs.com is a customized AllcountJS application, and that AllcountJS allows both static and dynamic views to be combined with little hassle. It even allows dynamically loaded parts to be inserted into static content. For example, AllcountJS manages a collection of demo application templates. There is a demo widget on the main page of allcountjs.com that loads a random application template from that collection. A handful of other sample applications are available in the gallery at allcountjs.com.

Getting Started

To demonstrate some of the capabilities of RAD framework AllcountJS, we will create a simple application for Toptal, which we will call Toptal Community. If you follow our blog you may already know that a similar application was built using Hoodie as part of one of our earlier blog posts. This application will allow community members to sign up, create events and apply to attend them.

In order to set up the environment, you should install Node.js, MongoDB and Git. Then, install AllcountJS CLI by invoking an “npm install” command and perform project init:

npm install -g allcountjs-cli
allcountjs init toptal-community-allcount
cd toptal-community-allcount
npm install

AllcountJS CLI will ask you to enter some info about your project in order to pre-fill package.json.

AllcountJS can be used as standalone server or as a dependency. In our first example we aren’t going to extend AllcountJS, so a standalone server should just work for us.

Inside this newly created app-config directory, we will replace contents of main.js JavaScript file with the following snippet of code:

A.app({
  appName: "Toptal Community",
  onlyAuthenticated: true,
  allowSignUp: true,
  appIcon: "rocket",
  menuItems: [{
    name: "Events",
    entityTypeId: "Event",
    icon: "calendar"
  }, {
    name: "My Events",
    entityTypeId: "MyEvent",
    icon: "calendar"
  }],
  entities: function(Fields) {
    return {
      Event: {
        title: "Events",
        fields: {
          eventName: Fields.text("Event").required(),
          date: Fields.date("Date").required(),
          time: Fields.text("Starts at").masked("99:99").required(),
          appliedUsers: Fields.relation("Applied users", "AppliedUser", "event")
        },
        referenceName: "eventName",
        sorting: [['date', -1], ['time', -1]],
        actions: [{
          id: "apply",
          name: "Apply",
          actionTarget: 'single-item',
          perform: function (User, Actions, Crud) {
            return Crud.actionContextCrud().readEntity(Actions.selectedEntityId()).then(function (eventToApply) {
              var userEventCrud = Crud.crudForEntityType('UserEvent');
              return userEventCrud.find({filtering: {"user": User.id, "event": eventToApply.id}}).then(function (events) {
                if (events.length) {
                  return Actions.modalResult("Can't apply to event", "You've already applied to this event");
                } else {
                  return userEventCrud.createEntity({
                    user: {id: User.id},
                    event: {id: eventToApply.id},
                    date: eventToApply.date,
                    time: eventToApply.time
                  }).then(function () { return Actions.navigateToEntityTypeResult("MyEvent") });
                }
              });
            })
          }
        }]
      },
      UserEvent: {
        fields: {
          user: Fields.fixedReference("User", "OnlyNameUser").required(),
          event: Fields.fixedReference("Event", "Event").required(),
          date: Fields.date("Date").required(),
          time: Fields.text("Starts at").masked("99:99").required()
        },
        filtering: function (User) { return {"user.id": User.id} },
        sorting: [['date', -1], ['time', -1]],
        views: {
          MyEvent: {
            title: "My Events",
            showInGrid: ['event', 'date', 'time'],
            permissions: {
              write: [],
              delete: null
            }
          },
          AppliedUser: {
            permissions: {
	          write: []
            },
            showInGrid: ['user']
          }
        }
      },
      User: {
        views: {
          OnlyNameUser: {
            permissions: {
              read: null,
              write: ['admin']
            }
          },
          fields: {
            username: Fields.text("User name")
          }
        }
      }
    }
  }
});

Although AllcountJS works with Git repositories, for sake of simplicity we will not use it in this tutorial. To run the Toptal Community application, all we have to do is invoke AllcountJS CLI run command in the toptal-community-allcount directory.

allcountjs run

It is worth noting that MongoDB should be running when this command is executed. If all goes well, the application should be up and running at http://localhost:9080.

To login please use the username “admin” and password “admin”.

Less Than 100 Lines

You may have noticed that the application defined in main.js took only 91 lines of code. These lines include the declaration of all the behaviors that you may observe when you navigate to http://localhost:9080. So, what exactly is happening, under the hood? Let us take a closer look at each aspect of the application, and see how the code relates to them.

Sign in & Sign up

The first page you see after opening the application is a sign in. This doubles as a sign up page, assuming that the checkbox - labelled “Sign Up” - is checked before submitting the form.

Sign in & Sign up

This page is shown because the main.js file declares that only authenticated users may use this application. Moreover, it enables the ability for users to sign up from this page. The following two lines are all that was necessary for this:

A.app({
  ...,
  onlyAuthenticated: true,
  allowSignUp: true,
  ...
})

Welcome Page

After signing in, you’ll be redirected to a welcome page with an application menu. This portion of the application is generated automatically, based on the menu items defined under the “menuItems” key.

welcome page example

Along with a couple of other relevant configurations, the menu is defined in the main.js file as follows:

A.app({
  ...,
  appName: "Toptal Community",
  appIcon: "rocket",
  menuItems: [{
    name: "Events",
    entityTypeId: "Event",
    icon: "calendar"
  }, {
    name: "My Events",
    entityTypeId: "MyEvent",
    icon: "calendar"
  }],
  ...
});

AllcountJS uses Font Awesome icons, so all icon names referenced in the configuration are mapped to Font Awesome icon names.

Browsing & Editing Events

After clicking on “Events” from the menu, you’ll be taken to the Events view shown in the screenshot below. It is a standard AllcountJS view that provides some generic CRUD functionalities on the corresponding entities. Here, you may search for events, create new events and edit or delete existing ones. There are two modes of this CRUD interface: list and form. This portion of the application is configured through the following few lines of JavaScript code.

A.app({
  ...,
  entities: function(Fields) {
    return {
      Event: {
        title: "Events",
        fields: {
          eventName: Fields.text("Event").required(),
          date: Fields.date("Date").required(),
          time: Fields.text("Starts at").masked("99:99").required(),
          appliedUsers: Fields.relation("Applied users", "AppliedUser", "event")
        },
        referenceName: "eventName",
        sorting: [['date', -1], ['time', -1]],
        ...
      }
    }
  }
});

This example shows how entity descriptions are configured in AllcountJS. Notice how we are using a function to define the entities; every property of AllcountJS configuration can be a function. These functions can request dependencies to be resolved through its argument names. Before the function is called, appropriate dependencies are injected. Here, “Fields” is one of the AllcountJS configuration APIs used to describe entity fields. The property “Entities” contains name-value pairs where the name is an entity-type identifier and value is its description. An entity-type for events is described, in this example, where the title is “Events.” Other configurations, such as default-sort-ordering, reference name, and the like, may also be defined here. Default-sort-order is defined through an array of field names and directions, while the reference name is defined through a string (read more here).

allcountJS function

This particular entity-type has been defined as having four fields: “eventName,” “date,” “time” and “appliedUsers,” the first three of which are persisted in the database. These fields are mandatory, as indicated by the use of “required().” Values in these fields with such rules are validated before the form is submitted on the front-end as shown in the screenshot below. AllcountJS combines both client-side and server-side validations to provide the best user experience. The fourth field is a relationship that bears a list of users who have applied to attend the event. Naturally, this field is not persisted in the database, and is populated by selecting only those AppliedUser entities relevant to the event.

allcountjs development rules

Applying to Attend Events

When a user selects a particular event, the toolbar shows a button labelled “Apply.” Clicking on it adds the event to the user’s schedule. In AllcountJS, actions similar to this can be configured by simply declaring them in the configuration:

actions: [{
  id: "apply",
  name: "Apply",
  actionTarget: 'single-item',
  perform: function (User, Actions, Crud) {
    return Crud.actionContextCrud().readEntity(Actions.selectedEntityId()).then(function (eventToApply) {
      var userEventCrud = Crud.crudForEntityType('UserEvent');
      return userEventCrud.find({filtering: {"user": User.id, "event": eventToApply.id}}).then(function (events) {
        if (events.length) {
          return Actions.modalResult("Can't apply to event", "You've already applied to this event");
        } else {
          return userEventCrud.createEntity({
            user: {id: User.id},
            event: {id: eventToApply.id},
            date: eventToApply.date,
            time: eventToApply.time
          }).then(function () { return Actions.navigateToEntityTypeResult("MyEvent") });
        }
      });
    })
  }
}]

The property “actions” of any entity type takes an array of objects that describe the behavior of each custom action. Each object has an “id” property which defines a unique identifier for the action, the property “name” defines the display name and the property “actionTarget” is used to define the action context. Setting “actionTarget” to “single-item” indicates that the action should be performed with a particular event. A function defined under the property “perform” is the logic executed when this action is performed, typically when the user clicks on the corresponding button.

Dependencies may be requested by this function. For instance, in this example the function depends on “User,” “Actions” and “Crud.” When an action occurs, a reference to the user, invoking this action, can be obtained by requiring the “User” dependency. The “Crud” dependency, which allows the manipulation of database state for these entities, is also requested here. The two methods that return an instance of Crud object are: The method “actionContextCrud()” - returns CRUD for “Event” entity-type since the action “Apply” belongs to it, while the method “crudForEntityType()” - returns CRUD for any entity type identified by its type ID.

CRUD dependencies

The implementation of the action begins by checking if this event is already scheduled for the user, and if not, it creates one. If it is already scheduled, a dialog box is shown by returning the value from calling “Actions.modalResult()”. Besides showing a modal, an action may perform different types of operations in a similar way, such as “navigate to view,” “refresh view,” “show dialog,” and so on.

implementation of the action

User Schedule of Applied Events

After successfully applying to an event, the browser is redirected to the “My Events” view, which shows a list of events the user has applied to. The view is defined by the following configuration:

UserEvent: {
	fields: {
		user: Fields.fixedReference("User", "OnlyNameUser").required(),
		event: Fields.fixedReference("Event", "Event").required(),
		date: Fields.date("Date").required(),
		time: Fields.text("Starts at").masked("99:99").required()
	},
	filtering: function (User) { return {"user.id": User.id} },
	sorting: [['date', -1], ['time', -1]],
	views: {
		MyEvent: {
		title: "My Events",
		showInGrid: ['event', 'date', 'time'],
		permissions: {
			write: [],
			delete: null
		}
		},
		AppliedUser: {
		permissions: {
			write: []
		},
		showInGrid: ['user']
		}
	}
},

In this case, we are using a new configuration property, “filtering.” As with our earlier example, this function also relies on the “User” dependency. If the function returns an object, it is treated as a MongoDB query; the query filters the collection for events that belong only to the current user.

Another interesting property is “Views.” “View” is a regular entity-type, but it’s MongoDB collection is the same as for parent entity-type. This makes it possible to create visually different views for the same data in the database. In fact, we used this feature to create two different views for “UserEvent:” “MyEvent” and “AppliedUser.” Since the prototype of the sub-views is set to the parent entity type, properties that are not overridden are “inherited” from the parent type.

views

Listing Event Attendees

After applying to an event, other users may see a list of all the users planning to attend. This is generated as a result of the following configuration elements in main.js:

AppliedUser: {
    permissions: {
        write: []
    },
    showInGrid: ['user']
}
// ...
appliedUsers: Fields.relation("Applied users", "AppliedUser", "event")

“AppliedUser” is a read-only view for a “MyEvent” entity-type. This read-only permission is enforced by setting an empty array to the “Write” property of the permissions object. Also, as the “Read” permission isn’t defined, by default, reading is permitted for all users.

applieduser for myevent type

Extending Default Implementations

The typical backdraw of RAD frameworks is the lack of flexibility. Once you’ve built your app and you need to customize it, you may encounter significant obstacles. AllcountJS is developed with extensibility in mind and allows replacement of every building block inside.

To achieve that AllcountJS uses it’s own Dependency Injection (DI) implementation. DI allows the developer to override the default behaviors of the framework through extension points, and, at the same time, allows it through the reuse of existing implementations. Many aspects of RAD framework extension are described in the documentations. In this section, we will explore how we may extend two of the many components in the framework, server side logic and views.

Continuing with our Toptal Community example, let us integrate an external data source to aggregate event data. Let’s imagine that there are Toptal Blog posts discussing plans for events the day before each event. With Node.js, it should be be possible to parse the blog’s RSS feed and extract such data. In order to do this, we will need some extra npm dependencies, such as “request,” “xml2js” (to load Toptal Blog RSS feed), “q” (to implement promises) and “moment” (to parse dates). These dependencies can be installed by invoking the following set of commands:

npm install xml2js
npm install request
npm install q
npm install moment

Let’s create another JavaScript file, name it “toptal-community.js” in the toptal-community-allcount directory and populate it with the following:

var request = require('request');
var Q = require('q');
var xml2js = require('xml2js');
var moment = require('moment');
var injection = require('allcountjs');
injection.bindFactory('port', 9080);
injection.bindFactory('dbUrl', 'mongodb://localhost:27017/toptal-community');
injection.bindFactory('gitRepoUrl', 'app-config');

injection.bindFactory('DiscussionEventsImport', function (Crud) {
    return {
        importEvents: function () {
            return Q.nfcall(request, "https://www.toptal.com/blog.rss").then(function (responseAndBody) {
                var body = responseAndBody[1];
                return Q.nfcall(xml2js.parseString, body).then (function (feed) {
                    var events = feed.rss.channel[0].item.map(function (item) { return {
                        eventName: "Discussion of " + item.title, 
                        date: moment(item.pubDate, "DD MMM YYYY").add(1, 'day').toDate(), 
                        time: "12:00"
                    }});
                    var crud = Crud.crudForEntityType('Event');
                    return Q.all(events.map(function (event) {
                        return crud.find({query: {eventName: event.eventName}}).then(function (createdEvent) {
                            if (!createdEvent[0]) {
                                return crud.createEntity(event);
                            }
                        });
                    } ));
                });
            })
        }
    };
});

var server = injection.inject('allcountServerStartup');
server.startup(function (errors) {
    if (errors) {
        throw new Error(errors.join('\n'));
    }
});

In this file, we are defining a dependency called “DiscussionEventsImport,” which we can use in our main.js file by adding an import action on the “Event” entity-type.

{
    id: "import-blog-events",
    name: "Import Blog Events",
    actionTarget: "all-items",
    perform: function (DiscussionEventsImport, Actions) {
        return DiscussionEventsImport.importEvents().then(function () { return Actions.refreshResult() });
    }
}

Since it is important to restart the server after making some changes to the JavaScript files, you may kill the previous instance and start it again by executing the same command as before:

node toptal-community.js

If all goes right, you will see something like the screenshot below after running the “Import Blog Events” action.

Import Blog Events action

So far so good, but let us not stop here. Default views work, but they can be boring at times. Let us customize them a little.

Do you like cards? Everyone likes cards! To make a card view, put the following in a file named events.jade inside the app-config directory:

extends main
include mixins

block vars
    - var hasToolbar = true
block content
    .refresh-form-controller(ng-app='allcount', ng-controller='EntityViewController')
        +defaultToolbar()
        .container.screen-container(ng-cloak)
            +defaultList()
                .row: .col-lg-4.col-md-6.col-xs-12(ng-repeat="item in items") 
                    .panel.panel-default
                        .panel-heading
                            h3 {{item.date | date}} {{item.time}}
                            div
                                button.btn.btn-default.btn-xs(ng-if="!isInEditMode", lc-tooltip="View", ng-click="navigate(item.id)"): i.glyphicon.glyphicon-chevron-right
                                |  
                                button.btn.btn-danger.btn-xs(ng-if="isInEditMode", lc-tooltip="Delete", ng-click="deleteEntity(item)"): i.glyphicon.glyphicon-trash
                        .panel-body
                            h3 {{item.eventName}}
            +noEntries()
            +defaultEditAndCreateForms()

block js
    +entityJs()

After that, simply reference it from the “Event” entity in main.js as “customView: “events”.” Run your app and you should see a card-based interface instead of the default tabular one.

Event entity in main.js

Conclusion

Nowadays, the development flow of web applications is similar across many web technologies, where some operations are repeated over and over again. Is it really worth it? Maybe, it is time to rethink the way your web applications are developed?

AllcountJS provides an alternative approach to rapid application development frameworks; you start by creating a skeleton for the application by defining entity descriptions, and then add views and behavior customizations around it. As you can see, with AllcountJS, we created a simple, yet fully functional application, in less than a hundred lines of code. Maybe, it doesn’t meet all production requirements, but it is customizable. All of this makes AllcountJS a good tool for rapidly bootstrapping web applications.

About the author

Pavel Tiunov, Russia
member since February 22, 2014
Pavel is a team leader and developer who has designed and developed many OLTP and OLAP systems for state authorities of the Russian Federation. [click to continue...]
Hiring? Meet the Top 10 Freelance Node.js Developers for Hire in December 2016

Comments

axeo
Looks a bit like LightSwitch. Their web site is not working. :)
Fernando Canizo
https://allcountjs.com/ says "502 Bad Gateway" ... Beautiful
plintus
Cool story, comrade! http://hilton.org.uk/blog/javascript-frameworks-days.png
Valentino Aluigi
My experience with very declarative frameworks (see: lots of configuration) has never been amazing. Take Ext JS, Sencha Touch, or even Grunt. Often a steep learning curve, and the troubles begin when you have to do something different from the standard - but still play well with the internal architecture of the framework. For me it's also a matter of personal coding style: I do prefer writing code than configuring existing components, and I do prefer crafting a bespoke application tailored on the user needs. But in the end, I agree that there are always very valid use cases for these frameworks: usually when they are not part of the core business.
Pavel Tiunov
Valentino thank you for sharing your experience! Agree with you when winning in development speed usually losing in flexibility. AllcountJS is initially designed to overcome such problems by allowing customize almost every part of it using DI. It's where code writing comes into play to create application that satisfies user needs.
Gavin Engel
This seems similar to dcl.js, although I think allcount.js seems easier to use. What other node.js frameworks are similar to allcount.js? By the way, I think it RAD syntax that is further shrunk down to look something similar to SCSS would be great.
beerfan
This sort of rad tools prove good for building mock ups . It's good for designer who want to build configuration based pages . I don't think this will be useful in building enterprise apps .
Pavel Tiunov
Hello beerfan! Thanks for your feedback! But why do you think so? Please share your opinion and/or experience.
Orlando Landaverde B
Hi Pavel, can i put entities on more than one file: for example: teachers.js ( define here fields, actions, sort, filters) and another for students.js ( define here fields, actions, sort, filters) .. etc.. Thanks..
Pavel Tiunov
Hello Orlando! Yes, you can. Just create multiple *.js files in app config directory. Each JS file should have own `A.app()` call.
garduino
Hi Pavel: I'm trying to follow this interesting tutorial but can't start the app, I get: Failed to fetch "toptal-community-repo". Trying to use as regular directory. Application server for "toptal-community-repo" listening on port 9080 /Users/Shared/gsa/Dev/CodeRepository/toptal-community-allcount/node_modules/allcountjs/node_modules/mongoose/node_modules/mongodb/lib/mongodb/connection/base.js:246 throw message; ^ TypeError: Cannot read property 'length' of undefined Any hint to solve this issue will be appreciated. Thanks.
garduino
My first attempt was in a Mac OSX, now I tried in a Windows box with no luck. Even when the login screen get displayed, after type us/pw I get the same error (pic attached).
Pavel Tiunov
Hello garduino! Could you please share your stack trace or full console log?
garduino
Sure! In the first attempt in each machine it started and I get the error after typing us/pw. But in the next attempts the app refuses to start with the following errors: node_modules/.bin/allcountjs --git toptal-community-repo --db mongodb://localhost:27017/toptal-community ############################################################## # # !!! MONGOOSE WARNING !!! # # This is an UNSTABLE release of Mongoose. # Unstable releases are available for preview/testing only. # DO NOT run this in production. # ############################################################## Failed to fetch "toptal-community-repo". Trying to use as regular directory. Application server for "toptal-community-repo" listening on port 9080 /Users/Shared/gsa/Dev/CodeRepository/toptal-community-allcount/node_modules/allcountjs/node_modules/mongoose/node_modules/mongodb/lib/mongodb/connection/base.js:246 throw message; ^ TypeError: Cannot read property 'length' of undefined at processResults (/Users/Shared/gsa/Dev/CodeRepository/toptal-community-allcount/node_modules/allcountjs/node_modules/mongoose/node_modules/mongodb/lib/mongodb/db.js:1581:31) at /Users/Shared/gsa/Dev/CodeRepository/toptal-community-allcount/node_modules/allcountjs/node_modules/mongoose/node_modules/mongodb/lib/mongodb/db.js:1619:20 at /Users/Shared/gsa/Dev/CodeRepository/toptal-community-allcount/node_modules/allcountjs/node_modules/mongoose/node_modules/mongodb/lib/mongodb/db.js:1157:7 at /Users/Shared/gsa/Dev/CodeRepository/toptal-community-allcount/node_modules/allcountjs/node_modules/mongoose/node_modules/mongodb/lib/mongodb/db.js:1890:9 at Server.Base._callHandler (/Users/Shared/gsa/Dev/CodeRepository/toptal-community-allcount/node_modules/allcountjs/node_modules/mongoose/node_modules/mongodb/lib/mongodb/connection/base.js:448:41) at /Users/Shared/gsa/Dev/CodeRepository/toptal-community-allcount/node_modules/allcountjs/node_modules/mongoose/node_modules/mongodb/lib/mongodb/connection/server.js:481:18 at MongoReply.parseBody (/Users/Shared/gsa/Dev/CodeRepository/toptal-community-allcount/node_modules/allcountjs/node_modules/mongoose/node_modules/mongodb/lib/mongodb/responses/mongo_reply.js:68:5) at null.<anonymous> (/Users/Shared/gsa/Dev/CodeRepository/toptal-community-allcount/node_modules/allcountjs/node_modules/mongoose/node_modules/mongodb/lib/mongodb/connection/server.js:439:20) at emit (events.js:107:17) at null.<anonymous> (/Users/Shared/gsa/Dev/CodeRepository/toptal-community-allcount/node_modules/allcountjs/node_modules/mongoose/node_modules/mongodb/lib/mongodb/connection/connection_pool.js:201:13) Let me know if I should send any other data. Thanks.
Pavel Tiunov
Very interesting. Looks like it's some kind of that bug: https://github.com/Automattic/mongoose/issues/2684 What MongoDB version do you use and what mongoose version has been selected by npm while install?
garduino
The MongoDB version is: C:\MongoDB\Server\3.0\bin>mongod --version db version v3.0.3 git version: b40106b36eecd1b4407eb1ad1af6bc60593c6 OpenSSL version: OpenSSL 1.0.1m-fips 19 Mar 2015 but I do not know how to find the mongoose version installed by npm (I'm not familiar with Mongo, sorry) but I imagine that should be the latest.
garduino
Anyway I tried "npm install mongoose" and after the installation I get the same error trying to start the toptal sample application.
Pavel Tiunov
Yep. It's a mongoose 3.9 compatibility issue with MongoDB 3.0. I fixed version to minor instead of major (3.8) and it's fixed. Released 1.10.5. Please run `npm update` from your project dir (toptal-community-allcount) and try run application again. Thanks for that catch!
garduino
Excellent, it works now and I can continue the tutorial. Thanks you for the help1 Two more questions: 1. What you fixed to make it work? 2. What npm update does to help fix it? As you already imagined I'm not familiar with node-npm-etc, just learning. Thanks again.
Pavel Tiunov
1. Changed mongoose version constraint from "^3.8.21" to "~3.8.21" in package.json. It means that it could select only patch version after 21 and not a 9 minor where compatibility bug is. 2. npm update just updates installed dependencies where newer version could be loaded that matches version constraints.
garduino
Thanks you very much!
Pavel Tiunov
You're welcome!
David
This is fantastic!!!! I am getting an error with the import feature. C:\n\nodeprj\toptal-community-allcount\node_modules\allcountjs\node_modules\q\q. js:126 throw e; ^ Error: toptal-community.js:1 var request = require('request'); ^ ReferenceError: require is not defined at toptal-community.js:1:15 The app starts and works before I add the toptal-community.js file. The file to add the import feature. I am new to js frameworks. Can someone help?
David
I got this resolved with Pavel's help. See: https://groups.google.com/d/msg/allcountjs/9C5A2qrrdHQ/ZTzwMloBkIUJ and http://davidgleba.blogspot.ca/2015/07/trying-out-allcountjs-javascript-web.html
comments powered by Disqus
Subscribe
The #1 Blog for Engineers
Get the latest content first.
No spam. Just great engineering and design posts.
The #1 Blog for Engineers
Get the latest content first.
Thank you for subscribing!
You can edit your subscription preferences here.
Trending articles
Relevant technologies
About the author
Pavel Tiunov
Scala Developer
Pavel is a team leader and developer who has designed and developed many OLTP and OLAP systems for state authorities of the Russian Federation.