SSR a "Create React App" the hard way

I’ve been building out a web app using Create React App, developing and deploying is a breeze. But now I need a server side rendering solution for my specific sitation. All the solutions around the web I have found are great, but they don’t address the issue I have. So in this post I’ll share my own “from scratch” solution. The problem I am solving is:

Google has trouble with my single page app written in javascript, I want to create a static HTML render of all pages inside my app. I want to serve these static pages to googlebot (and other crawlers). Since my app displays a lot of dynamic information (pulled in from an API, updated hourly) I want to be able to easily rerender the complete UI with minimal effort (and compilation steps).

So this is NOT a SSR solution that prerenders an initial page for a user, instead it statically “compiles” a large number of pages in my app whenever the API data changes into plain HTML files which are served through a webserver (to crawlers, not to users). I’ve build my solution for the create-react-app version I am using, which is react-scripts 2.0.3 and uses react 16.4.1. If you are using another version everything below might not work.

Note that my approach is NOT recommended, it requires you to put monkey patch components in your app and forces you to break promises (out of spec compliance). As soon as React updates their lifecycle hooks (again) you will need to come and fix this or you might break everything below. Here by dragons.

more

How to use Geospatial Indexing in mongo using nodejs and mongoose

A couple of weeks ago I build a mobile app for school that shows you cultural stuff todo around you. It’s called randomapp (the assignment was to build an app using the Artsholland API. Therefor it only works in the Netherlands).

I’ve decided to build the backend in Mongo because of the recently added support for geospatial. I’ve got more experience in MySQL but the idea of having to write all the logic involved with querying location data myself alone was enough for me to choose for Mongo.

The backend for the app is a very simple API written in Node to query the Mongo collection location data. In this tutorial I’ll show you how some easy steps to get started asking Mongo questions about locations. I definitely recommend you check out the official documentation which covers everything I’m about to explain plus a lot more. Though this tutorial is more of an introduction to the subject.

more

How to control your HTML5 game using Arduino

Yesterday I made a mini HTML5 game based on the retro game Asteroids (here is a flashversion) without the asteroids, so I just made a ship with those controls. You can check it out on my personal site and the code is at my Github.

What about instead of using the keyboard for controls, using a potentiometer for the controls? Here is a video with me explaining it (the demo is at 8:30 into the video).

Here is the code to get it working:

The arduino code is straight from the node duino module. Read more about it in my article about duino.

Nodejs script

dependencies: node, duino, socket.io

var l = console.log;

var http = require('http');
var url  = require('url');

var io = require('socket.io').listen(1337);
io.set('log level', 1);

var arduino = require('duino');
var board = new arduino.Board({
  // debug: true
});
// defaults to a01
var pot = new arduino.Sensor({
  board: board,
});
var init = function() {
  broadcast();
}
board.on( 'ready', function() {
  // still not ready somehow
  setTimeout( init, 100 );
});

HTML file

<?php 
    header("Access-Control-Allow-Origin: *");
    header("Access-Control-Allow-Headers: *");
    header("Access-Control-Expose-Headers: Access-Control-Allow-Origin");
?>
<!doctype html>
<!--#

          Insert coin(s)

    &lt;Push any button to start&gt;

-->
<html lang=en>
    <head>
        <meta charset=utf-8>
        <meta http-equiv=X-UA-Compatible content="IE=edge,chrome=1">
        <title>ardiono game</title>
        <style>
            body {
                background-color:black;
            }
            #game {
              position: absolute;
              top: 0px;
              left: 0px;
              z-index: -1;
            }
        </style>
    </head>
    <body>
        <canvas id=game></canvas>
        <script src="//cdnjs.cloudflare.com/ajax/libs/socket.io/0.9.10/socket.io.min.js">
        <script src="game.js"></script>
    </body>
</html>
<!--#

    GAME OVER

front end javascript (game)

