Universal Javascript And
Bad Jokes

It's a tradition.....

Sam Clark

Iced Development

DevOps/Frontend/Backend/Slacker

@samrocksc

samrocksc@gmail.com

Why Universal Is Good

  • Shorter Dependency Lists
  • It's thoughtful and considerate
  • It's good for junior devs!
  • Robotics Libraries for the Browser(Johnny5/Noble)
  • Telling people your a 1337 fUlLSt4Ck d3v

Things We Can Do

Browser

  • the global is typically window
  • require doesn't exist
  • processes response objects differently

Node

  • handles buffers differently
  • Doesn't have a pre-defined `window` object
  • There is no DOM
  • Heavily module dependent
  • Processes responses differently

Look Out For

  • What libraries are handling what
  • Known Differences
  • Unknown Differences

Planning for Unknown Differences

Howard

Wrapping isomorphic-fetch

https://github.com/samrocksc/howard

Goals

  • Error Handling Differences
  • Request API for both frontend/backend
  • Extensibility
  • Return a promise
  • Ease of use with modern frontend API's
  • Still utilize fetch's super easy request interface

Base Usage

const { default: howard, json } = require('howard');

json(howard('https://swapi.co/api/people/1/'))  
  .then((res) => {
    console.log('res', res);
    return res;
  });

isomorphic-fetch

  • Only has two dependencies
  • Returns a promise

node-fetch

A library aimed at bringing node into consistency with window.fetch

fetch-ponyfill

Aims to build and be compliant with the whatwg fetch spec

Basic Howard

function howard(path, options) {
  if (options && options.body && !options.method) {
    options.method = 'POST';
  }
  return fetch(path, options);
}

Handling Differences

function formData(response) {
  return Promise.resolve(response)
    .then((res) => {
      if (typeof res.formData === 'function') {
        return res.formData();
      }
      return Promise.reject(new Error('Method not implemented'));
    });
}

...other ways

if (typeof options.body === 'object' && !(global.FormData && options.body instanceof FormData))

TESTING!!

Testing Deps

  • mocha-puppeteer
  • fetch-mock
  • expect(easier for promise resolves)

Typical Test Harness

// Form Data
  it('handle formData()', function() {
    if (isNode) {
      this.skip();
    }
    const data = new FormData();
    data.append('testing', 'this')
    fetchMock.getOnce(config.url + '/form', { body: data, sendAsJson: false })
    const request = formData(howard(config.url + '/form'))

    return expect(request).resolves.toEqual(data);
  });

Skipping With ifNode

const isNode = (process &&  process.release && process.release.name === 'node');

* For some reason this was one of the hardest things to figure out for me.  TypeErrors thrown in this situation can throw a wrench in the entire application.

Testing Scripts

"test:browser": "mocha-puppeteer test",
"test": "cross-env BABEL_ENV=commonjs mocha --async-only --compilers js:babel-register --recursive",

Testing Node!

Testing Browser!

Conclusion

Isomorphic development isn't for every aspect of programming, but when something can be done universally, let's try to make that happen!

Open Bad Jokes Session

Universal Javascript AndBad Jokes

By Sam Clark

Universal Javascript AndBad Jokes

  • 795