In this article, we’ll learn about NestJS, Vite, and esbuild; how they work together; and how to configure a NestJS app to make use of both other tools as dependencies. In the process, we will get to learn how to work with them in real life scenarios, their major features, and use cases.
Jump ahead:
NestJS is a Node.js framework for building efficient and scalable enterprise server-side/backend applications. According to the documentation, it supports all latest ECMAScript versions of both JavaScript and TypeScript.
NestJS combines the well-known programming concepts and philosophies of OOP, functional programming, and functional reactive programming to solve the architectural challenges in the design of backend applications that are scalable, maintainable, easily testable, and are not tightly coupled together.
Although NestJS is platform independent and can work with any Node.js library if a binding is written for it, NestJS uses Express as a dependency by default, and can also be configured to use Fastify. This ease in configuring the framework via exposed APIs with other third-party modules makes it very easy for developers to customize the framework on a case-by-case basis.
Vite is a build tool with lots of features, chief of which is a near-instant dev server startup time. It leverages the introduction of native ES modules in the browser and tooling written with languages that compile to native code to solve the issues with previous build tools (webpack, Parcel, etc.) relating to performance.
Vite works by first dividing the modules in an application into two categories, dependencies and source code, because dependencies rarely change during development. Vite pre-bundles these dependencies using esbuild under the hood. For source code that might need transforming (CSS, JSX, etc.), Vite serves them over native ESM to the browser.
As the browser makes the requests for the source code, Vite transforms and loads them on demand and the browser can use route-based code-splitting and conditional dynamic imports to bundle the needed code, making it a pretty fast process.
esbuild is a blazing fast JavaScript bundler written in Go and makes use of Go’s parallelism and ability to transform source code to machine code. Its features include, among others:
Now that we have looked at NestJS, Vite, and esbuild at a high level, let’s proceed to learn about how they work together by configuring a NestJS app to make use of both Vite and esbuild as dependencies. In the process, we‘ll learn how to work with them in real life scenarios, their major features, and prime use cases.
To get started with NestJS, go ahead and install the CLI, which bootstraps the starter code. This is an especially better option for those who are new to NestJS.
Another option is to clone the starter repo from GitHub. Note that to install the JavaScript flavor of the starter project, we can clone this repo, but you’ll need Babel to compile vanilla JavaScript).
For our purposes, we are going to go ahead and install the CLI. Run the following command:
npm i -g @nestjs/cli
After we are done installing the CLI, we can go ahead to create a new Nest project:
nest new nest_vite_esbuild_demo
The output of running this command is shown below:
nest new nest_vite_esbuild_demo took 24s ⚡ We will scaffold your app in a few seconds.. CREATE nest_vite_esbuild_demo/.eslintrc.js (665 bytes) CREATE nest_vite_esbuild_demo/.prettierrc (51 bytes) CREATE nest_vite_esbuild_demo/README.md (3340 bytes) CREATE nest_vite_esbuild_demo/nest-cli.json (118 bytes) CREATE nest_vite_esbuild_demo/package.json (2007 bytes) CREATE nest_vite_esbuild_demo/tsconfig.build.json (97 bytes) CREATE nest_vite_esbuild_demo/tsconfig.json (546 bytes) CREATE nest_vite_esbuild_demo/src/app.controller.spec.ts (617 bytes) CREATE nest_vite_esbuild_demo/src/app.controller.ts (274 bytes) CREATE nest_vite_esbuild_demo/src/app.module.ts (249 bytes) CREATE nest_vite_esbuild_demo/src/app.service.ts (142 bytes) CREATE nest_vite_esbuild_demo/src/main.ts (208 bytes) CREATE nest_vite_esbuild_demo/test/app.e2e-spec.ts (630 bytes) CREATE nest_vite_esbuild_demo/test/jest-e2e.json (183 bytes) ? Which package manager would you ❤️ to use? (Use arrow keys) ❯ npm yarn pnpm
Make sure you have the latest Node.js version installed on your machine (except v13, which is not supported).
As we can see from the above, the project directory has been populated with Nest’s core files, dependencies, and base modules. Follow the steps and select the package manager of your choice. In this post, we’re using npm. The folder structure at the end of the installation is shown below.
Next, we can navigate into the folder by running the following command.
cd nest_vite_esbuild_demo
Then, go ahead and start the project:
npm run start
The output of running that command is shown below.
npm run start > [email protected] start > nest start [Nest] 33031 - 08/08/2022, 3:22:16 AM LOG [NestFactory] Starting Nest application... [Nest] 33031 - 08/08/2022, 3:22:16 AM LOG [InstanceLoader] AppModule dependencies initialized +48ms [Nest] 33031 - 08/08/2022, 3:22:16 AM LOG [RoutesResolver] AppController {/}: +8ms [Nest] 33031 - 08/08/2022, 3:22:16 AM LOG [RouterExplorer] Mapped {/, GET} route +3ms [Nest] 33031 - 08/08/2022, 3:22:16 AM LOG [NestApplication] Nest application successfully started +8ms
As an alternative to the above, we can also start our application in development mode to watch for file changes, recompile the build, and reload the dev server by running the command below:
npm run start:dev
Then, navigate to http://localhost:3000/
in the browser. Further, we can also go ahead and perform a manual installation of NestJS’s core dependencies. In this setup, we can set up our project structure as we wish, just by running the command below.
npm i --save @nestjs/core @nestjs/common reflect-metadata
When we navigate to the src
directory inside our project folder, we can see the default files created for us. We have:
app.controller.ts
file, which represents our handler with just one routeapp.service.ts
file, which handles anything related to method or utility functions, so as to keep the controller slimapp.module
file, which handles the base module for our application (a way of structuring our application components)app.controller.spec.ts
file, which handles testing our controller logicmain.ts
file, which is entry point of our application and creates a Nest application instance using the core NestFactory
functionNestJS encourages developers to keep their application architecture as modular as possible, with each directory inside the source directory representing a single module. Each Nest application must have at least one module, a root module, which is the starting point for building the application graph.
For example, we can run the following command to create a new module:
nest g module product_demo
As you can see from the above, we have created a product_demo
module. We can then go ahead to create the other needed files — controller
and services
— and also import the product_demo
module into the app base module.
For more information regarding NestJS fundamentals and features, we can make reference to the documentation, including testing, providers, lifecycle events, decorators, middleware, modules etc., to gain mastery of the framework. Next, let us go ahead and set up Vite and esbuild on our NestJS application.
Vite is plugin-based and also comes with a well-optimized and fast build process, which can greatly improve your overall developer productivity and experience. It also supports TypeScript out of the box.
Now, let’s integrate Vite into our NestJS backend app. We are going to install Vite via the plugin, which runs a Node dev server with hot module replacement.
Run the command below as a development dependency.
npm install vite vite-plugin-node -D
Next, in the root of our project directory, we can create the vite.config.ts
file, which configures our project to make use of the plugin. Let’s see the contents of that file below.
import { defineConfig } from 'vite'; import { VitePluginNode } from 'vite-plugin-node'; export default defineConfig({ // ...vite configures server: { // vite server configs, for details see \[vite doc\](https://vitejs.dev/config/#server-host) port: 3000 }, plugins: [ ...VitePluginNode({ // Nodejs native Request adapter // currently this plugin support 'express', 'nest', 'koa' and 'fastify' out of box, // you can also pass a function if you are using other frameworks, see Custom Adapter section adapter: 'nest', // tell the plugin where is your project entry appPath: './src/main.ts', // Optional, default: 'viteNodeApp' // the name of named export of you app from the appPath file exportName: 'viteNodeApp', // Optional, default: 'esbuild' // The TypeScript compiler you want to use // by default this plugin is using vite default ts compiler which is esbuild // 'swc' compiler is supported to use as well for frameworks // like Nestjs (esbuild dont support 'emitDecoratorMetadata' yet) // you need to INSTALL `@swc/core` as dev dependency if you want to use swc tsCompiler: 'esbuild', }) ], optimizeDeps: { // Vite does not work well with optionnal dependencies, // mark them as ignored for now exclude: [ '@nestjs/microservices', '@nestjs/websockets', 'cache-manager', 'class-transformer', 'class-validator', 'fastify-swagger', ], }, });
Next, we need to update the entry point of our app server file (main.ts
) to export the app named viteNodeApp
(in the Vite config exportName
field above) or any other name we have configured in the Vite config file above. See below.
import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; if (import.meta.env.PROD) { async function bootstrap() { const app = await NestFactory.create(AppModule); await app.listen(3000); } bootstrap(); } export const viteNodeApp = NestFactory.create(AppModule);
Next, in our package.json
file, we can go ahead and add an npm script to run the dev server with the regular npm run dev
command:
"scripts": { "dev": "vite" }
As we have earlier mentioned, Vite comes with a dev server that serves our source files over native ESM. When we run our app with the Vite dev server using the npm run dev
command after the entire setup, we’ll see the output below.
To be sure everything is still working as expected, run the test suite with the npm run test
command and we can see the output below.
The plugin also uses Vite’s server-side rendering mode to build our app. To make use of this feature, we can go ahead and add a build script to our package.json
file (using Vite to build our app instead of Nest):
"scripts": { "build": "vite build" },
The vite build
command bundles our code with Rollup and spits out highly optimized assets for our production environment. When we build our app with Vite by running the npm run build
command after the entire setup, we can see the output below.
As mentioned before, Vite makes use of esbuild under the hood to transpile TypeScript to JavaScript. It is a very fast transpilation process — it can even be up to 20 times faster than regular TypeScript compilers, and HMR updates can reflect in the browser in under 50ms.
One of the advantages of using the Vite plugin (vite-plugin-node) is that we can choose to use either esbuild or swc to compile our TypeScript files, though we used only esbuild in this post.
In this post, we have learned how to get going with NestJS, Vite, and esbuild. As we have seen, NestJS has an interesting and rather new approach to building Node.js applications with a philosophy around OOP, FP, and FRP. These improvements greatly improve developer time and productivity in the long run, as we don’t have to bother about how to design our modules and components. NestJS solves that for us and proposes a one-module-per-folder pattern. We can also use the power of TypeScript to write our backend code.
Vite, on the other hand, is on a whole new level in the world of build tools. It is coming at a time where popular JS-based bundlers and build tooling in the NodeJS ecosystem needed a boost in terms of performance. Vite compiles our code to native code using a completely different approach from webpack and Parcel. As we have learned, it uses the browser and native ESM for bundling.
In all, we have combined these awesome technologies to build a simple boilerplate backend that can be a starting point for your next NestJS project.
Install LogRocket via npm or script tag. LogRocket.init()
must be called client-side, not
server-side
$ npm i --save logrocket // Code: import LogRocket from 'logrocket'; LogRocket.init('app/id');
// Add to your HTML: <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script> <script>window.LogRocket && window.LogRocket.init('app/id');</script>
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowLearn how to manage memory leaks in Rust, avoid unsafe behavior, and use tools like weak references to ensure efficient programs.
Bypass anti-bot measures in Node.js with curl-impersonate. Learn how it mimics browsers to overcome bot detection for web scraping.
Handle frontend data discrepancies with eventual consistency using WebSockets, Docker Compose, and practical code examples.
Efficient initializing is crucial to smooth-running websites. One way to optimize that process is through lazy initialization in Rust 1.80.
One Reply to "Getting started with NestJS, Vite, and esbuild"
Hi, really interesting article. Do you know if for Nestjs monorepos that use webpack this approach will work?