Dr. Frisby's Mostly Adequate Guide to Functional Programming

A “Mostly Adequate” guide to funcitonal programming. Going onto the reading list.

Written using Gitbook which is a really cool tool for writing online books. I wish they had a way to star or add books to a library as it is… I’ll just have to drop this link here.

Dokku + Dockerfile deployments

Dokku is a fantastic little PAAS app written in bash that can take a git push, build, and host your apps. It can use Heroku Buildpacks or Docker to build your app (and uses Docker to host your running app) and can hadle running multiple Docker containers on a single host and sets up Nginx to proxy and route requests to the correct application.

I have deployed many apps onto our Dokku hosts, mostly Node.js apps using Hapi.js, Express, as well as Java services. Recently I built a statically-rendered React application (a client-side only app without a server process doing the rendering etc; see this post for explaination) where I just wanted Nginx to handle serving the app code and assets. To do so I created a new base Docker image for serving the app just ran Nginx and ponted to my app code.

One problem with this static-rendering strategy is the issue of configuration. Depending on the environment we were deploying the app too meant we needed slightly different setups. For the test environment, we want to point to test envirnmant APIs, use production builds, while production would need to use the produciton APIs, and also would be proxied to from the getway nginx servers with a path prefix i.e. under test the app would be at http://myapp.test.dev while the production app would be at http://foo.com/myapp – there is an extra layer of nginx proxying involved in the production setup, but it also requires the production build to be aware of that extra path segment when returning URLs for assets (javascript, PNGs etc).

Where we have server process handling requests we have full control over these and use request headers to inject this configuration per request, however a static app can’t do this in the same way so we’re left with producing environment specific builds. Dokku inherently does this as every deployment (e.g. to prod host or test host) so we just need to be able to pass this configuration at build time. According to the Dokku docs “The variables are available both at run time and during the application build/compilation step” however this is only true for Heroku buildpack deployments, not Docker deployments.

To get the same thing with Docker builds we have to use the dokku docker-options command which allows us to set options for build, deploy, or run phases of the process. Under the covers the Dokku build invokes docker build which does not take the same -e parameter that docker run accepts for setting environment variables, but does have the --build-args parameter. One caveat of this technique is this requires the Dockerfile your app uses to be modified to accept build arguments using the ARG command. If you don’t specify the arg being passed with ARG the build will fail with the error “One or more build-args were not consumed”.

So to set a build argument of PATH_PREFIX in our example above:

  1. dokku build-options:add myapp build '--build-arg "PATH_PREFIX=/myapp"'
  2. modify your dockerfile
FROM baseimage

ARG PATH_PREFIX                            # declare PATH_PREFIX as build arg
RUN PATH_PREFIX=${PATH_PREFIX} ./dobuild   # use PATH_PREFIX (in this case as an env var consumed by build script)

Then commit changes and git push your app

Safari + Hapi.js + CORS = 404

So with recent software updated on OS X (including newest Safari 9.0.1) and also updating a project to latest version of hapi.js(v11) CORS requests are broken.

It appears Safari now sends an Access-Control-Request-Headers value of “accept, origin, authorization”, while Hapi has a default configuration for CORS headers doesn’t include “origin”. Chrome meanwhile, was still working only sending “accept, authorization” for the header value.

The solution is to tell Hapi to also accept “origin” as a CORS request header value:

server.connection({
  port: process.env.PORT || 8081,
  routes: {
    cors: {
      additionalHeaders: [
        "Origin"
      ]
    }
  }
});

There have been a few changes in configuration and handling of CORS in Hapi of late, so this may not be needed down the road if changes are made to the default configuration.

React & Redux Tutorial (including testing!)

This article by Tero Parviainen (@teropa) is one of the best full-stack Redux & React examples I’ve come across lately, covering the server-side, thinking about application state, socket.io, and testing react components.

Well worth the read.

Hapi.js & invalid cookies

I love using Hapi.js, but every time I start a new project with it I have this problem where all my requests get 400 responses due to an invalid cookie value.

My new shiny hapi app doesn’t care about cookies. I don’t want it to care about cookies. But it still does. By default, hapi tries to read all the cookies that were sent with the request, including signed cookies set by express. Of course, as I’ve a habit of developing on localhost, and because we have multiple apps on the same domain (java, express, as well as hapi) where some of those apps care about these cookies, these cookies are going to get sent along with the request to an app that doesnt care about them. Hapi does it’s job to try and verify the signed express cookie and fails (it doesn’t have any way to actually verify, i.e. no keys/secrets used in the signing), retuning the 400.

So every new hapi service I start with now I ignore cookie errors:

server.connection({
  port: process.env.PORT || 8000,
  state: {
    ignoreErrors: true
  }
});