Skip to main content

Controllers

Developers coming from MVC will know what is a controller and how it helps us to handle the request and response lifecycle.

RestController Introduction

For elegant handling we have added a base RestController which has all the necessary functions to handle the response transformation. While creating your controller, remember to extend RestController.

Check below to know how to create a controller.

src/user/controllers/UserController.ts
import { RestController, Request, Response } from '@app/core';
import { Controller, Get, Req, Res } from '@nestjs/common';
import { UserService } from '../services';
import { UserDetailTransformer } from '@app/transformer';

@Controller('users')
export class UserController extends RestController {
constructor(private users: UserService) {
super();
}

@Get('/profile')
async getProfile(
@Req() req: Request,
@Res() res: Response,
): Promise<Response> {
return res.noContent();
}
}

RestController Methods

transform()

You might have noticed in the previous snippets, on how we created an instance of the transformer and ran the methods to process an output. To handle it more beautifully, you can handle it like below:

src/user/controllers/UserController.ts
import { RestController, Request, Response } from '@app/core';
import { Controller, Get, Req, Res } from '@nestjs/common';
import { UserService } from '../services';
import { UserDetailTransformer } from '@app/transformer';

@Controller('users')
export class UserController extends RestController {
constructor(private users: UserService) {
super();
}

@Get('/profile')
async getProfile(
@Req() req: Request,
@Res() res: Response,
): Promise<Response> {
const user = await this.users.get();
return res.success(
await this.transform(user, new UserDetailTransformer, { req })
);
}
}

Notice, the {req} object that we are passing as third param, the base RestController itself handles reading the url and parsing the includes. Now we don't need to do the tedious task again and again. 😃

collection()

Same responsibility as the transform method. Use this whenever you need to transform an array.

paginate()

Breaks down the obj passed into transformed data and pagination data. Expects the obj to include pagination key.

Bonus

If you are dealing with some other key than pagination, you can change the key name inside paginate method in src/core/controllers/RestController.ts file.

Route Decorators

This boilerplate comes packed with few route helpers, which you can use to make your operations easy and beautiful.

WithAlias

You can use the decorator to give an alias/name to your route.

import { RestController, Request, Response, WithAlias } from '@libs/core';
import { Controller, Get, Req, Res } from '@nestjs/common';

@Controller('users')
export class UserController extends RestController {

@Get('/profile')
@WithAlias('auth.profile')
async getProfile(
@Req() req: Request,
@Res() res: Response,
): Promise<Response> {
// ... your logic
return res.success('Success');
}
}

Now, to access the route, you can simply do

route('auth.profile')
// https://localhost:5000/users/profile

the above mentioned code will return the fully qualified url of the route. In this case, https://localhost:5000/users/profile

For dynamic routes which includes path parameters, you can pass path variables as second argument.

@Get('/books/:slug')
@WithAlias('books.detail')
async getDetail(
@Req() req: Request,
@Res() res: Response,
): Promise<Response> {
// ... your logic
return res.success({name: 'Shoe Dog', author: 'Phil Night'});
}

// now, simply do
route("books.detail", {name: "the-blue-umbrella-by-ruskin-bond"})
// https://localhost:5000/books/the-blue-umbrella-by-ruskin-bond

Extra paramters, passed will be returned as query params

route("books.detail", {name: "the-blue-umbrella-by-ruskin-bond", include:"price"})
// https://localhost:5000/books/the-blue-umbrella-by-ruskin-bond?include=price

Request

We know how tedious it can get to fetch all the input params from a request object. Nobody wants to use the req.body, req.query, req.params seperately.

This boilerplate provides a decent method to get all the input params at once.

all()

Method to get the incoming parameters, all at once. No need to get body, query, and params seperately every time.

const inputs = req.all();
note

all() method automatically trims all strings received in the request.

Response

We have our own very generous set of api guidelines, we used our learning of multiple projects to provide structural consistency of response object (which is often unknowningly abused).

success()

Returns the success response, use this whenever your request is succesfull, usually for GET and POST requests.

Params

nameTypeDefault
dataObject--
statusNumber200
/**
* {
* success: true,
* code: 200,
* data: { message: "Hello There" }
* };
**/
return res.success({ message: 'Hello there!' });

error()

Returns the error response, used in exception filter.

Params

nameTypeDefault
errorObject or string--
statusNumber401
/**
* {
* success: false,
* code: 401,
* message: 'Unauthorized'
* };
**/
return res.error('Unauthorized!');

noContent()

There are cases when we need to send only a success status code and no body. noContent() will come in handy for you.

// will return a response with status code of 204
return res.noContent();

withMeta()

There can be some case where we need to send some payload to support our requested data. For example, pagination information incase of a paginated response. withMeta helps you achieve the same.

Params

nameTypeDefault
dataObject--
statusNumber200
/**
* {
* success: true,
* code: 200,
* data:[{},{},{}]
* meta: {
* pagination: {
* totalPages: 10,
* currentPage: 2,
* },
* ...some other custom attributes
* }
* };
**/
return res.withMeta({
data: [{}, {}],
pagination: { totalPages: 10, currentPage: 2 },
});