# HTTP/2 with Feathers and Express

**URL:** <https://forums.bitovi.com/t/http-2-with-feathers-and-express/1016>\
**Category:** DoneJS\
**Created:** [January 13, 2019, 7:44pm UTC](https://forums.bitovi.com/t/http-2-with-feathers-and-express/1016 "2019-01-13T19:44:36Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![leoj3n](https://yyz1.discourse-cdn.com/flex035/user_avatar/forums.bitovi.com/leoj3n/32/174_2.png) [@leoj3n](https://forums.bitovi.com/u/leoj3n)\
**Post date:** [January 13, 2019, 7:44pm UTC](https://forums.bitovi.com/t/http-2-with-feathers-and-express/1016/1 "2019-01-13T19:44:36Z")

</div>

I’ve collected some links about how the native HTTP/2 module doesn’t work with Express.

- However, this SPDY module does: [https://www.npmjs.com/package/spdy](https://www.npmjs.com/package/spdy)
- How to create a SPDY server: [https://webapplog.com/http2-node/](https://webapplog.com/http2-node/)
- Should be fixed in 5.0: [https://github.com/molnarg/node-http2/issues/100#issuecomment-98520102](https://github.com/molnarg/node-http2/issues/100#issuecomment-98520102)
- Express 5.0 alpha release: [https://github.com/expressjs/express/releases](https://github.com/expressjs/express/releases)
- Comment: [For now (while we are waiting for Express v5) you can use spdy with express](https://github.com/molnarg/node-http2/issues/100#issuecomment-231173779).
- If using steal-tools live-reload, you will need to pass along the key/cert: [see source code](https://github.com/stealjs/steal-tools/blob/22cadf61b75fd339bf46c17f12e753b70ce98506/lib/cli/cmd_live_reload.js#L16-L25).
- You may also consider using fastify instead of express: [https://github.com/SAP/ui5-server/issues/77](https://github.com/SAP/ui5-server/issues/77)

Since I’m using Feathers, I’ll be sticking to using `@feathersjs/express` with `spdy` until `express@5.0` is used and supports the native `http2` module, as it seems [fastify in feathers might not be straightforward](https://github.com/feathersjs/feathers/pull/605).

Here is what my server code looks like (seems to work as expected):

### `config/development.json`

```json
{
  "port": 3030,
  "host": "DEV_SERVER_HOST",
  "client": "../../client/",
  "mongodb": "mongodb://localhost:27017/database",
  "key": "localhost+2-key.pem",
  "cert": "localhost+2.pem"
}

```

Note: key/cert generated using [https://github.com/FiloSottile/mkcert](https://github.com/FiloSottile/mkcert).

### `index.js`

```auto
'use strict';

// ENVIRONMENT

if (process.argv.indexOf('--develop') !== -1) {
  process.env.DONE_SSR_DEBUG = 1;
  process.env.NODE_ENV = 'development';
}

console.log(`process.env.NODE_ENV = '${process.env.NODE_ENV}'`);

// app server

const fs = require('fs');
const path = require('path');
const spdy = require('spdy');
const app = require('./app');

const host = app.get('host');
const port = app.get('port');
const key = path.resolve(__dirname, app.get('key'));
const cert = path.resolve(__dirname, app.get('cert'));

const server = spdy
  .createServer(
    {
      key: fs.readFileSync(key),
      cert: fs.readFileSync(cert),
    },
    app
  )
  .listen(port);

app.setup(server);

server.on('listening', () =>
  console.log(
    `DoneJS and Feathers application started on https://${host}:${port}`
  )
);

// live-reload

if (process.argv.indexOf('--develop') !== -1) {
  const exec = require('child_process').exec;
  const killOnExit = require('kill-on-exit');

  const stealTools = path.join('node_modules', '.bin', 'steal-tools');

  const child = exec(
    `${stealTools} live-reload --ssl-key ${key} --ssl-cert ${cert}`,
    {
      cwd: app.get('client'),
    }
  );

  child.stdout.pipe(process.stdout);
  child.stderr.pipe(process.stderr);

  killOnExit(child);
}

```

### `app.js`

```auto
'use strict';

const path = require('path');
const cors = require('cors');
const services = require('./services');
const favicon = require('serve-favicon');
const compression = require('compression');
const middleware = require('./middleware');
const express = require('@feathersjs/express');
const feathers = require('@feathersjs/feathers');
const socketio = require('@feathersjs/socketio');
const configuration = require('@feathersjs/configuration');

const app = express(feathers());

app
  .configure(configuration())
  .options('*', cors())
  .use(cors())
  .use(compression())
  .use(favicon(path.join(app.get('client'), 'favicon.ico')))
  .use(express.json())
  .use(express.urlencoded({ extended: true }))
  .configure(express.rest())
  .configure(socketio())
  .configure(services)
  .use(express.static(app.get('client')))
  .use('/node_modules', (req, res) => {
    res.sendStatus(404);
  })
  .configure(middleware);

app.on('connection', connection => {
  app.channel('anonymous').join(connection);
});

app.publish((data, context) => {
  return app.channel('anonymous');
});

module.exports = app;

```

### `middleware/index.js`

```auto
'use strict';

const errorHandler = require('./error');

module.exports = function(app) {
  if (process.argv.indexOf('--static') !== -1) {
    console.log('Configuring server app with static middleware.');
    app.configure(require('./static'));
  } else {
    console.log('Configuring server app with SSR middleware.');
    app.configure(require('./ssr'));
  }

  app.configure(errorHandler);
};

```

### `middleware/static.js`

```auto
'use strict';

const fs = require('fs');
const path = require('path');

module.exports = function(app) {
  app.get('*', (req, res, next) => {
    const urlParts = path.parse(req.url);
    const isPushstateRoute = !urlParts.ext || urlParts.name.includes('?');

    if (isPushstateRoute) {
      const env = process.env.NODE_ENV || 'production';
      const htmlPath = path.join(app.get('client'), `${env}.html`);

      if (fs.existsSync(htmlPath)) {
        return res.sendFile(htmlPath);
      }
    }

    return next();
  });
};

```

### `middleware/ssr.js`

```auto
'use strict';

const path = require('path');
const ssrMiddleware = require('done-ssr-middleware');

module.exports = function(app) {
  app.use(
    ssrMiddleware(
      {
        config: path.join(app.get('client'), 'package.json!npm'),
        liveReload: process.env.NODE_ENV === 'development',
      },
      {
        strategy: 'incremental',
        debug: process.env.NODE_ENV === 'development',
      }
    )
  );
};

```

Note: using `strategy: 'incremental'` for [incremental rendering](https://github.com/donejs/done-ssr#strategy-incremental) works.

### `middleware/error.js`

```auto
'use strict';

const errorFormat = require('donejs-error-format');

module.exports = function(app) {
  app.use((err, req, res, next) => {
    if (res.headersSent) {
      return next(err);
    }

    const html = errorFormat.html(errorFormat.extract(err), {
      liveReload: process.env.NODE_ENV === 'development',
    });

    console.error(err.stack);

    res
      .status(500)
      .type('html')
      .end(html);
  });
};

```

### `services/index.js`

```auto
'use strict';

const mongoose = require('mongoose');

// require services
const contributor = require('./contributor');

module.exports = function(app) {
  mongoose.Promise = global.Promise;

  // connect to mongodb
  if (process.env.TESTING !== 'true') {
    mongoose.connect(
      app.get('mongodb'),
      {
        useCreateIndex: true,
        useNewUrlParser: true,
        reconnectTries: Number.MAX_VALUE,
      }
    );
  }

  // configure endpoints
  app.configure(contributor);
};

```

### `services/contributor/index.js`

```auto
'use strict';

const Model = require('./model');
const service = require('feathers-mongoose');

module.exports = function(app) {
  app.use(
    '/api/contributors',
    service({
      Model: Model,
      lean: true,
    })
  );

  app.service('/api/contributors').hooks({
    after(context) {
      context.data && delete context.data.__v;
    },
  });
};

```

### `services/contributor/model.js`

```auto
'use strict';

const mongoose = require('mongoose');

const Schema = new mongoose.Schema({
  name: { type: String, required: true },
  email: { type: String, required: true, unique: true },
  active: { type: Boolean },
});

const Model = mongoose.model('Contributor', Schema);

module.exports = Model;

```

## Result

 ![ss](https://canada1.discourse-cdn.com/flex035/uploads/donejs/original/1X/0a51adbc920a6ca360370b2294448e88c31c18b6.png)

Hope this helps someone else trying to get h2 working with feathers express.

More links about server push (for future self reference):

- [https://docs.google.com/document/d/1K0NykTXBbbbTlv60t5MyJvXjqKGsCVNYHyLEXIxYMv0/edit](https://docs.google.com/document/d/1K0NykTXBbbbTlv60t5MyJvXjqKGsCVNYHyLEXIxYMv0/edit)
- [https://devcenter.heroku.com/articles/http-routing#http-versions-supported](https://devcenter.heroku.com/articles/http-routing#http-versions-supported)
- [https://github.com/google/node-h2-auto-push](https://github.com/google/node-h2-auto-push)
- [https://stackoverflow.com/a/34319871](https://stackoverflow.com/a/34319871)
- [https://github.com/rmurphey/chrome-http2-log-parser](https://github.com/rmurphey/chrome-http2-log-parser)
- [https://bugs.chromium.org/p/chromium/issues/detail?id=464501](https://bugs.chromium.org/p/chromium/issues/detail?id=464501)
- [https://medium.com/totally-tooling-tears/issue-5-http-2-push-9c6eba8d6d7c](https://medium.com/totally-tooling-tears/issue-5-http-2-push-9c6eba8d6d7c)
- [https://github.com/fastify/fastify/blob/master/docs/HTTP2.md](https://github.com/fastify/fastify/blob/master/docs/HTTP2.md)
- [https://http2-push.appspot.com/](https://http2-push.appspot.com/)
- [https://github.com/donejs/done-ssr-middleware/issues/69](https://github.com/donejs/done-ssr-middleware/issues/69)

---

<div class="post-metadata">

**Author:** ![justinbmeyer](https://yyz1.discourse-cdn.com/flex035/user_avatar/forums.bitovi.com/justinbmeyer/32/179_2.png) [@justinbmeyer](https://forums.bitovi.com/u/justinbmeyer)\
**Post date:** [January 13, 2019, 8:18pm UTC](https://forums.bitovi.com/t/http-2-with-feathers-and-express/1016/2 "2019-01-13T20:18:44Z")

</div>

Wow. Thanks for sharing!

---

<div class="post-metadata">

**Author:** ![kissema](https://yyz1.discourse-cdn.com/flex035/user_avatar/forums.bitovi.com/kissema/32/470_2.png) [@kissema](https://forums.bitovi.com/u/kissema)\
**Post date:** [July 11, 2020, 3:24pm UTC](https://forums.bitovi.com/t/http-2-with-feathers-and-express/1016/3 "2020-07-11T15:24:49Z")

</div>

Now you can use express with http2 protocol easily using the express-h2 module. You can read on this post: [Click Here](https://medium.com/@kissema1/the-easy-way-to-use-http2-protocol-with-express-framework-dfae4f8e9689?source=friends_link&sk=b6986b8dac15016b684b08b618567249)
