Using a Database

In this guide we will implement the location service by creating a database to store our locations.

Generating a Database Access Class

First, let's generate the class that will access the database:

$ bb generate database -d location

If you open up the generated database class in LocationDatabase you will see the following:

import {database} from 'blackbox-database'
import Location from './Location';
import {named} from 'blackbox-ioc';

@database()
@named('location-database')
export default class LocationDatabase {
    async getLocation(_name:string): Promise<Location> {return <Location>{}}
    async getLocationList(): Promise<Location[]> {return <Location[]>[]}
    async createLocation(_location: Location) {}
    async updateLocation(_name: string, _location: Location) {}
    async deleteLocation(_name: string) {}
}

You may notice that the methods of the class have no meaningful body. This is because the @database() annotation will replace the class prototype's methods with generated database access logic.

The @named annotation creates an instance of the class that can be used elsewhere in your code, such as in your service implementation.

The class' method names follow a specific pattern that tells the @database annotation how to generate the method body. Method names should be written according to the following regular expression: ^(get|create|update|delete)${typeName}(ListBy|ListWithQuery|List|By)?([A-Z][A-Za-z0-9_]*)?$ where ${typeName} is the name of the type either specified in the @database annotation parameters if provided or in the class name preceding the word 'Database' otherwise.

Setting Up the Database

Currently the blackbox-database package only supports MongoDB. To setup the MongoDB database you must use the configureDatabase() function which can be imported from the blackbox-database package. It takes a single configuration parameter with url field for specifying the MongoDB URL, and a dbname field for specifying the name of the database. For guidance on setting up a MongoDB database see the MongoDB Getting Started documentation. We will not cover the setup of the database in this guide, but you will need to setup a database named bb and run it on localhost on port 27017.

In our case we will setup the database in the index.ts file. Open index.ts and add the following import:

import {configureDatabase} from 'blackbox-database'

Then, find the call to initRootServices() and add the following on a new line after it:

configureDatabase({
    url: 'mongodb://localhost:27017',
    dbname: 'bb'
})

We now have a database setup and a class for accessing it. Next, we will implement the Location service to use the database.

Connect the Service to the Database

The LocationServiceImpl class includes a service method named getLocationList. This will be called when we hit the /location endpoint with a GET request. It should return an array of Location objects.

First, add an autowired reference to the LocationDatabase class:

@autowired('location-database')
db: LocationDatabase

Then load the location array from the database:

getLocationList(props:any): Promise<Location[]> {
    return this.db.getLocationList()
}

Notice that we had to change the return type of the method since the database access class operates asynchronousely.

Now, make sure your database is up and running, then rebuild and restart the server:

$ npm run build && npm run start

If you make a get request to /location you should now receive an empty array read from your database.

[]

Create and Read from the Database

The final thing we will do in this guide is implement the other Location services. Add a body to the createLocation() method as follows:

createLocation({location}:any): Promise<void|NamedReference> {
    return this.db.createLocation(location)
    .then(() => {name: location.name})
}

The parameter passed to the method include req (a request object), verbose which is true if the verbose query parameter was included in the URL, and the object to be created, which in this case is named location. We are only interested in the Location object, so we extract that from the method parameter.

The returned object must include a name identifying the created entity.

Rebuild and restart. Then, using a tool such as Postman, post a location object to the /location end-point, for example:

{
    "name": "Hobart",
    "coordinates": {
        "longitude": 42.8821,
        "latitude": 147.3272
    }
}

Make a GET request to /location and should now see our newly created location in the returned array.

The other methods all correspond to the /location/{name} endpoint and can be implemented as follows:

getLocation({name}:any): Promise<Location> {
    return this.db.getLocation(name)
}

replaceLocation({location}:any): Promise<void|NamedReference> {
    return this.db.updateLocation(location.name, location)
    .then(() => {name: location.name})
}

updateLocation({location}:any): Promise<void|NamedReference> {
  return this.db.getLocation(location.name)
  .then((oldLocation) => this.db.updateLocation(location.name, Object.assign(oldLocation, location)))
  .then(() => {name: location.name})
}

deleteLocation([name]:any): Promise<void|NamedReference> {
  return this.db.deleteLocation(name)
  .then(() => {name})
}

These methods respectively correspond to HTTP methods as follows: GET, PUT, PATCH and DELETE. Gives these a try, for example you could use the location we created previously at /location/Hobart.