(function(d, c){
  //helpers
  var getHeight = function() {
    var D = document;
    return Math.max(
        Math.max(D.body.scrollHeight, D.documentElement.scrollHeight),
        Math.max(D.body.offsetHeight, D.documentElement.offsetHeight),
        Math.max(D.body.clientHeight, D.documentElement.clientHeight)
    );
  };
  var getWidth = function() {
    var D = document;
    return Math.max(
        Math.max(D.body.scrollWidth, D.documentElement.scrollWidth),
        Math.max(D.body.offsetWidth, D.documentElement.offsetWidth),
        Math.max(D.body.clientWidth, D.documentElement.clientWidth)
    );
  };
  // polyfill: set the window.requestAnimationFrame to the browsers specific version.
  (function() {
    var lastTime = 0
      , vendors = ['ms', 'moz', 'webkit', 'o']
      , x
      , length
      , currTime
      , timeToCall;

for(x = 0, length = vendors.length; x < length && !window.requestAnimationFrame; ++x) {
    window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
    window.cancelAnimationFrame = 
      window[vendors[x]+'CancelAnimationFrame'] || window[vendors[x]+'CancelRequestAnimationFrame'];
}
if (!window.requestAnimationFrame)
    window.requestAnimationFrame = function(callback, element) {
        currTime = new Date().getTime();
        timeToCall = Math.max(0, 16 - (currTime - lastTime));
        lastTime = currTime + timeToCall;
        return window.setTimeout(function() { callback(currTime + timeToCall); }, 
          timeToCall);
    };
if (!window.cancelAnimationFrame)
    window.cancelAnimationFrame = function(id) {
        clearTimeout(id);
    };

}());
  var angle = 0;
  // socket io hook
  var socket = io.connect('http://localhost:1337');
   socket.on('news', function (data) {
     // console.log(data.v);
     angle = data.v / 1024 * 8;
   });

var canvas = document.getElementById("game");
  canvas.height = getHeight();
  canvas.width = getWidth();
  // draw function is in a closure
  var draw = (function() {
    var PI = Math.PI
      , twoPI = PI * 2
      , speed = 5
      , shipX = 100
      , shipY = 100
      , degrees
      , cap = function( n, cap ) {
        return n < 0 ? n + cap : n % cap;
      }
    return function() {
      // clear the stage
      ctx.clearRect( 0, 0, canvas.width, canvas.height );
      ctx.save();
      // calc new position
      shipX += Math.sin( angle ) * speed;
      shipY -= Math.cos( angle ) * speed;
      shipX = cap( shipX, canvas.width );
      shipY = cap( shipY, canvas.height );
      // draw the changes
      // position
      ctx.translate( shipX, shipY );
      // rotate
      ctx.rotate( angle );
      // draw
      ctx.beginPath();
      ctx.fillStyle   = 'white';
      ctx.strokeStyle = 'white';
      // ship
      ctx.lineTo(0, -15);
      ctx.lineTo(10, 15);
      ctx.lineTo(0, 13);
      ctx.lineTo(-10, 15);
      // finish
      ctx.fill();
      ctx.stroke();
      ctx.closePath();
      ctx.restore();
    }
  })();
  if( canvas.getContext ) {
    var ctx = canvas.getContext("2d");
    // the loop
    (function animloop(){
        requestAnimationFrame( animloop );
        draw();
      })();
  }

Introduction to Node.js and what I find so great about it

My knowledge of the inner workings of node.js and threading is pretty basic. I can’t guarantee everything written here is accurate. If you spot an error, please let me know!

I’ve been playing around with Node.js for a while now and even though the community behind it is getting bigger, there are a lot of people disliking a lot of things about Node.js. In this post I will explain some of the different inner workings and what I like about them.

I hear you ask yourself: “Why Javascript?”. Even though a lot of people think Javascript is slow, it’s not so bad actually. Note that a lot of things that make it slow in the browser (like the really slow DOM api) are non existent on the server.

From the Node.js homepage:

Node.js is a platform built on Chrome’s JavaScript runtime for easily building fast, scalable network applications. Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient, perfect for data-intensive real-time applications that run across distributed devices.

Node.js allows you to write your server side scripts in javascript. The V8 engine of Google (which is also a part of Chrome, and is one of the big reasons on why Chrome is so fast) will take your javascript scripts and compile them on the fly to machine code.

But the thing that is most different from Node to traditional server sided languages for doing web stuff is the way it works.

more

How to connect your Arduino to nodejs using duino

In this video I’ll explain how to set up your Arduino and Duino so you can interact with your Arduino using nodejs (and javascript).

  • The Arduino software package is downloadable from the Arduino website.
  • You can find the Duino module here.
  • Installing Duino is as simple as running npm install duino.
  • My custom bleep function:

    var arduino = require(‘duino’),

    board = new arduino.Board();
    

    var led = new arduino.Led({ board: board, pin: 13 });

    var bleep = function() {

    led.on();
    setTimeout(function() {
        led.off();
    }, 100);
    

    }

Troubleshooting:

  • You need a recent version of nodejs (0.4.x was not working) for the Duino module.
  • If you can’t upload the Arduino program, make sure the right model is selected at Tools -> Board.