Building microservices with Dropwizard, MongoDB & Docker

Building microservices with Dropwizard, MongoDB & Docker

In this tutorial I will create a microservice using Dropwizard, this framework provides all kind of libraries to build a web application. Some of the libraries that include are:

Dropwizard making magic. 

In this tutorial I will create a microservice using Dropwizard, this framework provides all kind of libraries to build a web application. Some of the libraries that include are:

Jetty Server: this is an open-source web server, lightweight and easy to embed in any application.

Jersey: implementation for creating RESTful web services.

Jackson: allows you manipulation from JSON objects to POJOs and vice versa.

Metrics: provides information about the state of HTTP requests, database connections, queues, etc.

Guava: support with many classes to deal with collections, validations, strings etc.

Hibernate Validator: validations as constrains for java objects.

JDBI: wraps JDBC and provides a flexible way to manipulate relational databases.

Liquibase: perfect to migrations and DDL changes in a database.

MongoDB a NoSQL database and much more.🌳

This NoSQL database oriented to documents (by documents like JSON) combines some of the features from relational databases, easy to use and the multi-platform is the best option for scale up and have fault tolerance, load balancing, map reduce, etc.

Technologies used in this microservice 📝

  • OpenJDK 8
  • Docker 2.0.0.0-mac81 (version for Mac)
  • MongoDB 4.0
  • Maven 3.5
  • Mac OS mojave (or any other like windows or linux)
  • Nginx 1.15
  • IntelliJ IDEA 2018

Ok let's start to create the project, Dropwizard provide us a maven archetype that we can use from its web site, here a example of this command:

mvn archetype:generate -DarchetypeGroupId=io.dropwizard.archetypes -DarchetypeArtifactId=java-simple -DarchetypeVersion=1.3.5 -DgroupId=com.demo -DartifactId=dropwizard-mongodb-ms -Dversion=1.0.0-SNAPSHOT -Dname=DropwizardMongoDBMicroservice

Open a terminal and paste the before the command, make sure you have installed and configurated maven and java.

The final structure looks like this:

dropwizard-mongodb-ms/
├── README.md
├── config.yml
├── pom.xml
└── src
    ├── main
    │   ├── java
    │   │   └── com
    │   │       └── demo
    │   │           ├── DropwizardMongoDBMicroserviceApplication.java
    │   │           ├── DropwizardMongoDBMicroserviceConfiguration.java
    │   │           ├── api
    │   │           ├── cli
    │   │           ├── client
    │   │           ├── core
    │   │           ├── db
    │   │           ├── health
    │   │           └── resources
    │   └── resources
    │       ├── assets
    │       └── banner.txt
    └── test
        ├── java
        │   └── com
        │       └── demo
        │           ├── api
        │           ├── client
        │           ├── core
        │           ├── db
        │           └── resources
        └── resources
            └── fixtures

Once the project was created, add the dependencies in the pom.xml file:

. . .
<properties>
        <dropwizard.version>1.3.5</dropwizard.version>
        <mainClass>com.demo.DropwizardMongoDBMicroserviceApplication</mainClass>
        <mongodb.version>3.8.2</mongodb.version>
        <jdk.version>1.8</jdk.version>
        <dropwizard.swagger.version>1.0.6-1</dropwizard.swagger.version>
        <mockito.core.version>2.23.0</mockito.core.version>
</properties>
. . .
<dependency>
    <groupId>io.dropwizard</groupId>
    <artifactId>dropwizard-core</artifactId>
</dependency>
<dependency>
    <groupId>org.mongodb</groupId>
    <artifactId>mongodb-driver-sync</artifactId>
    <version>${mongodb.version}</version>
</dependency>

<dependency>
<groupId>com.smoketurner</groupId>
<artifactId>dropwizard-swagger</artifactId>
<version>${dropwizard.swagger.version}</version>
</dependency>

<!-- Testing -->
<dependency>
<groupId>io.dropwizard</groupId>
<artifactId>dropwizard-testing</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>${mockito.core.version}</version>
<scope>test</scope>
</dependency>
<!-- Testing -->
. . .

I added some configurations to the file configuration.yaml, looks like this:

