InteractiveFrameworks

First Steps

Boot a Nest application from one module, one controller and one provider, read what Nest prints, and return your first JSON response.

What you'll learn

  • Recognise the four files every Nest application starts from and what each declares
  • Register a provider in a module so Nest can inject it into a controller
  • Read Nest's dependency error and its boot log to see what was and was not wired

Nest is a framework for building backends in TypeScript on top of Node. Underneath it uses Express, the same library that answers most Node web requests, but it adds the thing Express leaves to you: structure. Every Nest application is a graph of classes, each with one job, and Nest builds the graph for you from the decorators on those classes. This course teaches that graph, one piece per lesson, in a real Nest application running in your browser.

The four files

main.ts is the entry point. It creates the application from a root module and starts listening:

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  await app.listen(3000);
}
bootstrap();

NestFactory.create reads the root module and wires up everything the module declares, recursively. app.listen starts the HTTP server. In this browser runtime the port is a formality, but the call is still required: it is what tells the grader your application is ready.

app.module.ts is the root module. A module is a class with an @Module() decorator listing what belongs together:

@Module({
  imports: [],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}
  • controllers handle incoming requests.
  • providers are the classes Nest can inject into other classes: services, repositories, helpers.
  • imports brings in other modules. You will meet it in lesson 4.

app.controller.ts maps routes to methods. @Controller() with no argument means "the root path", and @Get() on a method means "GET on that path":

@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Get()
  getHello() {
    return { message: this.appService.getHello() };
  }
}

app.service.ts holds the logic. @Injectable() marks the class as something Nest may construct and hand to whoever asks for it.

How the service reaches the controller

Look at the controller's constructor: constructor(private readonly appService: AppService). That parameter is a request. When Nest creates AppController it reads the parameter's type, finds a provider registered for AppService, and passes the instance in. Nobody writes new AppService(). This is dependency injection, and it is the idea the rest of this course builds on: classes declare what they need, and the module decides what they get.

It only works if the provider is registered somewhere Nest can see. Leave AppService out of providers and Nest refuses to start:

Nest can't resolve dependencies of the AppController (?).
Please make sure that the argument AppService at index [0] is available in the AppModule context.

Read that error closely once, because you will meet it for the rest of your Nest career. AppController is the class being built. (?) stands in for the argument Nest could not find, and "at index [0]" says it is the first constructor parameter. "Available in the AppModule context" names the module whose providers array needs the entry.

Returning JSON

A route handler that returns an object or an array sends it as JSON, with Content-Type: application/json. A handler that returns a string sends it as plain text. Real APIs return objects, so this course does too.

Reading the console

Press Run tests and Nest prints the same lines nest start prints on your machine: [NestFactory] Starting Nest application..., one [InstanceLoader] line per module it initialises, one [RouterExplorer] Mapped {/, GET} route line per route it finds, and [NestApplication] Nest application successfully started. Then the grader calls your endpoints and reports each one. When something does not work, the console is where the answer is: a route that is not mapped is a route Nest never saw.

Your task

Two things are missing from the starter code:

  1. AppModule does not register AppService, so the controller cannot be built.
  2. AppController.getHello() returns a placeholder instead of the service's greeting.

Fix both and run. Run once after fixing only the first, and compare what the console prints with the error above.

When it fails

  • Nest can't resolve dependencies of the AppController (?): the provider is not in the module's providers array.
  • The route answers {"message":"TODO"}: the handler still returns the placeholder. The grader prints what it got next to what it expected.
  • A red line naming a file and a line number is a TypeScript error. Nest never starts an application that does not compile, and neither does this editor.

Remember

  • A module lists the classes that belong together: controllers answer requests, providers do the work.
  • A constructor parameter with a class type is a request to Nest, and Nest fulfils it from the module's providers.
  • Return an object and Nest sends JSON.
Stuck? Show a hint

AppController asks for AppService in its constructor, so AppService has to be in the module's providers array. Then getHello() should return whatever the service's getHello() gives, inside { message }.