Create a CRUD REST API with Heroku and Spark, and a free database with mLab and KMongo

Florent Blot
11 min readFeb 4, 2019

Back your future with Kotlin

Inspired by Back To The Future

Prologue: follow Marty and Emmett in their previous adventure to install a server-app with Heroku and Spark.

TL;DR

Dr. Emmett Brown and Marty McFly are trapped in time. They should go to the cloud to restart the DeLorean and get back to their timeline. Will they succeed to easily create a server using Heroku, mLab, Spark and KMongo to go back home?

The DeLorean wouldn’t start, the engine stays silent. Dr. Brown, frustrated, gets out of the car, followed by Marty. While he walks a few steps, he shouts:

– Since we, and this car, aren’t in the cloud anymore, we cannot start the engine and go back in time. We’re stuck here! He takes a look at his young friend and asks, Marty, can you create a POC server?
– No, I can’t, answers the boy, I just develop front apps! Some of them was in Kotlin tho.

The old man seems stressed and frowns. His previous server is lost, the time-machine server broke. He quickly needs a new CRUD REST API and a database, to insert the DeLorean and himself into the cloud. He starts to pace back and forth. Suddenly, he stops, his look beyond the street.

Laying down, the doctor grips his friend’s arm:

– Don’t you know that you can create a server easily and use our application to restore the space-time continuum?
– Wait a minute, Doc. Ah… Are you telling me that I can build a server… with Kotlin?
– You need to see the big picture, Marty! Kotlin has the same bytecode as Java, therefore any server which works with a JVM, works with Kotlin. You’ve already done some front apps, haven’t you? Maybe some of them were in Kotlin or in Swift, which have a close syntax by the way. We can use your knowledge to code a backend service.
– Yes, right, I see your point, replies Marty, helping Emmett to stand up, but we have no money to buy an online server or a database, and I don’t know how to do so. We have to add us as pilots and the DeLorean as a car into the database quickly, to get back to our time!

Emmett walks promptly to the car and pulls out some handwritten notes from the glove box.

– We haven’t enough time but here, he says holding the papers out to Marty, you should read these notes. It’s just quick summary. Perhaps some of these should already be familiar to you.

To easily create a server-app, get a free cloud web hosting service which allows to deploy a server as a Java application like Heroku.In your server-app, use Spark to connect to your Heroku server and develop a REST API by setting the routes with it. Careful: don't import spark.kotlin instead of spark.Spark.Get a free MongoDB of 0.5Gb of space on AWS or Google Cloud, thanks to a "Database-as-a-Service" like mLab.Try KMongo to quickly create your calls to Mongo database in your server-app.Give a chance to Gson to convert readily your Mongo collections into JSON format and vice-versa.

After reading these small notes, the teenager seems a little lost. Noticing it,
the doctor holds him another paper with the title: “Kotlin: Start your future”. He wrote it from a previous adventure. It is crumpled but Marty could read the essential to install a server-app with Heroku and Spark. He remembers, this was the last project created (ed. named “time-machine-server”).

Unidentified Flying Object

– OK, starts the young man, it should be fine, where do I start, Doc?
– Did you read my last note above? It’s really important for the next steps. You should have a Procfile — which specifies the commands that are executed by the server-app on startup — and the stage task in your gradle — to let Heroku builds and starts it… Did you do that, Marty?
– Yes, I already did this. I’m ready. So what’s next?
– Since you’ve previously created a Gradle project and added Kotlin’s dependency, I suppose you’ve already put it into “time-machine-server”, says the doctor with confidence, we need to add our dependencies. Search for your build.gradle file and edit it by adding Spark, Slf4j — logger for Spark, KMongo and Gson.

dependencies {
...
compile 'com.sparkjava:spark-kotlin:1.0.0-alpha'
compile 'org.slf4j:slf4j-simple:1.7.12'
compile 'org.litote.kmongo:kmongo:3.9.0'
compile 'com.google.code.gson:gson:2.2.4' }

Marty sits quickly in the DeLorean and takes a mini computer out of his pocket. He opens the project “time-machine-server” and starts typing on the small keyboard to add the dependencies. The man with the white hair joins him into the car.

– As we saw in an earlier timeline, sums up the old man, you need an entry point. That’s the purpose of the file Main.kt that we created and we needed to set the port to listen to.

– We made it as a singleton with the keyword object, continues Dr. Brown. We get the port from Heroku and assign it to Spark. Wow… Where did you get this mini computer, Marty?
– In 1997, twenty minutes ago. Well, I mean, when we were in the DeLo…

A model to be followed!

Emmett quickly stops the young man, and pulling himself together, asks:

– Nevermind, what do we need to start the engine?
– We need a pilot and a car. Ah… I mean, you add a pilot into the database, with a name and its age, then you add a car… Doc, we got no time for that!
– Sure, so quickly, let’s see.
– I think I get it. We… we just have to create two data classes PilotModel and CarModel, right?
– Marty, you read my mind!

data class PilotModel(val _id: String, 
val name: String?,
val age: Int?)
data class CarModel(val _id: String,
val model: String?,
val company: String?)

– Don’t forget, mentions the doctor, MongoDB creates automatically a space collection for our model into database, no need to create a table like in SQL. Each entry — item’s collection — generates a unique id.

A controller to rule them all

Dr. Brown carries on:

– We will manage all routes, requests and responses into ApiController. Start by creating this new class and call it into the main function.

– Spark, continues Dr. Brown, uses this following scheme: a verb (get, post, head, trace…), a path and a callback. Therefore, a GET request is as follows:

get("/route") { request, response -> "message" }

The young partner learns that this example above sends the String “message” back (the callback) when we call https://time-machine-server.herokuapp.com/route(the path) from the HTTP method GET (the verb).

He noticed that the callback has request and response data as parameters, and if he doesn’t use any of them, he could replace the argument by _.

– Let’s do this, Marty! Create your main routes inside ApiController. You will create a GET request for your root server and for a subdomain too, the route/api/. I let you decide if you want to add some custom features if you want.

– Great, you wrote it fast! But let me explain a few things here:

  • path(): useful when you have a lot of routes, you can separate them into groups. It gives you a scope for routes and filters (or nested paths).
  • before(): this is where your middlewares can be called. Each request passing by /api/ should do something like checkAuthenticate() before going any further.
  • after(): same thing as before(), however it occurs just before responding to a request. In the class above, we set the response type to JSON. Notice that this type is only set for requests inside /api/’s path.
  • halt(): to immediately stop a request within a route. As an example, the user is not logged, so we abort the request and return a 401 status and a custom message.
  • notFound(), internalServerError(): to customize the response when the resource does not exist or the server has an internal error.

– You can find them in Spark’s documentation. There is a lot more to use. Now, continues the doctor, stopping Marty in his thoughts, we need to create our routes according by models.

All routes lead to Rome…

– Usually, thinks Marty out loud, we set all things up to manage the models, like saving, showing or deleting them. These routes in this PilotRoute should correspond, shouldn’t it?

The Dr. Brown smiles and says:

– That’s it, Marty, you just easily defined the endpoints for the pilot. In these functions, we will use a Data Access Object (DAO) to access the database via KMongo. Do the same for car’s endpoints, just copy-paste this class and change its name for CarRoute
– Wow, this is easy, you were right, says Marty with stars in his eyes. But, something’s missing. How do I call it from our controller?
– Good question, and simple answer: just add their path into /api/’s path.

While the teenager appears euphoric, his old friend is already thinking about the next steps.

Like a mongoose in the sea

– We need to create another object which should be connected to the database by an URI, provided by mLab. Marty, carries on the doctor, you should create two functions inside it, which should be able to connect() and getting the database().

– Our future DAOs will use this object to retrieve or send our models as Mongo collection. In order to work, you have to create a Mongo client and passing the URI into it. mLab gives you this URI when you create a database and a user. We use a local variable named DB_URI above, and we will set it as below:

private const val DB_USER = ""
private const val DB_PASS = ""
private const val DB_NAME = ""
private const val DB_URI = "mongodb://$DB_USER:$DB_PASS@ds00000.mlab.com:00000/$DB_NAME"

Back to the DAOs

– Do you remember, in September… Curious, I’ve already heard these words…
– Doc, can we focus, please? How can I do the DAOs ?
– Yes, Sorry, you should create a class named PilotDao.

The old man explains that this class should handle all routes previously defined and returning either an object filled from database or a custom message. It should perform tasks as creating a pilot into database, retrieving, updating or deleting one.

Following the advice of Dr. Brown, Mr. McFly creates the method getCollection() to access to database using his MongoDb object and retrieve a specific collection like PilotModel.

private fun getCollection() =
MongoDb.getDatabase().getCollection<PilotModel>()

Finally, using Gson, the young man creates the extension toJson() on any objects, to convert a Pilot into a string in JSON format. And, he writes toModel() which is a function that returns a Pilotfrom a JSON string.

private fun Any?.toJson() = toGson(this)private fun <R> toGson(r: R): String =
GsonBuilder().setPrettyPrinting().create().toJson(r)
private fun String?.toModel(): PilotModel {
val type = object: TypeToken<PilotModel>(){}.type
return GsonBuilder().create().fromJson(s, type)
}

– Link this DAO into your PilotRoute, adds the old doctor.

