Manual Reference Source Test

Advanced Usage

The advanced setup has some more options to setup your API in regards with the database connection and the port your API will be listening on. But also how you can serve content through built-in route controllers as well as how to extend the built-in route controllers.

Mongoose Models

For the advanced setup we will create a model using mongoose and a route controller extending from BaseContentController. Below we create a simple mongoose model.

// ./myModel.js
import mongoose, { Schema } from 'mongoose'

// Create a simple mongoose schema.
const mySchema =  new Schema({
  _id: {
    type: String,
    required: true,
    index: {
      unique: true
    }
  },
  slug: {
    type: String,
    required: true
  },
  name: {
    type: String,
    required: true
  }
})

// Create a model from the schema.
export default mongoose.model('MyModel', mySchema)

ContentService

Before we create a route controller we need a ContentService object which ats as the DAL for the CRUD operations. The ContentService object will be used as a parameter when creating the route controller.

// ./myService.js
import { ContentService } from 'pop-api'
import MyModel from './myModel'

const myService = new ContentService({
  Model: MyModel,           // The model for the service.
  projection: { name: 1 },  // Projection used to display multiple items.
  query: {}                 // (Optional) The default query to fetch items.
})

// Methods available:
// - myService.getContents([base])
// - myService.getPage([sort, page, query])
// - myService.getContent(id, [projection])
// - myService.createContent(obj)
// - myService.createMany(arr)
// - myService.updateContent(id, obj)
// - myService.updateMany(arr)
// - myService.deleteContent(id)
// - myService.deleteMany(arr)
// - myService.getRandomContent()

export default myService

Now we create a route controller which extends from BaseContentController. This route controller can be used on it's own or classes can extend from it to implement new behaviour or override existing behaviour. The BaseContentController class already implements the registerRoutes method from IController and adds the following routes to your API (note the base path will be taken from the basePath value of your route controller):

Controllers

The following example extends from BaseContentController, registers the default routes and implements a GET /hello route.

// ./MyRouteController.js
import { BaseContentController } from 'pop-api'

// Extend from the `BaseContentController` which has defaults methods for CRUD
// operations.
export default class MyRouteController extends BaseContentController {

  // The constructor of `BaseContentController` needs an instance of
  // `ContentService` which we will create later. It can also take additional
  // parameters for your own implementation.
  constructor({basePath, service, name}) {
    // binds: this.baseBath and this.service.
    super({basePath, service})

    this.name = name
  }

  // Implement the 'registerRoutes' method from the 'IController interface.
  registerRoutes(router, PopApi) {
    // Call the `registerRoutes` method from the `BaseContentController` class
    // to register the default routes.
    super.registerRoutes(router, PopApi)

    // And add additional routes for your route controller.
    router.get('/hello', this.getHello.bind(this))
  }

  // Router middleware to execute on the 'GET /hello' route.
  getHello(req, res, next) {
    return res.json({
      message: `Hello, ${this.name}`
    })
  }

}

Configuration

Now to initial your API we create a list of route controllers we want to register with their constructor parameters. This example also shows additional parameters to pass down to the init method of the PopApi instance.

// ./index.js
import express from 'express'
import { PopApi, ContentService } from 'pop-api'
import { join } from 'path'
import MyRouteController from './MyRouteController'
import myService from './myService'
import { name, version } from './package.json'

;(async () => {
  try {
    // Define the controllers you want to use.
    const controllers = [{
      Controller: MyRouteController,  // The controller to register.
      args: {                         // The arguments passed down to the
                                      // constructor of the controller.
        basePath: 'example',          // The base path to register the routes
                                      // to.
        service: myService,           // The content service for the
                                      // BaseContentController.
        name: 'John'                  // The additional arguments to pass to
      }                               // your route controller.
    }]

    // Initiate your API with optional parameters.
    await PopApi.init({
      app: express(),          // The express instance  to use.
      controllers,             // The controllers to register.
      name,                    // The name of your API.
      version,                 // The version of your API.
      logDir: join(...[        // (Optional) The directory to store the log
        __dirname,             // files in. Defaults to `./tmp`.
        '..',
        'tmp'
      ]),
      hosts: ['11.11.11.11'],  // (Optional) The hosts to connect to for
                               // MongoDB. Defaults to `['localhost']`.
      dbPort: 27019,           // (Optional) The port of MongoDB to connect to
                               // Defaults to `27017`.
      username: 'myUsername',  // (Optional) The username to connect to.
                               // MongoDB. Defaults to `null`.
      password: 'myPassword',  // (Optional) The password to connect to.
                               // MongoDB. Defaults to `null`.
      serverPort: 8080,        // (Optional) The port to run your API on.
                               // Defaults to `5000`.
      workers: 4               // The amount of workers to fork for the server.
                               // Defaults to `2`.
    }, [                       // (Optional) A list of middlewares to register.
      // Cli,                  // Defaults to:
      // Logger,               //  [Cli, Logger, Database, Routes, HtttpServer]
      // Database,
      // Routes,
      // HttpServer
    ])
    // API is available on port 8080.

    // GET http://localhost:8080/hello
    // { "message": "Hello, John" }

    // GET http://localhost:8080/examples
    // ["/examples/1', "/examples/2"]

    // GET http://localhost:8080/examples/1
    // [
    //   { "_id": "578df3efb618f5141202a196", "name": "John" },
    //   { "_id": "578df3efb618f5141202a196", "name": "Mary" }
    // ]

    // GET http://localhost:8080/example/578df3efb618f5141202a196
    // { "_id": "578df3efb618f5141202a196", "name": "John", "slug": "john" }

    // POST http://localhost:8080/examples
    // body: { "name": "Mary", "slug": "mary" }
    // { "_id": "578df3efb618f5141202a196", "name": "Mary", "slug": "mary" }

    // PUT http://localhost:8080/example/578df3efb618f5141202a196
    // body: { "name": "James", "slug": "james" }
    // { "_id": "578df3efb618f5141202a196", "name": "James", "slug": "james" }

    // DELETE http://localhost:8080/example/578df3efb618f5141202a196
    // { "_id": "578df3efb618f5141202a196", "name": "James", "slug:" :james" }

    // GET http://localhost:8080/random/example -> { }
    // { "_id": "578df3efb618f5141202a196", "name": "Mary", "slug": "mary" }
  } catch (err) {
    console.error(err)
  }
})()