Meteor and Isomorphic Javascript

@peterpeerdeman

iso·mor·phic

"being of identical or similar form, shape, or structure"

iso·mor·phic

"(javascript) code that is used in both client and server application"

Hello Single Page Apps

  • no pagerefresh after interaction
  • separation of concerns
  • excellent cause for a meetup ;)
  • different teams / languages
  • logic duplication

meanwhile at lifely...

Angular + PHP -> Javascript

Meteor

  • Full stack Javascript app platform
  • "All inclusive" development environment
  • Reactive data model (ddp)
  • Quick kickstart web projects

quick meteor demo

  • (single codebase)
  • (data publication)
  • (data subscription)
  • (blaze / react rendering)

Isomorphic JS in Part-up

(one of our big production Meteor apps)

Shared "lib" package

                        
    ▾ partup:lib/
      ▸ collections/
      ▸ helpers/
      ▸ private/
      ▸ schemas/
      ▸ services/
      ▸ startup/
      ▸ transformers/
        error.js
        globals.js
        namespace.js
        package.js
        routes.js
        version.js
                        
                    

collections / model helpers

                        
/**
 @namespace Networks
 @name Networks
 */
Networks = new Mongo.Collection('networks', {
    transform: function(document) {
        return new Network(document);
    }
});

/**
 * Find featured networks
 *
 * @memberOf Networks
 * @param {String} language
 * @return {Mongo.Cursor}
 */
Networks.findFeatured = function(language) {
    var selector = {'featured.active': true};
    if (language) {
        selector.language = language;
    }
    return Networks.find(selector);
};
                        
                    

schemas definition

                        
/**
 * New message Form
 * @name inviteUpper
 * @memberof Partup.schemas.forms
 */
Partup.schemas.forms.inviteUpper = new SimpleSchema({
    name: {
        type: String
    },
    email: {
        type: String,
        max: 255,
        regEx: Partup.services.validators.email
    },
    message: {
        type: String,
        max: 2500,
        custom: function() {
            if (!Partup.services.validators.containsNoHtml(this.value)) {
                return 'shouldNotContainHtml';
            }

            if (!Partup.services.validators.containsRequiredTags(this.value, ['url', 'name'])) {
                return 'missingRequiredTags';
            }

            if (!Partup.services.validators.containsNoUrls(this.value)) {
                return 'shouldNotContainUrls';
            }
        }
    }
});
                        
                    

schema use (frontend)

                        
Template.InviteToActivity.helpers({
    formSchema: Partup.schemas.forms.inviteUpper,
    submitting: function() {
        return Template.instance().submitting.get();
    },

    // snip
});
                        
                    

schema use (backend)

                        
    /**
     * Invite someone to an partup
     *
     * @param {string} partupId
     * @param {string} email
     * @param {string} name
     */
    'partups.invite_by_email': function(partupId, fields) {
        check(fields, Partup.schemas.forms.inviteUpper);

        var inviter = Meteor.user();

        if (!inviter) {
            throw new Meteor.Error(401, 'unauthorized');
        }

        var partup = Partups.findOneOrFail(partupId);

        // snip
    }
                        
                    

shared functions

                        
/**
 * Extract mentions from a message
 *
 * e.g. "Hello [user:9ZxF5SHPjcAfbey4j|Jesse de Vries] & [user:EpWPsBoBexD9QFSMR|Nick Koster]"
 */
Partup.helpers.mentions.extract = function(message) {
    var mentions = [];
    // extracts user (single) mentions
    extractUsers(message).forEach(function(mention) {
        var existingMention = lodash.find(mentions, {_id: mention._id});
        if (!existingMention) mentions.push(mention);
    });

    // extracts partners (group) mention
    extractPartners(message).forEach(function(mention) {
        var existingMention = lodash.find(mentions, {name: 'Partners'});
        if (!existingMention) mentions.push(mention);
    });

    // extracts supporters (group) mention
    extractSupporters(message).forEach(function(mention) {
        var existingMention = lodash.find(mentions, {name: 'Supporters'});
        if (!existingMention) mentions.push(mention);
    });
    return mentions;
};
                        
                    

ES2015 modules (meteor 1.3)

                        
/**
 * Extract mentions from a message
 *
 * e.g. "Hello [user:9ZxF5SHPjcAfbey4j|Jesse de Vries] & [user:EpWPsBoBexD9QFSMR|Nick Koster]"
 */
export function extract(message) {
    var mentions = [];
    // extracts user (single) mentions
    extractUsers(message).forEach(function(mention) {
        var existingMention = lodash.find(mentions, {_id: mention._id});
        if (!existingMention) mentions.push(mention);
    });

    //snip

    return mentions;
};
                        
                    

ES2015 modules (meteor 1.3)

                        
import extract from './helpers/mentions/extract';

Template.InviteToActivity.helpers({
    extractedMentions: function() {
        return extract(Template.instance().message.get());
    },

    // snip
});
                        
                    

Next steps

(future of isomorphicness)

  • holy grail: full isomorphic app
  • html + data rendering on backend
  • client side SPA interaction
  • meet: react-starter-kit

react-starter-kit

sneakpeak

  • react components
  • react router
  • graphql api
  • serverside rendering (server.js:100)

Thank you for your attention

Meteor and Isomorphic Javascript

@peterpeerdeman