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