Fullstacking: Connecting NodeJS ↔ MongoDB
Tags: node, mongodb
Published: 2019-08-29 23:21:35

With [React Native](https://dev.to/heymarkkop/fullstacking-setting-up-react-native-1g21), [NodeJS + KoaJS](https://dev.to/heymarkkop/fullstacking-setting-up-nodejs-koajs-2jhb) and [MongoDB](https://dev.to/heymarkkop/fullstacking-setting-up-mongodb-3oed) set up, we can start connecting them with each other.

## KoaJS ↔ MongoDB

As I want to learn and practice **T**est-**D**riven **D**evelopment, we'll be creating tests first and it seems that [Mocha](https://mochajs.org/#getting-started) is [recommended over Jest](https://mongoosejs.com/docs/jest.html) to test Mongoose (which we'll be using later). 
We'll also be using [supertest](https://github.com/visionmedia/supertest) for integration testing.

`yarn add mocha --dev`
`yarn add supertest --dev`

Create a test file like this:

```javascript
// server.test.js
const request = require('supertest');
const app = require('./koa');

describe('Server', () => {
  it('is running', done => {
    request(app)
      .get('/')
      .expect(200, done);
  });
});
```

and change `package.json` as following:
```javascript
// package.json
...
  "scripts": {
    ...
    "test": "mocha server.test.js --watch",
    ...
  },
...
```

I've used `mocha './server/*.test.js' --recursive --watch` instead so it runs all test files inside server folder. We'll probably change this later.

Run `yarn test` and find that `TypeError: app.address is not a function` because app doesn't exist yet thus it's time to write the actual code

```javascript
// server.js
const Koa = require('koa');

const app = new Koa();

app.use(async ctx => {
  ctx.body = "Hello World, I'm Koa";
});

module.exports = app.listen(3000, () =>
  console.log('Running on http://localhost:3000/'),
);
```
Don't forget to `module.exports` it.
Now our first test is passing, but it can throw `Uncaught Error: listen EADDRINUSE: address already in use :::3000` when trying to run the test again or **--watch**ing it.
We have to [close](https://github.com/visionmedia/supertest/issues/437) the server after each test, so add this inside `describe()` block

```javascript
// server.test.js
...
 afterEach(() => {
    app.close();
  });
...
```

With Koa working and being tested, we can now try to read some information from our MongoDB instance.

Let's try to find our [stampler](https://dev.to/heymarkkop/fullstacking-setting-up-mongodb-3oed) by adding this test
```javascript
// server.test.js
...
  it('finds our stampler', done => {
    request(app)
      .get('/')
      .expect(/Stampler/)
      .expect(200, done);
  });
```

It'll return `Error: expected body 'Hello World, I\'m Koa' to match /stampler/` because `ctx.body` is a plain text, not the data in our database.
To access it, we'll use [Mongoose](https://mongoosejs.com/docs/index.html)
`yarn add mongoose` or `npm install mongoose`

Create a `Product.js` to define a new schema
```javascript
// Product.js
var mongoose = require('mongoose');

const ProductSchema = mongoose.Schema({
  title: String,
});

module.exports = mongoose.model('Product', ProductSchema);
```

And use it as it follows
```javascript
// server.js
const Koa = require('koa');
const mongoose = require('mongoose');
const Product = require('./Product');

const app = new Koa();

mongoose.connect('mongodb://127.0.0.1:27017/test', {useNewUrlParser: true});

app.use(async ctx => {
  //ctx.body = "Hello World, I'm Koa";
  ctx.body = await Product.find({});
});

module.exports = app.listen(3000, () =>
  console.log('Running on http://localhost:3000/'),
);

```

Note that your [MongoDB should be running](https://dev.to/heymarkkop/fullstacking-setting-up-mongodb-3oed) or you'll get
```
MongoNetworkError: failed to connect to server [127.0.0.1:27017] on first connect [MongoNetworkError: connect ECONNREFUSED 127.0.0.1:27017]
```
You can check it by running `mongo` in the terminal and
`sudo services mongodb start` to start it.


It should work at first, however you might notice an error if **--watch**ing the test suite:
`OverwriteModelError: Cannot overwrite 'Product' model once compiled.`
To fix it, add this beforeEach like you did with afterEach, so it deletes the Product model before testing again.
```javascript
// server.test.js
...
  beforeEach(async () => {
    const url = 'mongodb://127.0.0.1:27017/test';
    await mongoose.connect(url, {useNewUrlParser: true});
    delete mongoose.connection.models.Product;
  });
...
```

While we're on it, let's try to add a new product, check if it exists and clean it after. Let's also separate some `describes`
```javascript
// server.test.js
const request = require('supertest');
const mongoose = require('mongoose');
const app = require('./app');
const Product = require('./Product');

describe('Server', () => {
  describe('without acessing MongoDB', () => {
    afterEach(() => {
      app.close();
    });

    it('is successful', done => {
      request(app)
        .get('/')
        .expect(200, done);
    });

    it('finds our stampler', done => {
      request(app)
        .get('/')
        .expect(/Stampler/)
        .expect(200, done);
    });
  });

  describe('acessing MongoDB direcly', () => {
    afterEach(() => {
      Product.deleteOne({title: 'skate'}).exec();
    });

    beforeEach(async () => {
      const url = 'mongodb://127.0.0.1:27017/test';
      await mongoose.connect(url, {useNewUrlParser: true});
      delete mongoose.connection.models.Product;
    });

    it('creates and finds a skate', done => {
      const skate = new Product({title: 'skate'});
      skate.save();
      request(app)
        .get('/')
        .expect('Content-Type', /json/)
        .expect(/skate/)
        .expect(200, done);
    });
  });
});

```

This probably isn't the optimal and most elegant way to [test](https://medium.com/nongaap/beginners-guide-to-writing-mongodb-mongoose-unit-tests-using-mocha-chai-ab5bdf3d3b1d) integration, but I'll leave the comments open for hints and suggestions

## References
[Hackernoon - API testing using SuperTest](https://hackernoon.com/api-testing-using-supertest-1f830ce838f1)
[Bits and Pieces- Build a Unit-Testing Suite with Mocha and Mongoose](https://blog.bitsrc.io/build-a-unit-testing-suite-with-mocha-and-mongoose-eba06c3b3625)
[Zellwk - Connecting Jest and Mongoose](https://zellwk.com/blog/jest-and-mongoose/)
[SmoothTerminal - Build an API with Koa.js](https://www.smoothterminal.com/articles/build-an-api-with-koa-js)