server:
maxThreads: 512
applicationContextPath: /dropwizard-mongodb-ms
applicationConnectors:
- type: http
port: 8080
adminConnectors:
- type: http
port: 8081

logging:
level: INFO
loggers:
com.demo: INFO

#You can choose the user and password what you want.
mongoDBConnection:
credentials:
username: "user_donuts"
password: "pAsw0Rd"
seeds:
- host: "mongodb"
port: 27017
database: "donuts"

swagger:
basePath: /dropwizard-mongodb-ms
resourcePackage: com.demo.resources
scan: true
info:
version: "1.0.0"
title: "Donuts API CRUD"
description: "A simple API used for expose CRUD operation on MongoDB collection"
termsOfService: "http://swagger.io/terms/"
contact:
name: "Donuts API "
license:
name: "Rich Lopez"

The configuration.yml maps to a class that extends of io.dropwizard.Configuration and looks like this:

package com.demo;

import com.demo.db.configuration.MongoDBConnection;
import com.fasterxml.jackson.annotation.JsonProperty;

import io.dropwizard.Configuration;
import io.federecio.dropwizard.swagger.SwaggerBundleConfiguration;

public class DropwizardMongoDBMicroserviceConfiguration extends Configuration {

/**
 * The data configuration for MongoDB.
 */

private MongoDBConnection mongoDBConnection;

@JsonProperty("swagger")
private SwaggerBundleConfiguration swaggerBundleConfiguration;

//Getters and setters

}

This microservice will expose an API REST base on a CRUD (Create, Read, Update, Delete) I've chosen a product like donuts (🍩 🤤), some parts of the code you can find in my Github repository at the end of this tutorial.

I'm using the com.mongodb.client.MongoCollection interface for access to the collection called donuts to have more granularity in the operations so I created a DAO (Data Access Object) to manipulate the CRUD operations.

package com.demo.db.daos;
//imports ...
public class DonutDAO {

/** The collection of Donuts */
final MongoCollection&lt;Document&gt; donutCollection;

/**
 * Constructor.
 *
 * @param donutCollection the collection of donuts.
 */
public DonutDAO(final MongoCollection&lt;Document&gt; donutCollection) {
    this.donutCollection = donutCollection;
}

/**
 * Find all donuts.
 *
 * @return the donuts.
 */
public List&lt;Donut&gt; getAll() {
    final MongoCursor&lt;Document&gt; donuts = donutCollection.find().iterator();
    final List&lt;Donut&gt; donutsFind = new ArrayList&lt;&gt;();
    try {
        while (donuts.hasNext()) {
            final Document donut = donuts.next();
            donutsFind.add(DonutMapper.map(donut));
        }
    } finally {
        donuts.close();
    }
    return donutsFind;
}

/**
 * Get one document find in other case return null.
 *
 * @param id the identifier for find.
 * @return the Donut find.
 */
public Donut getOne(final ObjectId id) {
    final Optional&lt;Document&gt; donutFind = Optional.ofNullable(donutCollection.find(new Document("_id", id)).first());
    return donutFind.isPresent() ? DonutMapper.map(donutFind.get()) : null;
}

public void save(final Donut donut){
    final Document saveDonut = new Document("price", donut.getPrice())
                                  .append("flavor", donut.getFlavor());
    donutCollection.insertOne(saveDonut);
}

/**
 * Update a register.
 *
 * @param id the identifier.
 * @param donut the object to update.
 */
public void update(final ObjectId id, final Donut donut) {
    donutCollection.updateOne(new Document("_id", id),
            new Document("$set", new Document("price", donut.getPrice())
                    .append("flavor", donut.getFlavor()))
    );
}

/**
 * Delete a register.
 * @param id    the identifier.
 */
public void delete(final ObjectId id){
    donutCollection.deleteOne(new Document("_id", id));
}

For map the information that comes from the database I use a utility class that filters the fields from MongoDB to a POJO.

package com.demo.util;

import com.demo.api.Donut;
import org.bson.Document;

public class DonutMapper {

/**
 * Map objects {@link Document} to {@link Donut}.
 *
 * @param donutDocument the information document.
 * @return A object {@link Donut}.
 */
public static Donut map(final Document donutDocument) {
    final Donut donut = new Donut();
    donut.setId(donutDocument.getObjectId("_id"));
    donut.setFlavor(donutDocument.getString("flavor"));
    donut.setPrice(donutDocument.getDouble("price"));
    return donut;
}

}

Then create a POJO to manipulate the information on MongoDB.

package com.demo.api;
//imports ...
public class Donut implements Serializable {

/** The id.*/
@JsonSerialize(using = ObjectIdSerializer.class)
private ObjectId id;

/** The price. */
@NotNull
private double price;

/** The principal flavor.*/
@NotNull
private String flavor;

/**Constructor.*/
public Donut() {
}

//getters & setters
//hashcode, equals and toString methods

This class is only used in response to the endpoint.

package com.demo.api;
//imports ...
public class Response {
/** The message.*/
private String message;

/** Constructor.*/
public Response() {
}

//getters & setters
//hashcode, equals and toString methods

Here more utility classes for convert org.bson.types.ObjectId to Stringobjects using Jackson.

package com.demo.util;

import java.io.IOException;

import org.bson.types.ObjectId;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;

public class ObjectIdSerializer extends JsonSerializer<ObjectId> {
@Override
public void serialize(final ObjectId objectId, final JsonGenerator jsonGenerator,
final SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeString(objectId.toString());
}
}

For convert String object to array of char.

package com.demo.util;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;

import java.io.IOException;

public class PasswordSerializer extends JsonSerializer<String> {
@Override
public void serialize(final String input, final JsonGenerator jsonGenerator,
final SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeString(input.toCharArray(), 0, input.toCharArray().length);
}

@Override
public Class&lt;String&gt; handledType() {
    return String.class;
}

}

Use these POJOs that map information with the configuration.yml.

package com.demo.db.configuration;

import java.util.Arrays;
import java.util.Objects;

import com.demo.util.PasswordSerializer;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;

/**

  • This class is used for credentials.

  • @version 1.0.0

  • @since 1.0.0

  • @author Rich Lopez
    */
    public class Credentials {