– Do the same as before, Marty, take this DAO above and copy-paste to CarDao, change the model for CarModel, copy-paste PilotRoute as CarRoute and change the DAOs accordingly, and so on.

I see duplicate, I see duplicate everywhere!

Mr. McFly suddenly stops being enthusiastic and stares at the doctor with a serious look in his eyes:

– There’s a little matter we have to talk about.
– Hum? Everything works as expected. “Time-machine-server” is running and works fine. It answers what we wa…
– No, Doc, there is something ugly here. We just duplicate our classes. The endpoints use the same functions, and the DAOs are the exactly the same, only the model changes. This is absolutely disgusting.

– Great Scott, you’re right, kid! Give me the mini computer please, I’ll put all DAOs methods inside a BaseDao and use generic type for the different collections.

– To get a generic model from a Mongo collection, goes on Dr. Brown, we need to do a reflection by using KMongoUtil from KMongo. I remember, I saw it on a newspaper. This should be something like these two methods below.

protected fun getCollection(): MongoCollection<T> =
getDaoModelClass().let { k ->
MongoDb.getDatabase().getCollection(
KMongoUtil.defaultCollectionName(k), k.java)
}
@Suppress("UNCHECKED_CAST")
private fun getDaoModelClass(): KClass<T> =
((this::class.java.genericSuperclass as
ParameterizedType).actualTypeArguments[0] as Class<T>).kotlin

– Then we modify our Gson customization to handle generics.

protected fun Any?.toJson() = toGson(this)private fun <R> toGson(r: R): String =
GsonBuilder().setPrettyPrinting().create().toJson(r)
protected fun String?.toModel(): T = Gson().fromJson(this)private fun Gson.fromJson(json: String?): T =
this.fromJson<T>(json, object : TypeToken<T>() {}.type)

– See? I changed getCollection(), toJson() and toModel() to protected functions to use them from the children. Finally, I’ll just have to extend it for every DAO children.

class PilotDao : BaseDao<PilotModel>()

class CarDao : BaseDao<CarModel>()

Focused and attentive, Marty looks at his old friend coding the same for the routes and gathers all similar functions into BaseRoute.

– And right now, says the doctor, I just have to do the same as before: extend this new base to its children by passing its associate DAO and the specific model for the collection.

class PilotRoute : BaseRoute<PilotDao, PilotModel>(PilotDao())class CarRoute : BaseRoute<CarDao, CarModel>(CarDao())

“Great Scott, we did it!” shouts Dr. Brown. He jumps out of the DeLorean, shaking his arms in all direction. Overexcited, he runs a bit and comes back to the car. The boy goes out next, smiling and astounded by the sparkle of his old friend.

The man with dishevelled hairs hugs the young boy and, approching the mini computer to his face, says:

– Now you can create some specific routes for a particular model and avoid usual duplicate functions. For example, Marty, if you want to retrieve minor pilots, but not affect car’s endpoints, you add it only in the specific classes:

class PilotRoute : BaseRoute<PilotDao, PilotModel>(PilotDao()) {
override fun getRoutes() {
super.getRoutes()
get("/minors/") { _, _ -> dao.getMinorPilots() }
}
}
class PilotDao : BaseDao<PilotModel>() {
...
fun getMinorPilots(): String = getCollection()
.find(PilotModel::age lt 18)
.into(mutableListOf<PilotModel>())
.toJson()
}

– Oh, and you just have to connect to the url/pilots/minors/ . It’s awesome, Doc!

In the end

The two acolytes push all their code on Heroku. After a few seconds, the screen displays a successful deployment.

Connecting to the server-app thanks to a front application, like Postman, they succeed to insert into the database the car as { "model": "DMC-12", "company": "DMC"}. Then, they add themself into the database { "name": "Marty", "age": 17 } and { "name": "Doc", "age": null }.

While they close the doors of the car, ready to go, Marty turns to Dr. Brown and asks confused:

– Doc, why… Why didn’t you insert your age?
– You don’t have to know that. I don’t want to talk about it. I’ll explain it in another adventure… Maybe… Not.
– What? Do you mean you’re old like very old, very old?

Marty could not hide a little smile on his face when he teased his friend. Without a word, Emmett turns the starting key, and prefered to avoid any eye contact. The car starts! They laugh, the pressure on their shoulders is definitively gone. The DeLorean steps forward in the street, its engine whirring.

A handwritten note, pushed by wind, lands on the ground:

Warning: 
In case of a space-time continuum's bug, build your own time-machine-server with a server-app to insert new pilots and new cars into a remote database.
- Dr. Emmett Brown
See the source: https://github.com/gitdefllo/back-your-future

--

--

Florent Blot

Maker, Mobile developer, Kotlin & Dart enthusiast, IoT & DIY addict. @Geev: https://www.geev.com