Skip to main content

Command Palette

Search for a command to run...

#4 — Bootstrapping: main.ts & AppModule

Published
4 min read
M

As a former 3D Animator with more than 12 years of experience, I have always been fascinated by the intersection of technology and creativity. That's why I recently shifted my career towards MERN stack development and software engineering, where I have been serving since 2021.

With my background in 3D animation, I bring a unique perspective to software development, combining creativity and technical expertise to build innovative and visually engaging applications. I have a passion for learning and staying up-to-date with the latest technologies and best practices, and I enjoy collaborating with cross-functional teams to solve complex problems and create seamless user experiences.

In my current role as a MERN stack developer, I have been responsible for developing and implementing web applications using MongoDB, Express, React, and Node.js. I have also gained experience in Agile development methodologies, version control with Git, and cloud-based deployment using platforms like Heroku and AWS.

I am committed to delivering high-quality work that meets the needs of both clients and end-users, and I am always seeking new challenges and opportunities to grow both personally and professionally.

1) Big Picture: How Nest boots

(main.ts)  →  (AppModule)  →  (Your Feature Modules)
  • main.ts bootstraps the application.

  • It creates a Nest instance from AppModule.

  • AppModule is the root module that imports your feature modules (Users, Posts, Auth, …).


2) Start the dev server

Use the built-in script (watch mode):

npm run start:dev

You’ll see logs like:

[Nest] xxxx  - ...  [InstanceLoader] AppModule dependencies initialized
[Nest] xxxx  - ...  Nest application successfully started

Open http://localhost:3000 to confirm.

Tip: If you see EADDRINUSE 3000, another process is using port 3000. Stop it or change the port (see §4).


3) main.ts: the bootstrap file

A default main.ts looks like this:

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

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

What’s happening

  • NestFactory.create(AppModule) builds the application graph from the root module.

  • app.listen(3000) starts the HTTP server (Express by default; Fastify if you configured it).

Changing the port

await app.listen(3300); // now it serves on :3300

Hot-reload will restart the server; open http://localhost:3300.

Peeking at the app object (for learning)

console.log(app); // a large object describing your app graph

⚠️ It’s huge; use sparingly. Later we’ll explore app.get(...) to resolve providers.


4) AppModule: the root wiring hub

Open src/app.module.ts (or src/app/app.module.ts if you nest it). A minimal version:

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

@Module({
  imports: [],            // other modules go here
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

Key idea: The @Module() decorator makes a class a Nest module — the filename is just a convention.

  • imports: other modules this module depends on

  • controllers: HTTP route handlers

  • providers: injectable classes (services, guards, pipes, interceptors, repositories)

  • exports: which providers to expose to other modules

Every feature module you create must eventually be added to some module’s imports (often AppModule initially), or it won’t exist in the app graph.


5) Organizing files under src/app/

By default, the CLI puts app files directly under src/. Many teams prefer:

src/
  app/
    app.module.ts
    app.controller.ts
    app.service.ts
  main.ts

If you move files into src/app/, fix the import in main.ts:

// from
import { AppModule } from './app.module';
// to
import { AppModule } from './app/app.module';

If the path is wrong, Nest will fail to start with an import error.


6) What do the generated files do?

app.controller.ts

  • Declares routes (e.g., GET /) using decorators like @Controller() and @Get().

  • Delegates the actual work to a service.

app.service.ts

  • Holds business logic used by controllers.

  • Example returns "Hello World!" (or your custom message).

app.controller.spec.ts

  • Unit tests for the controller. *.spec.ts is purely a naming convention; test frameworks pick them up by glob.

Separation of concerns: controller = I/O + routing; service = business logic. This pays off as the app grows.


7) v11 note: Express 5 by default

Nest v11 upgrades the default HTTP platform to Express 5 (performance & type improvements). Most apps will “just work”, but if you have custom middleware/types, re-check them after upgrading. Staying on v10 for learning is also fine.


8) Common hiccups & fixes

  • Port won’t open: another service is running → change port or stop the other process.

  • Module not found: incorrect path after moving files → fix the import in main.ts.

  • Feature not reachable: forgot to add your feature module to some module’s imports.

  • TS errors after upgrades: delete node_modules + lockfile and reinstall.


9) Hands‑on tasks (do these now)

  1. Change the port to 3300, verify in the browser, then change back to 3000.

  2. Create src/app/ and move app files there; fix the AppModule import in main.ts.

  3. Edit AppService to return "Hello from NestJS" and confirm it appears at /.

  4. (Stretch) Log process.env.NODE_ENV in bootstrap() and pass it to your app via cross-env NODE_ENV=dev npm run start:dev.


10) Quick check (1‑minute quiz)

  1. What two steps does main.ts usually perform?

  2. Which part of a module lists other modules it depends on?

  3. True/False: Naming a file *.controller.ts automatically makes it a controller.

Answers: create app from AppModule and listen(port); imports; False — the @Controller() decorator does.