    /** The user name.*/
    private String username;

    /** The password.*/
    @JsonSerialize(using = PasswordSerializer.class)
    private char[] password;

    //getters, setters, hashcode and equals methods.
    }
    package com.demo.db.configuration;

import java.util.Objects;

public class Seed {

/** The host.*/
private String host;

/** The port.*/
private int port;
//getters, setters, hashcode and equals methods.

}
package com.demo.db.configuration;

import java.util.List;

public class MongoDBConnection {

/**
 * The credentials user and password.
 */
private Credentials credentials;

/**
 * The lis of seeds.
 */
private List&lt;Seed&gt; seeds;

/**
 * The db.
 */
private String database;

}

For manage the connection with MongoDB we need map the information come from the yaml file configuration to this classes com.demo.db.configuration.Credentials , com.demo.db.configuration.MongoDBConnection, com.demo.db.configuration.Seed.

The class com.demo.db.MongoDBFactoryConnection creates the client for MongoDB with the options that provided in yaml file, the method for create the instance look like:

package com.demo.db;
//imports ...
public class MongoDBFactoryConnection {

/** The configuration for connect to MongoDB Server.*/
private MongoDBConnection mongoDBConnection;

/**
 * Constructor.
 *
 * @param mongoDBConnection the mongoDB connection data.
 */
public MongoDBFactoryConnection(final MongoDBConnection mongoDBConnection) {
    this.mongoDBConnection = mongoDBConnection;
}

/**
 * Gets the connection to MongoDB.
 *
 * @return the mongo Client.
 */
public MongoClient getClient() {
    LOGGER.info("Creating mongoDB client.");
    final Credentials configCredentials = mongoDBConnection.getCredentials();

    final MongoCredential credentials = MongoCredential.createCredential(
            configCredentials.getUsername(),
            mongoDBConnection.getDatabase(),
            configCredentials.getPassword());

    final MongoClient client = MongoClients.create(
            MongoClientSettings.builder()
                    .credential(credentials)
                    .applyToClusterSettings(builder -&gt; builder.hosts(getServers())).build()
    );

    return client;
}

/**
 * Map the object {@link Seed} to objects {@link ServerAddress} that contain the information of servers.
 *
 * @return the list of servers.
 */
private List&lt;ServerAddress&gt; getServers() {
    final List&lt;Seed&gt; seeds = mongoDBConnection.getSeeds();
    return seeds.stream()
            .map(seed -&gt; {
                final ServerAddress serverAddress = new ServerAddress(seed.getHost(), seed.getPort());
                return serverAddress;
            })
            .collect(Collectors.toList());
}

In Dropwizard life cycle the object can be manage by the interface io.dropwizard.lifecycle.Managed and the class io.dropwizard.lifecycle.MongoDBManaged.

package com.demo.db;
//imports ...
public class MongoDBManaged implements Managed {

/** The mongoDB client.*/
private MongoClient mongoClient;

/**
 * Constructor.
 * @param mongoClient   the mongoDB client.
 */
public MongoDBManaged(final MongoClient mongoClient) {
    this.mongoClient = mongoClient;
}

@Override
public void start() throws Exception {
}

@Override
public void stop() throws Exception {
    mongoClient.close();
}

Another important class is the Healthcheck, for creating this class is only required to extend from com.codahale.metrics.health.HealthCheck and implement the method check.

package com.demo.health;
//imports ...
public class DropwizardMongoDBHealthCheck extends HealthCheck {
/** A client of MongoDB.*/
private MongoClient mongoClient;

/**
 * Constructor.
 *
 * @param mongoClient the mongo client.
 */
public DropwizardMongoDBHealthCheck(final MongoClient mongoClient) {
    this.mongoClient = mongoClient;
}
@Override
protected Result check() {
    try {
        final Document document = mongoClient.getDatabase("donuts").runCommand(new Document("buildInfo", 1));
        if (document == null) {
            return Result.unhealthy("Can not perform operation buildInfo in Database.");
        }
    } catch (final Exception e) {
        return Result.unhealthy("Can not get the information from database.");
    }
    return Result.healthy();
}

The entry point of this microservice is the class com.demo.DropwizardMongoDBMicroserviceApplication loads all bundles configurations, initialization classes, etc. This class must extend from io.dropwizard.Application and is parametrized by a class that extends of io.dropwizard.Configuration.

package com.demo;

import com.demo.db.MongoDBFactoryConnection;
import com.demo.db.daos.DonutDAO;
import com.demo.db.MongoDBManaged;
import com.demo.health.DropwizardMongoDBHealthCheck;
import com.demo.resources.DonutResource;

import io.dropwizard.Application;
import io.dropwizard.setup.Bootstrap;
import io.dropwizard.setup.Environment;
import io.federecio.dropwizard.swagger.SwaggerBundle;
import io.federecio.dropwizard.swagger.SwaggerBundleConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class DropwizardMongoDBMicroserviceApplication extends Application<DropwizardMongoDBMicroserviceConfiguration> {

/**
 * Logger class.
 */
private static final Logger LOGGER = LoggerFactory.getLogger(DropwizardMongoDBMicroserviceApplication.class);

/**
 * Entry point for start Application.
 *
 * @param args the args.
 * @throws Exception when the app can not start.
 */
public static void main(final String[] args) throws Exception {
    LOGGER.info("Start application.");
    new DropwizardMongoDBMicroserviceApplication().run(args);
}

@Override
public String getName() {
    return "DropwizardMongoDBMicroservice";
}

@Override
public void initialize(final Bootstrap&lt;DropwizardMongoDBMicroserviceConfiguration&gt; bootstrap) {
    bootstrap.addBundle(new SwaggerBundle&lt;DropwizardMongoDBMicroserviceConfiguration&gt;() {
        @Override
        protected SwaggerBundleConfiguration getSwaggerBundleConfiguration(
                    final DropwizardMongoDBMicroserviceConfiguration dropwizardMongoDBMicroserviceConfiguration) {
            return dropwizardMongoDBMicroserviceConfiguration.getSwaggerBundleConfiguration();
        }
    });
}

@Override
public void run(final DropwizardMongoDBMicroserviceConfiguration configuration,
                final Environment environment) {

    final MongoDBFactoryConnection mongoDBManagerConn = new MongoDBFactoryConnection(configuration.getMongoDBConnection());

    final MongoDBManaged mongoDBManaged = new MongoDBManaged(mongoDBManagerConn.getClient());

    final DonutDAO donutDAO = new DonutDAO(mongoDBManagerConn.getClient()
            .getDatabase(configuration.getMongoDBConnection().getDatabase())
            .getCollection("donuts"));

    environment.lifecycle().manage(mongoDBManaged);
    environment.jersey().register(new DonutResource(donutDAO));
    environment.healthChecks().register("DropwizardMongoDBHealthCheck",
            new DropwizardMongoDBHealthCheck(mongoDBManagerConn.getClient()));
}

}

For building the project using the command:

$ mvn clean package

Before to start the application is required to have the database, with MongoDB could you download from the website or using a docker image in DockerHub.

Once download the image of MongoDB, start a new container:

$ docker run --name mongodb -d -p 27017:27017 mongo

NOTE: Ensure the creation of a volume to maintain the persistence of information.

enter the container and type the next commands:

$ docker exec -it [container name or id] /bin/bash

$ mongo

$ use donuts

> db.createUser({ user: "user_donuts", pwd: "pAsw0Rd", roles: [ { role: "readWrite", db: "donuts"} ]});

By default, MongoDB sets the authentication mechanism to SCRAM.

And now start the application with the command:

$ java -jar target/dropwizard-mongodb-ms-1.0.0-SNAPSHOT.jar server configuration.yml

Once we have created the user and the database, we can enter the swagger documentation in the following URL:

http://localhost:8080/dropwizard-mongodb-ms/swagger

Using docker compose 🐳

One of the reasons for use docker every day is because provide us with a unique environment and all developers keep the dependencies, databases, web servers, etc synchronized. The easy way to build and deploy artifacts reduce delivery time.

I’m using docker for start the application and the MongoDB server, besides I add Nginx proxy for receiving the request, this is an example of docker compose file:

version: '3'
services:
mongodb:
image: mongo
restart: always
container_name: mongodb
environment:
MONGO_INITDB_ROOT_USERNAME: admin
MONGO_INITDB_ROOT_PASSWORD: admin
ports:
- 27017:27017
networks:
- dropw-mongodb-ntw

nginx:
image: nginx
container_name: nginx
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
ports:
- "8080:80"
- "443:443"
networks:
- dropw-mongodb-ntw

dropw-ms:
image: openjdk:8-jre
container_name: dropw-ms
volumes:
- ./target/dropwizard-mongodb-ms-1.0.0-SNAPSHOT.jar:/microservice/dropwizard-mongodb-ms-1.0.0-SNAPSHOT.jar
- ./configuration.yml:/microservice/configuration.yml
working_dir: /microservice
command: ["java", "-jar", "dropwizard-mongodb-ms-1.0.0-SNAPSHOT.jar", "server", "configuration.yml"]
ports:
- "8090:8080"
- "8091:8081"
networks:
- dropw-mongodb-ntw

networks:
dropw-mongodb-ntw:
external:
name: dropw-mongodb-ntw

We create the docker network with the command :

docker network create dropw-mongodb-ntw

build the project using the command:

$ mvn clean package

Start docker compose:

$ docker-compose up

once we have created all the services we need to create the user for the database, we will access the container with the following commands.

$ docker exec -it [container name or id] /bin/bash

$ mongo

> use donuts

> db.createUser({ user: "user_donuts", pwd: "pAsw0Rd", roles: [ { role: "readWrite", db: "donuts"} ]});

http://localhost:8080/dropwizard-mongodb-ms/swagger

Conclusion

Dropwizard provides us with several tools to create Microservices in a way easy. You can integrate with other technologies like MongoDB, Docker, Nginx by example. Remember that all depends on the requirements and the problem to solve, but for me, this is good for start and share experiences.


By :Ricardho


WordPress in Docker. Part 1: Dockerization

WordPress in Docker. Part 1: Dockerization

This entry-level guide will tell you why and how to Dockerize your WordPress projects.

This entry-level guide will tell you why and how to Dockerize your WordPress projects.

List all containers in Docker(Docker command)

List all containers in Docker(Docker command)

We can get a list of all containers in docker using `docker container list` or `docker ps` commands.

We can get a list of all containers in docker using docker container list or docker ps commands.

List Docker Containers

To list down docker containers we can use below two commands

