DEV Community

Mark Kop
Mark Kop

Posted on

1 1

Fullstacking: Connecting NodeJS ↔ MongoDB

With React Native, NodeJS + KoaJS and MongoDB set up, we can start connecting them with each other.

KoaJS ↔ MongoDB

As I want to learn and practice Test-Driven Development, we'll be creating tests first and it seems that Mocha is recommended over Jest to test Mongoose (which we'll be using later).
We'll also be using supertest for integration testing.

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

Create a test file like this:

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

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

and change package.json as following:

// package.json
...
  "scripts": {
    ...
    "test": "mocha server.test.js --watch",
    ...
  },
...
Enter fullscreen mode Exit fullscreen mode

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

// 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/'),
);
Enter fullscreen mode Exit fullscreen mode

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 --watching it.
We have to close the server after each test, so add this inside describe() block

// server.test.js
...
 afterEach(() => {
    app.close();
  });
...
Enter fullscreen mode Exit fullscreen mode

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 by adding this test

// server.test.js
...
  it('finds our stampler', done => {
    request(app)
      .get('/')
      .expect(/Stampler/)
      .expect(200, done);
  });
Enter fullscreen mode Exit fullscreen mode

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
yarn add mongoose or npm install mongoose

Create a Product.js to define a new schema

// Product.js
var mongoose = require('mongoose');

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

module.exports = mongoose.model('Product', ProductSchema);
Enter fullscreen mode Exit fullscreen mode

And use it as it follows

// 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/'),
);

Enter fullscreen mode Exit fullscreen mode

Note that your MongoDB should be running 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]
Enter fullscreen mode Exit fullscreen mode

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 --watching 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.

// 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;
  });
...
Enter fullscreen mode Exit fullscreen mode

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

// 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);
    });
  });
});

Enter fullscreen mode Exit fullscreen mode

This probably isn't the optimal and most elegant way to test integration, but I'll leave the comments open for hints and suggestions

References

Hackernoon - API testing using SuperTest
Bits and Pieces- Build a Unit-Testing Suite with Mocha and Mongoose
Zellwk - Connecting Jest and Mongoose
SmoothTerminal - Build an API with Koa.js

Neon image

Build better on Postgres with AI-Assisted Development Practices

Compare top AI coding tools like Cursor and Windsurf with Neon's database integration. Generate synthetic data and manage databases with natural language.

Read more →

Top comments (0)

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

👋 Kindness is contagious

Explore a trove of insights in this engaging article, celebrated within our welcoming DEV Community. Developers from every background are invited to join and enhance our shared wisdom.

A genuine "thank you" can truly uplift someone’s day. Feel free to express your gratitude in the comments below!

On DEV, our collective exchange of knowledge lightens the road ahead and strengthens our community bonds. Found something valuable here? A small thank you to the author can make a big difference.

Okay