-
Notifications
You must be signed in to change notification settings - Fork 0
controllers
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.
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).
-
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. -
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:
- Create a folder called
dto
insrc/cats
. - Add the following 4 files to that folder:
- Create a folder called
// 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 {}
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.
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
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.
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.
http GET :3000/cats/abecd
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.
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
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')
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
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'
.
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.
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.
http GET :3000/cats/params/1/2
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
.
http POST :3000/cats/exp
http GET :3000/cats/exp
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:
- Routes that use the
async
keyword, if they return a value, should do so in a promise. In the next chapter, we'll see thefindAll()
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
.
- 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 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.
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.
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:
- generates a folder structure that organizes components in an orderly, standard fashion
- generates test files for components
- 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:
- creates the folder
src/dogs
- adds a template for a dogs controller in
src/dogs/dogs.controller.ts
- adds a template for a dogs unit test in
src/dogs/dogs.controller.spec.ts
- imports the generated
DogsController
class into the default app module (src/app.module.ts
). - 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.
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.
Next up is the Providers chapter.