These days REST API has become a web applications development standard, allowing to divide web development into two separate parts. There are several mainstream frameworks like Angular, React, Vue, that are used for UI. Backend developers are free to choose from large variety of languages and frameworks. Today I’d like to discuss framework. We’re going to create a simple CRUD application using Nest and the package. NestJS @nestjsx/crud Why NestJS Quite a number of backend frameworks have appeared in JavaScript community lately. They all offer the same features as Nest, but Nest architecture makes it stand out from the others. The following NestJS features allow you to create production-ready applications and scale development for larger teams: TypeScript as the main programming language. Although NestJS supports JavaScript, it may not work properly, especially in third-party packages; DI container that allows to create loosely coupled components; The framework functionality is divided into independent interchangeable components. For instance, both and can be used as frameworks under the hood, and for databases Nest provides , , bindings out of the box; express fastify typeorm mongoose sequelize NestJS is platform independent and supports REST, GraphQL, Websockets, gRPC, etc. The framework was inspired by (frontend framework), and conceptually they have much in common. Angular NestJS Installation and Project Deployment is a Nest package which allows to quickly deploy a basic application framework. Install this package globally: @nest/cli install -- @nest/cli npm global After that generate your application framework named nest-rest running the command . nest new nest-rest dmitrii@dmitrii-HP-ZBook-17-G3:~/projects $ nest new nest-rest ⚡ We will scaffold your app a few seconds.. CREATE /nest-rest/.prettierrc (51 bytes) CREATE /nest-rest/README.md (3370 bytes) CREATE /nest-rest/nest-cli.json (84 bytes) CREATE /nest-rest/nodemon-debug.json (163 bytes) CREATE /nest-rest/nodemon.json (67 bytes) CREATE /nest-rest/package.json (1805 bytes) CREATE /nest-rest/tsconfig.build.json (97 bytes) CREATE /nest-rest/tsconfig.json (325 bytes) CREATE /nest-rest/tslint.json (426 bytes) CREATE /nest-rest/src/app.controller.spec.ts (617 bytes) CREATE /nest-rest/src/app.controller.ts (274 bytes) CREATE /nest-rest/src/app.module.ts (249 bytes) CREATE /nest-rest/src/app.service.ts (142 bytes) CREATE /nest-rest/src/main.ts (208 bytes) CREATE /nest-rest/ /app.e2e-spec.ts (561 bytes) CREATE /nest-rest/ /jest-e2e.json (183 bytes) ? Which package manager would you ❤️ to use? yarn ✔ Installation progress... ☕ 🚀 Successfully created project nest-rest 👉 Get started with the following commands: $ nest-rest $ yarn run start Thanks installing Nest 🙏 Please consider donating to our open collective to us maintain this package. 🍷 Donate: https://opencollective.com/nest in test test in cd for help Choose yarn as a package manager. You can now start the server running the command. Follow the link to see the main page. Nice, but that’s not what we’re here for. npm start http://localhost:3000 Setting Up the Database Thinking of a database management system for this project, I decided to go with PostrgreSQL. Tastes differ, and I believe this is the most mature database management system with all possible features you may need. As I already mentioned, Nest provides integration with different packages to work with databases. Since I’ve chosen PostgreSQL, it’s only logical to choose TypeORM as an ORM. So let’s install all necessary packages for database integration: @ / pg yarn add typeorm nestjs typeorm Now let’s see what we need each package for: — comes with ORM; typeorm @nestjs/typeorm — a TypeORM package for NestJS. It adds helper decorators and modules ready for import into project modules; pg — a PostgreSQL driver. version: '3.1' services: db: image: postgres:11.2 restart: always environment: POSTGRES_PASSWORD: example volumes: - ../db:/var/lib/postgresql/data - ./postgresql.conf:/etc/postgresql/postgresql.conf ports: - 5432 :5432 adminer: image: adminer restart: always ports: - 8080 :8080 As you can see, this file configures running 2 containers: db — a container with the database. We’re using PostgreSQL version 11.2; adminer — a database manager. It provides a web interface to view and manage the database. To interact with TCP connections, I added the . following config That’s it, now you can run the containers with the docker-compose up -d command or the docker-compose up command to run them in a separate console window. Viewing logs is simpler this way. Well then, packages are installed, the database is run, now we have to bring them together. To do that, add ormconfig.js file to the project root directory: process = ( ); username = process.env.POSTGRES_USER || ; password = process.env.POSTGRES_PASSWORD || ; .exports = { : , : , : , username, password, : , : , : , : , : [__dirname + , __dirname + ], : [ ], : [ , ], : { : , : , : } } const require 'process' const "postgres" const "example" module "type" "postgres" "host" "localhost" "port" 5432 "database" "postgres" "synchronize" true "dropSchema" false "logging" true "entities" "/src/**/*.entity.ts" "/dist/**/*.entity.js" "migrations" "migrations/**/*.ts" "subscribers" "subscriber/**/*.ts" "dist/subscriber/**/.js" "cli" "entitiesDir" "src" "migrationsDir" "migrations" "subscribersDir" "subscriber" This configuration will be used for the TypeORM CLI. Let’s look at it a little closer. In the lines 3 and 4 we get username and password from environmental variables. That is quite handy when you have several environments (dev, stage, prod, etc). The default username is postgres, password — example. The rest of the config is quite trivial, so let’s consider only the most important parameters. . It specifies if the database schema should be automatically created when launching the application. Mind that if you use this parameter in production, you’re going to lose some data. This parameter is useful for debug and development though. You can run the schema:sync command from the TypeORM CLI instead. synchronize . It drops the schema whenever the connection is established. This parameter should only be used for debug and development just like the previous one. dropSchema . It specifies where to find models description. Note that mask search is supported here. entities . It is the directory where models created from the TypeORM CLI are stored by default. cli.entitiesDir To take full advantage of TypeORM features in our Nest application, we need to import into . Your will look like this: TypeOrmModule AppModule AppModule { Module } ; { AppController } ; { AppService } ; { TypeOrmModule } ; * process ; username = process.env.POSTGRES_USER || ; password = process.env.POSTGRES_PASSWORD || ; ({ imports: [ TypeOrmModule.forRoot({ : , host: , port: , username, password, database: , entities: [__dirname + ], synchronize: , }), ], controllers: [AppController], providers: [AppService], }) AppModule {} import from '@nestjs/common' import from './app.controller' import from './app.service' import from '@nestjs/typeorm' import as from "process" const 'postgres' const 'example' @Module type 'postgres' 'localhost' 5432 'postgres' '/**/*.entity{.ts,.js}' true export class As you see, we pass into method the same database configuration as in ormconfig.ts file. forRoot We’ve got one more thing to do — add a few tasks into package.json to work with TypeORM. The thing is that CLI is written in JavaScript and therefore runs in NodeJS, but all our models and migrations are written in Typescript. For that reason, we need to transpile them before using CLI. To do that, we need the package to be installed globally: ts-node npm install -g ts- node Add the following commands to package.json: : , : , : , : "typeorm" "ts-node -r tsconfig-paths/register ./node_modules/typeorm/cli.js" "migration:generate" "yarn run typeorm migration:generate -n" "migration:create" "yarn run typeorm migration:create -n" "migration:run" "yarn run typeorm migration:run" The first command is typeorm which adds ts-node as a wrap to run the TypeORM CLI. Other commands are just useful contractions that you are going to use every day as a developer: — generate migrations based on changes in your models. “migration:generate” — create an empty migration FILE. “migration:create” — run a migration. “migration:run” That’s finally it! We’ve added all required packages, configured the app to interact with the database from both the CLI and the app itself, and started up the database management system. Now it’s time to add some logic to our app. Installing CRUD Packages You can design an API to create, read, update, and delete an entity using Nest only. This approach is the most flexible one, but in some cases it may be excessive. For example, if you need to quickly create a prototype, you can sacrifice flexibility for high development speed. A lot of frameworks allow you to generate CRUD API from an entity data model description. Nest is not an exception. The package makes things happen. Its features are quite exciting: @nestjsx/crud DBMS independence; easy installation and setup; powerful query language with filtering, pagination, sorting, relations, nested relations, cache, etc.; a package with query builder for frontend usage; easy controller methods overriding; tiny config; swagger documentation support. It consisnts of several packages: — core package which provides @Crud() decorator for endpoints generation, configuration, and validation; @nestjsx/crud — request builder/parser package for frontend usage; @nestjsx/crud-request — TypeORM package which provides base TypeOrmCrudService with methods for CRUD database operations. @nestjsx/crud-typeorm For our application we’ll need the @nestjsx/crud and @nestjsx/crud-typeorm packages. Let’s install them: yarn @nestjsx/crud -transformer - add class class validator The and packages are required for declarative description of model instance transformation rules and incoming requests validation respectively. These packages were created by the same author and therefore have similar implementations. class-transformer class-validator CRUD Implementation Let’s take a list of users as a model. Users have the following fields: , , , . is an auto-increment field, and — unique fields. Simple as that! Now let’s bring the idea to life in the form of a Nest application. id username displayName email Id email username First, let’s create a users module, that will be responsible for working with users. Run the nest g module users command in the NestJS CLI in the project root directory. dmitrii@dmitrii-HP-ZBook-17-G3:~/projects/nest-rest git:(master*)$ nest g module users CREATE /src/users/users.module.ts (82 bytes) UPDATE /src/app.module.ts (312 bytes) Add the entities folder where you’ll be storing this module models. Add here the file with users model description: user.entity.ts { Column, Entity, PrimaryGeneratedColumn } ; () User { () id: ; ({unique: }) email: ; ({unique: }) username: ; ({nullable: }) displayName: ; } import from 'typeorm' @Entity export class @PrimaryGeneratedColumn string @Column true string @Column true string @Column true string For our app to find this model we need to import with the following content into : TypeOrmModule UsersModule { Module } ; { UsersController } ; { UsersService } ; { TypeOrmModule } ; { User } ; ({ controllers: [UsersController], providers: [UsersService], imports: [ TypeOrmModule.forFeature([User]) ] }) UsersModule {} import from '@nestjs/common' import from './controllers/users/users.controller' import from './services/users/users.service' import from '@nestjs/typeorm' import from './entities/user.entity' @Module export class We import and pass this module models list as the parameter. TypeOrmModule forFeature Now we need to create a corresponding database entity. That’s what we need our migration mechanism for. To create a migration based on model changes run the following command: $ npm run migration:generate -- CreateUserTable Migration /home/dmitrii/projects/nest-rest/migrations/1563346135367-CreateUserTable.ts has been generated successfully. Done 1.96s. in See, you don’t have to manually write the migration, it is created automatically. It’s magic! But this is just a beginning. Let’s take a look at our migration file: {MigrationInterface, QueryRunner} ; CreateUserTable1563346816726 MigrationInterface { up(queryRunner: QueryRunner): < > { queryRunner.query( ); } down(queryRunner: QueryRunner): < > { queryRunner.query( ); } } import from "typeorm" export class implements public async Promise any await `CREATE TABLE "user" ("id" SERIAL NOT NULL, "email" character varying NOT NULL, "username" character varying NOT NULL, "displayName" character varying, CONSTRAINT "UQ_e12875dfb3b1d92d7d7c5377e22" UNIQUE ("email"), CONSTRAINT "UQ_78a916df40e02a9deb1c4b75edb" UNIQUE ("username"), CONSTRAINT "PK_cace4a159ff9f2512dd42373760" PRIMARY KEY ("id"))` public async Promise any await `DROP TABLE "user"` As you can see, not only a run but also a rollback method was generated automatically. Incredible! Now we only have to run this migration. Use the following command: npm migration: . run run Schema changes have just been transferred to the database. Now let’s create a service that will be responsible for working with users and inherit it from . We need to pass a relevant entity repository (User repository in our case) to the parent constructor parameter. TypeOrmCrudService { Injectable } ; { TypeOrmCrudService } ; { User } ; { InjectRepository } ; { Repository } ; () UsersService TypeOrmCrudService<User>{ ( ){ (usersRepository); } } import from '@nestjs/common' import from '@nestjsx/crud-typeorm' import from '../../entities/user.entity' import from '@nestjs/typeorm' import from 'typeorm' @Injectable export class extends constructor (User) usersRepository: Repository<User> @InjectRepository super We’ll need this service in users controller. To create the controller run the command in the CLI. nest g controller users/controllers/users dmitrii@dmitrii-HP-ZBook-17-G3:~/projects/nest-rest git:(master*)$ nest g controller users/controllers/users CREATE /src/users/controllers/users/users.controller.spec.ts (486 bytes) CREATE /src/users/controllers/users/users.controller.ts (99 bytes) UPDATE /src/users/users.module.ts (188 bytes) Open the controller and add some @nestjsx/crud magic to it. Add the following decorator for the class: UsersController ({ model: { : User } }) @Crud type The decorator adds to the controller some useful methods to work with models. Model type is specified in model.type field in the decorator configuration. @Crud The next step is implementing interface. The entire code of the controller will look like this: CrudController<User> { Controller } ; { Crud, CrudController } ; { User } ; { UsersService } ; ({ model: { : User } }) ( ) UsersController CrudController<User>{ ( ){} } import from '@nestjs/common' import from '@nestjsx/crud' import from '../../entities/user.entity' import from '../../services/users/users.service' @Crud type @Controller 'users' export class implements constructor service: UsersService public That’s all! The controller now supports the whole set of operations with the model. It can’t be true. Let’s see our application in action! Creating a Request Scenario in TestMace To test our service, we’ll be using — an IDE for API design. Why TestMace? It has some advantages compared to its counterparts: TestMace powerful variables mechanism. There are several variable types, each of which serves its own purpose: default variables, dynamic variables, environment variables. Each variable belongs to a specific node, inheritance is supported; creating scenarios without programming. We’ll discuss it a bit later; a human-readable format, which allows to store your projects in a version control system; autocomplete feature, syntax highlighting, variables values highlighting; API description support + import from Swagger. Let’s start our server running the command and access the user list. According to the controller configuration, the list can be found at . Let’s send a request to this url. After running TestMace, you can see the following interface: npm start localhost:3000/users At the top left of the screen there is the project tree with being a root node. Let’s create the first request to get the user list. Right-click on the Project node and choose . Project Add node -> RequestStep Insert in the URL field and send the request. You’ll get a 200 code and an empty array in the response body. It makes sense, we haven’t yet added anything. localhost:3000/users Let’s create a scenario with the following steps: create a user; request the user by its id; delete the user by its id. Let’s go. Create a for convenience. In fact, it’s just a folder where the whole scenario will be stored. To create a Folder node right-click in the Project node and choose . Name it . Add your first request within this node to create a user. Name the new node . At the moment we have the following node hierarchy: Folder node Add node -> Folder check-create create-user Now go to the node tab. Enter the following request parameters: create-user Request type — ; POST URL — ; localhost:3000/users Body — JSON with the value . {“email": “user@user.com", “displayName": “New user", “username": “user"} Send the request. The application tells us the record has been created. Let’s check then. We need to save the id of the added user to be able to use it later. That’s what our mechanism can help us with. Let’s see how it works using the previous example. Right-click on the id node and choose in on the parsed tab. Set the parameters in the dialog window: dynamic variables Assign to variable Node specifies the parent node for the variable. Choose ; check-create Variable name. Let’s name it . userId That’s how creating a dynamic variable looks like: With that done, the dynamic variable value will be updated every time you send this request. Since dynamic variables support hierarchical inheritance, the variable will always be available in node children of any nesting level. userId check-create We’ll need this variable in the next request. Let’s get the newly added user. Create the request as a child of the node with as a url parameter. The construct is used to get the value. As we’ve got a dynamic variable , we have to access , so the construct will look like this: . Send the request and make sure it does what it was made for. check-if-exists check-create localhost:3000/users/${$dynamicVar.userId} ${variable_name} variable $dynamicVar ${dynamicVar.userId} And the last thing to do is to delete the user. We need to send the final request not only to check if it works properly, but also to clean up the database, as email and username fields are unique. Create the request in the node with the following parameters: delete-user check-create Request type — ; DELETE URL — . localhost:3000/users/${$dynamicVar.userId} Run it. Wait. Enjoy :) Now you can run the full scenario any time you want. To do that, just right-click on the node and choose . check-create Run Nodes will be run consequently. You can save this scenario to use it in your own projects by . File -> Save project Conclusion I haven’t covered all possible features of the tools used in this article. As for the @nestjsx/crud package — the main subject of this article, the following features are worth discussing: custom model validation and transformation; powerful query language and its convenient frontend usage; adding and overriding methods in crud-controllers; swagger support; cache management. However, the information given in the article is enough to realize that even such enterprise framework as NestJS has it all to quickly prototype an application. And our awesome IDE helps to keep up the pace. TestMace You can find the source code and the text of this article in the repository: . Choose to open a TestMace project in the app. https://github.com/TestMace/nest-rest File -> Open project