HTTP Controllers

Exposing a HTTP endpoint is common task for most backend developers. Let’s make

Files will be organised as per our domain desgin conventions.


Exposing a RESTful service

Create a Controller

In this example we would create the file under src/domain/users/UsersController.ts

@injectable allows our controller to be injected when our server.ts.

@controller(path, middleware?) creates a binding to the router and additionally allows for middleware to be applied to this controller context only.

// src/domain/users/UsersController.ts
import { injectable } from 'inversify'
import { controller, interfaces } from 'inversify-koa'
import Router from 'koa-router'

@injectable()@controller('/users')export class UsersController implements interfaces.Controller {
  // HTTP methods
}

Register the binding

Once our controller is created we need to register in with our IoC container located in config/container.ts.

// src/config/container.ts
...
container.bind<interfaces.Controller>(TYPE.Controller)
  .to(UsersController)
  .whenTargetNamed('UsersController')

Read more about documentation on how dependency injection works in Enso.

Constructor injection

This controller will be responsible for performing some basic CRUD functionality in the users domain.

For this problem we will be using the following classes to provide the functionality.

  • creating a user; UserFactory
  • reading user information; UserRepository
  • updating/deleting user/s; UserService
// src/domain/users/UsersController.ts
...
  constructor (
    private factory: UserFactory,
    @inject($b.UserRepository) private users: IUserRepository,
    private service: UserService
  ) {}
...

Create (POST /users/create)

Annotate the registerUser method with @httpPost(path, middleware?).

TODO: You learn how to validte the data HERE TODO: You learn how to validte the data HERE TODO: You learn how to validte the data HERE

// src/domain/users/UsersController.ts
...
  @httpPost('/create')
  async registerUser (ctx: Router.IRouterContext) {

    const user = await this.factory.registerUser(ctx.data)

    ctx.status = 201
    ctx.body = {
      message: 'User registered'
    }
  }
...

Read (GET /users/:uuid)

Annotate the readUser method with @httpGet(path, middleware?).

Notice how url params can be tokenised.

// src/domain/users/UsersController.ts
...
  @httpGet('/:uuid')
  async readUser (ctx: Router.IRouterContext) {

    const userUuid = ctx.params.uuid    const user = await this.users.browseByUuid(uuid)

    ctx.status = 200
    ctx.body = {
      message: 'ok',
      data: serialiseUserReponse(user)
    }
  }
...

Update (PUT /users/:uuid)

Annotate the updateUser method with @httpPut(path, middleware?).

  • Notice how url params can be tokenised.
  • Use a our previously injected service to update the record.
// src/domain/users/UsersController.ts
...
  @httpPut('/:uuid', validate(UpdateUserRequest))
  async updateUser (ctx: Router.IRouterContext) {

    const user = await this.service.updateUser(ctx.params.uuid, ctx.data)
    ctx.status = 200
    ctx.body = {
      message: 'ok',
      data: serialiseUserReponse(user)
    }
  }
...

Delete (DELETE /users/:uuid)

Annotate the deleteUser method with @httpDelete(path, middleware?).

// src/domain/users/UsersController.ts
  @httpDelete('/:uuid', validate(DeleteUserRequest))
  async deleteUser (ctx: Router.IRouterContext) {

    const user = await this.service.updateUser(ctx.params.uuid, ctx.data)

    ctx.status = 200
  }
...

Next

  • Learn how to leverage Middleware to validate request data