Insert Data in MongoDB with Spring Boot - Building a Blog
Tags: codenewbie, learning, java, mongodb
Published: 2020-08-06 09:12:29

Last time, we set up our database and Spring Boot project, and secured our database credentials. Now, we have to configure MongoDB in our project. Then we can start to insert data in mongodb and getting a feel for the queries to get the data from the database.

## Configuring Spring Boot to Access MongoDB
To get started, we need to configure our Spring application, so our application knows where the database is to begin with. We do that by adding the following lines to the [`application.properties`](https://github.com/Rosaa-xd/rosafiore/blob/mongodbConnection/blog-backend/src/main/resources/application.properties) file in the resources directory.

```.properties
spring.data.mongodb.host=localhost
spring.data.mongodb.port=27017
spring.data.mongodb.database=rosafiore
spring.data.mongodb.authentication-database=admin
```

When configuring MongoDB, we added a user to our database. This user uses the admin database to authenticate, and thus to access the other databases. That's why we need to define the authentication database, rather than just inputting the credentials to the database we will be talking to.

However, we still have to insert our secured credentials. Truth be told, it took a long time to get this one figured out. There was no clear resource online that I could find. So, after a lot of digging and trying, I have managed to create a mongo configuration that inserts the secured credentials and creates a connection to the database.

### MongoConfig
So, let's get started with this new config file. First off, we need to create a new class in our project. I call it [`MongoConfig`](https://github.com/Rosaa-xd/rosafiore/blob/mongodbConnection/blog-backend/src/main/java/eu/rosafiore/blogbackend/config/MongoConfig.java), because it contains the configuration for MongoDB. We have to annotate the class with the `@Configuration` and `@EnableMongoRepositories`. That way, Spring Boot knows that this is a configuration class, and thus must be taken along during initialization and to enable the repositories. Furthermore, we need to inherit the `AbstractMongoClientConfiguration` for a couple of methods to complete our configuration. So, our bare config class now looks like this:

```java
@Configuration
@EnableMongoRepositories
public class MongoConfig extends AbstractMongoClientConfiguration {

    @Override
    protected String getDatabaseName() {
        return null;
    }
}
```

In this class, we will create our own MongoClient to insert data in mongodb. For this, we need the properties we've defined in our `application.properties` file. We can access the properties by using a spring annotation on our local variable: `@Value("${<property name>}")`. With this construction, we create our variables with our property values and use them for configuring our client. One such variable will look like this in code:

```java
@Value("${spring.data.mongodb.database}")
private String database;
```

As you might have seen a bit before, the MongoConfig class inherited the method `getDatabaseName()`. There, we have to replace the `null` value with the `database` variable. Now, we need to dive in to configuring the MongoClient itself.

### MongoClientSettings
After searching for some time, I came across [the mongodb documentation](https://mongodb.github.io/mongo-java-driver/3.12/driver/tutorials/connect-to-mongodb/) for connecting to a database. Further down the page, I finally found what I was looking for: applying custom settings. I decided to create a separate private method to configure the settings to keep things tidy. Here is the code for my settings and I'll walk you through the code as we go.

```java
private MongoClientSettings setMongoClientSettings() {
    MongoCredential credential = MongoCredential.createCredential(
        Config.USERNAME, authDatabase, Config.PASSWORD);

    return MongoClientSettings.builder()
            .credential(credential)
            .applyToClusterSettings(builder ->
                    builder.hosts(Arrays.asList(new ServerAddress(host, port))))
            .build();
}
```

I start off with defining the credentials and the authentication database for my client. That is where I get to use my secured database credentials, instead of hardcoding them.

Then comes the `MongoClientSettings.builder()`, that is where the actual settings are created. The first thing I do, is setting the credentials to the one I created earlier in the method. Next, I need to configure the location of the database in the `applyToClusterSettings`. Here, I set the host to a new ServerAddress with the host and port number. Finally, the building can create the settings and return those. The settings are then used to create the MongoClient for Spring Boot to use.

```java
@Override
@Bean
public MongoClient mongoClient() {
    return MongoClients.create(setMongoClientSettings());
}
```

So, the MongoConfig class now looks like this:

```java
@Configuration
@EnableMongoRepositories
public class MongoConfig extends AbstractMongoClientConfiguration {

    @Value("${spring.data.mongodb.host}")
    private String host;

    @Value("${spring.data.mongodb.port}")
    private int port;

    @Value("${spring.data.mongodb.database}")
    private String database;

    @Value("${spring.data.mongodb.authentication-database}")
    private String authDatabase;

    @Override
    protected String getDatabaseName() {
        return database;
    }

    @Override
    @Bean
    public MongoClient mongoClient() {
        return MongoClients.create(setMongoClientSettings());
    }

    private MongoClientSettings setMongoClientSettings() {
        MongoCredential credential = MongoCredential.createCredential(
            Config.USERNAME, authDatabase, Config.PASSWORD);

        return MongoClientSettings.builder()
                .credential(credential)
                .applyToClusterSettings(builder ->
                        builder.hosts(Arrays.asList(new ServerAddress(host, port))))
                .build();
    }
}
```

## Insert Data in MongoDB

With our configuration completed, it is time to set up the rest of the code to insert data in mongodb. For this, we first need to create a class that is basically the blueprint of our object. In my case, I want to start out with a simple blog post object and the data I expect in it.

### BlogPost Model

As I explained in [Choosing a Database: SQL or NoSQL?](https://dev.to/shadowphoenix/building-a-blog-choosing-a-database-sql-or-nosql-1bea), my blogpost object contains the following properties:
- EndPoint
- Publish Date
- Categories
- Tags
- Blog Post (the content)

I reflected these properties in my [BlogPost class](https://github.com/Rosaa-xd/rosafiore/blob/mongodbConnection/blog-backend/src/main/java/eu/rosafiore/blogbackend/blogpost/BlogPost.java) which looks like this (I removed the getters and setters to avoid clutter):

```java
@Document(collection = "blogposts")
public class BlogPost {

    @Id
    private String documentId;
    @Indexed(unique = true)
    private String endPoint;
    private Date publishDate;
    private List<String> categories;
    private List<String> tags;
    private String article;
}
```

As you can see, I annotated the class with the `@Document` annotation. That allows me to choose the name of the collection where mongodb will be storing my blog posts. Furthermore, I have created a `documentId` with the `@Id` annotation to inform mongodb that this property will contain the ID for every blog post object stored in the database. Finally, I have added the `@Indexed(unique = true)` annotation to my EndPoint property. This annotation allows me to have mongodb check if the property is actually unique. I don't want duplicate endpoints for my blog posts after all!

### BlogPost Repository and Controller

With the model complete, it is time to insert the first blog post in the database. For this, we need a Repository first. Fortunately, this is a relatively simple one, I only need to create the interface and that is about it for now.

With that, the [BlogPostRepository](https://github.com/Rosaa-xd/rosafiore/blob/mongodbConnection/blog-backend/src/main/java/eu/rosafiore/blogbackend/blogpost/BlogPostRepository.java) looks like this:

```java
@Repository
public interface BlogPostRepository extends MongoRepository <BlogPost, Long> {
}
```

That is it for the repository, now we can continue with the [controller](https://github.com/Rosaa-xd/rosafiore/blob/mongodbConnection/blog-backend/src/main/java/eu/rosafiore/blogbackend/blogpost/BlogPostController.java). The controller contains the endpoints for our requests. I've made this a very simple controller, because it is still a proof of concept for me. I created two methods: one for posting a blog post and one for getting a blog post. For the POST method, I have hardcoded the values for the blog post.

In order to insert data into mongodb, I'll be using MongoTemplate which I only have to autowire into the application like this:

```java
@Autowired
private MongoTemplate mongoTemplate;
```

With the mongoTemplate autowired, creating the POST and GET method was relatively easy and straightforward.

```java
@PostMapping
    public void createPost() {
        BlogPost blogPost = new BlogPost();
        blogPost.setEndPoint("/test");
        blogPost.setPublishDate(new Date());
        blogPost.setCategories(Arrays.asList("TestCategory"));
        blogPost.setTags(Arrays.asList("TestTag"));
        blogPost.setArticle("testPost.md");

        mongoTemplate.save(blogPost, "blogposts");
    }

    @GetMapping
    public List<BlogPost> getPost() {
        Query query = new Query();
        query.addCriteria(Criteria.where("endPoint").is("/test"));
        return mongoTemplate.find(query, BlogPost.class);
    }
}
```

Of course, the only way to know for sure, is to test it. So, I went into Postman, inserted the requests and finally, after a long days struggle I get the following response when trying to obtain the blog post I had inserted.

![Getting my test post back from the database](https://dev-to-uploads.s3.amazonaws.com/i/imqv48hzyqizwdsp83i0.png)

With that, I have finally managed to insert data in mongodb! Now, I can start with expanding the backend code with more endpoints. Furthermore, I need to decide how to proceed with this project and what steps I will take next.