Skip to content

controllers

John Biundo edited this page Aug 25, 2019 · 2 revisions

Controller Chapter

At this point we've used the Nest CLI to generate what's referred to as the "Typescript Starter" template project.

Assuming you want to follow along step by step, if you followed along with the First steps chapter of the docs, you can set that folder aside, and switch over to your cloned copy of nest-cats, and continue editing there. Check out the controllers-start branch, and continue at the following along section on this page to begin coding.

If you just want a final version of the project code as of the end of the chapter, check out the controllers-end branch, and proceed to the upon completion section.

Following along

This is the first chapter you really add any non-boilerplate code. The controllers-start branch is just a generic boilerplate project (including a few sample snippets) set up to begin adding custom code.

As you read the Controllers chapter of the docs, it covers a lot of basics and there are a lot of little "demo" code snippets. Feel free to try any of the code samples from the Controllers chapter on this controllers-start branch. They loosely build on each other, however, we'll end up replacing them with the complete code sample in the Full resource sample section near the end of the chapter to stay on track with our mainline nest-cats app.

In terms of "live coding" our nest-cats project, completing the Controllers chapter requires just two "chunks" of code from the chapter. Note that the code from the docs chapter implies a folder structure, so let's make that explicit before adding code; then you can continue with the steps below.

First, create a folder called cats under src.

Now, add code as follows, (or, as always, just skip ahead and simply check out the controllers-end branch which will reflect those changes).

  1. Create a file called src/cats/cats.controller.ts and add the routes shown in the Full resource sample section. That code snippet kind of bundles up all of the main concepts of the Controllers chapter.

  2. Create a set of DTO objects as described in the Request payloads section of the docs. The routes from the previous step (from the Full resource sample section) actually require 3 DTOs, which aren't fully defined in the docs. To complete this step, you should do the following:

    1. Create a folder called dto in src/cats.
    2. Add the following 4 files to that folder:
// create-cat.dto.ts
export class CreateCatDto {
  readonly name: string;
  readonly age: number;
  readonly breed: string;
}
// list-all-entities.ts
export class ListAllEntities {
  limit: number;
}
// update-cat.dto.ts
export class UpdateCatDto {
  readonly name: string;
  readonly age: number;
  readonly breed: string;
}
// index.ts
export * from './create-cat.dto';
export * from './update-cat.dto';
export * from './list-all-entities';

Finally, add the CatsController to the src/app.module.ts file. It should look like this:

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { CatsController } from './cats/cats.controller';

@Module({
  imports: [],
  controllers: [AppController, CatsController],
  providers: [AppService],
})
export class AppModule {}

Upon completion

If you've live-coded the chapter as described above, congrats! Proceed with the test routes below. Alternatively, you may want to just check out the controllers-end branch to run these tests.

HTTPie test routes

Paste these on the command line to test the routes created in this chapter.

Create a cat

http POST :3000/cats name=Fred age:=3 breed='Alley Cat'

Get cats, with limit

http GET :3000/cats?limit=10

Get the #1 cat

http GET :3000/cats/1

Update the #1 cat

http PUT :3000/cats/1

Delete the #1 cat

http DELETE :3000/cats/1

Controller Chapter Appendix

As mentioned, some chapters will have an appendix to cover little "code branches" that are part of the official docs, but that diverge from the main codeline of nest-cats. We may also, from time to time, cover a few topics in a bit more depth than they're covered in the main docs.

Checkout branch controllers-end-appendix to have your code ready to follow along with this appendix.

Wildcard Routes

The Controllers chapter described wildcard routes. This controllers-end-appendix branch has such a route in src/cats.controller.ts. Test it by requesting cats/abecd, or feel free to try different combinations of wildcard matching in the route and request.

HTTPie query for wildcard route

http GET :3000/cats/abecd

Status Codes

The Controllers chapter described customizing the HTTP status code. The controllers-end-appendix branch has such a route in src/cats.controller.ts. Test the route with the request below. Compare it to the response you get for http POST :3000/cats (which has default behavior). You may also want to try modifying the controller to return a 204, and note how that affects the response.

HTTPie route for status code route

http POST :3000/cats/2

Note the HTTP response code (200 OK):

HTTP/1.1 200 OK Connection: keep-alive Content-Length: 26 Content-Type: text/html; charset=utf-8 Date: Tue, 27 Aug 2019 17:42:19 GMT ETag: W/"1a-2akZlhd5h5eyBoEmpkMg7vz8ALY" X-Powered-By: Express
This action adds a new cat

HTTP Headers

The Controllers chapter described customizing HTTP Headers. This branch has such a route in src/cats.controller.ts. Test the route with the request below. You can also try setting other arbitrary headers by modifying the @Header() decorator, for example: @Header('Vary', 'User-Agent')

HTTPie route for http headers route

http POST :3000/cats/3

Note the Cache-Control header in the response:

HTTP/1.1 201 Created Cache-Control: none Connection: keep-alive Content-Length: 26 Content-Type: text/html; charset=utf-8 Date: Tue, 27 Aug 2019 17:44:26 GMT ETag: W/"1a-2akZlhd5h5eyBoEmpkMg7vz8ALY" X-Powered-By: Express
This action adds a new cat

Route Order

The Controllers chapter discusses the significance of route order. To see this in action, comment out the 'wildcard routes' route, and uncomment the second copy of it, labeled 'route order testing'. This effectively moves the wildcard route down below the first route that matches GET /cats/:id, which will match routes like GET /cats/abecd, mapping the string 'abecd' to parameter 'id'.

HTTPie route for route order test

Try this both before and after making the route order switch described above.

http GET :3000/cats/abecd

As described in the documentation, simply move the wildcard route up higher (revert the comment/uncomment change you just made) to resolve this.

Using @Param() decorator

The Controllers chapter discusses use of the @Param() decorator. There are two flavors: the sample code for the chapter demonstrates picking out an individual parameter using code like findOne(@Param('id') id: string). You can also get back an object containing all parameters by omitting the 'id' token portion of the decorator. This branch has such a route in src/cats.controller.ts. Test it with the request below. Note that params is an object with properties corresponding to each parameter, as indicated by the tokens in the route's @Get() decorator.

HTTPie route for @Param() decorator

http GET :3000/cats/params/1/2

Library-specific approach

The Controllers chapter points out the differences between standard and library-specific responses. This branch has examples of these in src/cats.controller.ts.

POST /cats/exp runs the Express-specific version of POST /cats. GET /cats/exp runs the Express-specific version of (a route similar to) GET /cats.

HTTPie routes for library-specific approach

http POST :3000/cats/exp http GET :3000/cats/exp

Other topics

Asynchronicity

We'll explore some details of async route handlers and routes returning observables in future chapters. You'll see in upcoming chapters that we begin to prepare for the asynchronous operations that typically happen in the service layer (e.g., database requests, network API calls, etc.) by making our route handlers asynchronous with the async keyword. The things to recognize now, even if you're not familiar with promises and async/await, are:

  1. Routes that use the async keyword, if they return a value, should do so in a promise. In the next chapter, we'll see the findAll() route, which is such an example. It looks like:
  @Get()
  async findAll(): Promise<Cat[]> {
    return this.catsService.findAll();
  }

Note the Promise<Cat[]> syntax. This indicates that the route is asynchronous, and returns a Promise that, when resolved, is an array of elements of type Cat.

  1. Nest automatically takes care of resolving the asynchronous result (the Promise), and returns the resolved value to the HTTP client.

As a result, the syntax shown above is all you need to do to call asynchronous services in Nest. This is considerably simpler than what you would typically do in a native Node/Express app.

Scopes

Scopes are a more advanced topic that deal with statefulness of your connections and services. As of now, I don't have any specific exercises to cover scopes, but hope to add some in the future.

DTO's

Data transfer objects (DTOs) seem like mostly overhead in this chapter. They seem like a somewhat complicated way to provide type safety. In the pipes chapter, we'll see how they can be combined with decorators, pipes and exception filters to perform powerful validations.

Bonus topics

Nest CLI and Good Code Structure

In First steps, you used the Nest CLI to scaffold a new app with something like nest new cats-companion. You can also use the CLI to add components to an existing project. Let's take a closer look.

Using the CLI to add components is handy because it:

  1. generates a folder structure that organizes components in an orderly, standard fashion
  2. generates test files for components
  3. wires components together in a default way

For example, let's say you're a dog lover. Try this. At the OS command line (while in your project top level folder), run nest generate controller dogs

You'll notice that this does the following:

  1. creates the folder src/dogs
  2. adds a template for a dogs controller in src/dogs/dogs.controller.ts
  3. adds a template for a dogs unit test in src/dogs/dogs.controller.spec.ts
  4. imports the generated DogsController class into the default app module (src/app.module.ts).
  5. sets a route prefix of 'dogs' in the DogsController

Notice also that the controller class name - DogsController is made by converting the name of the component you gave to the CLI (dogs) to Pascal Case (Dogs), and appending Controller, to result in DogsController.

We'll use the CLI in later chapters and see that it can simplify our lives by both generating boilerplate for us, as well as helping promote good file organization principals.

One take-away principal to notice now is that our dogs-related code is in its own folder. So going into the next chapter, the branch we'll start with will use this to organize the cats code a little better (notice the cats folder when you get there). We'll try to follow the best practice code organization promoted by the CLI and the official Nest examples as we move forward.

Nest CLI shorthand

Going forward, we'll use shorthand notation for the CLI. So nest generate controller dogs will become nest g co dogs. See Nest CLI for full documentation of the CLI.

What's next

Next up is the Providers chapter.