Separate Express ‘app’ and ‘server’

One Paragraph Explainer

The latest Express generator comes with a great practice that is worth to keep - the API declaration is separated from the network related configuration (port, protocol, etc). This allows testing the API in-process, without performing network calls, with all the benefits that it brings to the table: fast testing execution and getting coverage metrics of the code. It also allows deploying the same API under flexible and different network conditions. Bonus: better separation of concerns and cleaner code

Code example: API declaration, should reside in app.js/app.ts

  1. const app = express();
  2. app.use(bodyParser.json());
  3. app.use('/api/events', events.API);
  4. app.use('/api/forms', forms);

Code example: Server network declaration, should reside in /bin/www

Javascript

  1. const app = require('../app');
  2. const http = require('http');
  3. // Get port from environment and store in Express.
  4. const port = normalizePort(process.env.PORT || '3000');
  5. app.set('port', port);
  6. // Create HTTP server.
  7. const server = http.createServer(app);

Typescript

  1. import app from '../app';
  2. import http from 'http';
  3. // Get port from environment and store in Express.
  4. const port = normalizePort(process.env.PORT || '3000');
  5. app.set('port', port);
  6. // Create HTTP server.
  7. const server = http.createServer(app);

Example: test your API in-process using supertest (popular testing package)

Javascript

  1. const request = require('supertest');
  2. const app = express();
  3. app.get('/user', (req, res) => {
  4. res.status(200).json({ name: 'tobi' });
  5. });
  6. request(app)
  7. .get('/user')
  8. .expect('Content-Type', /json/)
  9. .expect('Content-Length', '15')
  10. .expect(200)
  11. .end((err, res) => {
  12. if (err) throw err;
  13. });

Typescript

  1. import * as request from "supertest";
  2. const app = express();
  3. app.get('/user', (req: Request, res: Response) => {
  4. res.status(200).json({ name: 'tobi' });
  5. });
  6. request(app)
  7. .get('/user')
  8. .expect('Content-Type', /json/)
  9. .expect('Content-Length', '15')
  10. .expect(200)
  11. .end((err: Error) => {
  12. if (err) throw err;
  13. });