  • docker container list
  • docker ps

docker container ls command introduced in docker 1.13 version. In older versions we have to use docker ps command.

List all Containers in docker, using docker ls command

The below command returns a list of all containers in docker.

docker container list -all

or

docker container ls -all

List all containers in docker, using docker ps command

In older version of docker we can use docker ps command to list all containers in docker.

$ docker ps -all

or

$ docker ps -a

List all Running docker containers

The default docker container ls command shows all running docker containers.

$ docker container list

or

$ docker container ls

or

To get list of all running docker containers use the below command

$ docker ps

List all stopped docker containers command

To get list of all stopped containers in docker use the below commands

$ docker container list -f "status=exited"

or

$ docker container ls -f "status=exited"

or you can use docker ps command

$ docker ps -f "status=exited"

List all latest created docker containers

To list out all latest created containers in docker use the below command.

$ docker container list --latest

Show n last created docker containers

To display n last created containers in docker use the below command.

$ docker container list --last=n

Dockerize a Nodejs app connected to MongoDb

Dockerize a Nodejs app connected to MongoDb

I will run through some basic Docker terminology and concepts and then use a Node.js and MongoDB that I previously built and demonstrate how to run this in a Docker container.

Please find first part of series: 

Dockerize a Node.js app with VS Code

Problem:

You already know how to use Docker together with Node from previous article in this series. I know that we all love MERN/MEAN stacks. Our next step is to understand how Node and Mongo connects to each other running inside containers. Lets’ go!

1. Install MongoDb locally

Time to get into some document db stuff. First of all download MongoDb server from here.

If you haven’t change anything during install, it should also install a thing called MongoDb Compass Community.

This is a great tool to inspect, change, add or remove data in collections in MongoDb. You can connect to the local instance by using default address and port like on this image below or connect to any other server.

To connect locally just press connect. Inside you can see some default collections and you can play around. We will need MongoDb Compass a bit later.

2. Connect to MongoDb through Express app

In this tutorial I will be using my favorite editor Visual Studio Code. You will also need Nodejs and Docker installed. In my case I’m using Windows, so I got Docker for Windows from here.

Now run following command:

mkdir test-mongo-app && cd test-mongo-app && npm init -y && code .

Time to install dependencies. We will need express and mongoose packages.

npm i express mongoose

Create file called server.js inside root folder.

Also don’t forget to change your package.jsonto run server.js file at start.

{
 "name": "test-mongo-app",
 "version": "1.0.0",
 "description": "",
 "main": "server.js",
 "scripts": {
 "start": "node server.js"
 },
 "keywords": [],
 "author": "",
 "license": "ISC",
 "dependencies": {
 "express": "4.17.1",
 "mongoose": "5.6.1"
 }
}

Good. Let’s create a basic express app with two routes. One for reading Usersfrom the database and second is for adding dummy user data to it.

First of all check if everything works with just express server.

// server.js
const express = require("express");
const app = express();
const PORT = 8080;
app.get("/", (req, res) => {
 res.send("Hello from Node.js app \n");
});
app.listen(PORT, function() {
 console.log(`Listening on ${PORT}`);
});

You can run npm start to test it. If you see the message ”Listening on 8080” everything is ok. Also open http://localhost:8080 and check if you can see the hello message.

There is a nice thing called nodemon. It auto rebuilds our project when any changes happened in source code. Let’s use it! 😀

npm install — save-dev nodemon

Add a new command in package.json. So we use it for development.

 "scripts": {
 "start": "node server.js",
 "dev": "nodemon server.js"
 },

Now use run npm run dev while development instead of npm start.

npm run dev

You will notice difference in the console, because now nodemon is watching for any changes in your project and if needs, rebuild it. Change something in server.js and you will notice 😉

Now create folder src in root of the project. Here we will add all the rest files.

Let’s create a User model for mongoose. Create file names User.model.js

// User.model.js
const mongoose = require("mongoose");
const userSchema = new mongoose.Schema({
 username: {
 type: String
 }
});

const User = mongoose.model("User", userSchema);

module.exports = User;

Good! Here we defined a model for our document db. User model has only one field username which is a string. Enough for now :)

Let’s add a file called connection.js for connection to the database.


// connection.js
const mongoose = require("mongoose");
const User = require("./User.model");
const connection = "mongodb://localhost:27017/mongo-test";
const connectDb = () => {
return mongoose.connect(connection);
};
module.exports = connectDb;

Please notice that mongo-test will be the name of our database (cluster)!

Now modify a bit server.js and start the app. You should see message in the console that MongoDb is connected.


// server.js
const express = require("express");
const app = express();
const connectDb = require("./src/connection");
const PORT = 8080;
app.get("/users", (req, res) => {
res.send(“Get users \n”);
});
app.get("/user-create", (req, res) => {
res.send(“User created \n”);
});
app.listen(PORT, function() {
console.log(Listening on ${PORT});
connectDb().then(() => {
console.log("MongoDb connected");
});
});

Yeah! 🎉 We connected Express app with local MongoDb instance!

3. Implement read and write to MongoDb

We should implement two routes for reading and adding new users.

Open server.js file and first of all import our model on the top:


// server.js
const User = require("./src/User.model");
// …

Then implement both routes below like this:

// server.js
app.get("/users", async (req, res) => {
const users = await User.find();
res.json(users);
});
app.get("/user-create", async (req, res) => {
const user = new User({ username: "userTest" });
await user.save().then(() => console.log(“User created”));
res.send("User created \n");
});
// …

Be attentive here we are using async/await pattern.

Basically we implemented two routes /users and /user-create. ✋ Yeah, yeah, I know that create should be done through the POST http verb but just to make testing easier and escape configuring seed method for db.

Now it’s time to test! 🔍 Open in browser this link http://localhost:8080/user-create to create a dummy user record in db. Open this link http://localhost:8080/users to get all users as JSON in browser.

After doing that you can go back to MongoDb Compass and check users collection here. You should see this

4. Dockerize Node and MongoDb

Add Docker file to the root folder.

touch Dockerfile

Paste following inside it:

FROM node:8

Create app directory

WORKDIR /usr/src/app

Install app dependencies

COPY package*.json ./

RUN npm install

Copy app source code

COPY . .

#Expose port and start application
EXPOSE 8080
CMD [ "npm", "start" ]

We can simply build our express app with this command

docker build -t mongo-app .

But.. this will only run our express app, but not together with MongoDb. That’s why we need a docker-compose file. 🐳

Now create another file called docker-compose.yml and paste this:


version: "2"
services:
web:
build: .
ports:
— "8080:8080"
depends_on:
— mongo
mongo:
image: mongo
ports:
— "27017:27017"

We defined 2 services in this file. One is our node app running on port 8080 and the other is mongodb instance.

⚠️ Before you run next command, please make sure that you changed connection string to mongo db in connection.js file.

const connection = "mongodb://mongo:27017/mongo-test";

We replaced localhost with mongo which is very important. Because we should tell the app that we want to access MongoDb from docker internal virtual network and not the local one.

Now run the magic command 🔮

docker-compose up

Open a browser on http://localhost:8080/users and http://localhost:8080/user-create to see our app running in Docker.

(In case anything doesn’t work, try to stop/remove image and containers, rebuild it by ruining docker compose-up again and if mongo image is not pulled from hub try to re-login in into docker hub or restart docker for Windows)

Thanks for reading !

🚀 If you read something interesting from that article, please like and follow me for more posts. Thank you dear codern and share it with all of your programming buddies!

Further reading

☞ Build a Basic App with Spring Boot and JPA using PostgreSQL

☞ Build a Simple CRUD App with Spring Boot and Vue.js

☞ Introducing TensorFlow.js: Machine Learning in Javascript

☞ An illustrated guide to Kubernetes Networking

☞ Google Kubernetes Engine By Example

☞ AWS DevOps: Introduction to DevOps on AWS

☞ Docker Tutorial for Beginners

☞ Kotlin Microservices With Micronaut, Spring Cloud, and JPA



Originally published on https://itnext.io