NestJS Integration

ZingGrid works with your development stack, including NestJS! In this guide, we'll walk you through how to add ZingGrid to your NestJS CRUD app.

Usage

  1. Include the following dependencies in the header of your HTML file:

    <script src="path/to/zinggrid.min.js" defer></script>
  2. Create a <zing-grid> component, like so:

    <zing-grid id="myGrid" caption="Hello Doggos"></zing-grid>
  3. Use JavaScript to set or change your data, like so:

    window.addEventListener(() => {
      let zgRef = document.querySelector('zing-grid');
      zgRef.data = [
        { "breed": "Dachshund", "name": "Sousage"},
        { "breed": "Corgi", "name": "Plop"},
        { "breed": "Pomeranian", "name": "Floof"}
      ];
    });
Top

Setting up a CRUD App

This walkthrough will guide you through setting up a CRUD app utilizing NestJS CRUD API and hook it up to your grid.

Setting up the Project

Install the Nest CLI, the create and navigate to the project:

npm i -g @nest/cli
nest new project-name
cd project-name

Generate CRUD API

Then generate the CRUD API using the command:

nest g resource

For this demo, enter the following for the prompt:

  1. "users" as the name for the resource
  2. Select "REST API" as the transport layer
  3. Select "Yes" to generate the CRUD entry points

When the command finish generating the CRUD API endpoints, you'll notice that all the methods are there except for "PUT". Since ZingGrid uses the "PUT" method for record editing, let's add the method and placeholder.

  1. Go to src/users/users.controller.ts
  2. In line 1, include "PUT" in the import
    import { Controller, Get, Post, Body, Patch, Put, Param, Delete } from '@nestjs/common';
  3. Add the placeholder for the "PUT" endpoint
    @Put(':id')
    updateCell(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto) {
    return this.usersService.update(id, updateUserDto);
    }

With the endpoints created, let's prefix every registered route with "api" using setGlobalPrefix().

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

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.setGlobalPrefix('api');

  await app.listen(3000);
}
bootstrap();

Create Docker/MongoDB to Run Database

In this step, you can either choose Docker or MongoDB to run the database.

Docker

Install Docker from their site.

After, create docker-compose.yml file at the root level to specify the database and ports:

version: "3"

services:
  mongodb:
    image: mongo:latest
    environment:
      - MONGODB_DATABASE="test"
    ports:
      - 27017:27017

Once that is complete, run the Docker container.

MongoDB

Install MongoDB from their site.

Once the installation is complete, run MongoDB through the command:

mongod

Note that if an error occurs, create a data/db directory. Then set that directory as the db path:

mongod --dbpath PATH_TO/data/db

Setup Mongoose, Configure the Database, and Implement API Endpoints

Install Mongoose through the command:

npm install --save @nestjs/mongoose mongoose

Then define a DTO class in src/users/dto/create-user.dto.ts.

export class CreateUserDto {
  readonly name: string;
  readonly age: number;
}

Next, define the schema in src/users/schemas/user.schema.ts (Don't forget to create the schemas folder and file!).

import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { Document } from 'mongoose';

export type UserDocument = User & Document;

@Schema()
export class User {
  @Prop()
  name: string;

  @Prop()
  age: number;
}

export const UserSchema = SchemaFactory.createForClass(User);

Following that, implement the services in src/users/users.service.ts.

import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
import { User, UserDocument } from './schemas/user.schema';

@Injectable()
export class UsersService {
  constructor(
    @InjectModel(User.name) private readonly userModel: Model<UserDocument>,
  ) {}

  async create(createUserDto: CreateUserDto): Promise<User> {
    const createdUser = await new this.userModel(createUserDto);
    return createdUser.save();
  }

  async findAll(): Promise<User[]> {
    return this.userModel.find().exec();
  }

  async findOne(id: string): Promise<User> {
    return this.userModel.findOne({_id: id}).exec();
  }

  async update(id: string, updateUserDto: UpdateUserDto) {
    return this.userModel.findByIdAndUpdate(id, updateUserDto).exec();
  }
  
  async updateCell(id: string, updateUserDto: UpdateUserDto) {
    return this.userModel.findByIdAndUpdate(id, updateUserDto).exec();
  }

  async remove(id: string) {
    const deleted = await this.userModel
    .findByIdAndRemove({_id: id})
    .exec();
    return deleted;
  }
}

Then implement the controller in src/users/users.controller.ts.

import { Controller, Get, Post, Body, Patch, Put, Param, Delete } from '@nestjs/common';
import { UsersService } from './users.service';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';

@Controller('users')
export class UsersController {
  constructor(private readonly usersService: UsersService) {}

  @Post()
  create(@Body() createUserDto: CreateUserDto) {
    return this.usersService.create(createUserDto);
  }

  @Get()
  findAll() {
    return this.usersService.findAll();
  }

  @Get(':id')
  findOne(@Param('id') id: string) {
    return this.usersService.findOne(id);
  }

  @Patch(':id')
  update(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto) {
    return this.usersService.update(id, updateUserDto);
  }

  @Put(':id')
  updateCell(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto) {
    return this.usersService.update(id, updateUserDto);
  }

  @Delete(':id')
  remove(@Param('id') id: string) {
    return this.usersService.remove(id);
  }
}

After, add the Mongoose imports and specify the controllers and providers in src/users/users.module.ts.

Make sure for the Mongoose module to match the port and database name specified in docker-compose.yml.

import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
import { UsersController } from './users.controller';
import { UsersService } from './users.service';
import { User, UserSchema } from './schemas/user.schema';

@Module({
  imports: [MongooseModule.forFeature([{ name: User.name, schema: UserSchema }])],
  controllers: [UsersController],
  providers: [UsersService]
})
export class UsersModule {}

Statically Serve Client Containing ZingGrid

Almost there! Now we'll build out the client-side. First, install static-serve:

npm install --save @nestjs/serve-static

Then add the static-serve import and specify the directory to serve in src/app.module.ts.

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { UsersModule } from './users/users.module';
import { MongooseModule } from '@nestjs/mongoose';
import { ServeStaticModule } from '@nestjs/serve-static';
import { join } from 'path';

@Module({
  imports: [
    UsersModule, 
    MongooseModule.forRoot('mongodb://localhost:27017/test'),
    ServeStaticModule.forRoot({rootPath: join(__dirname, '..', 'client'), exclude: ['/api*']}),
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

Create the client folder at the root. This is the directly that will be served.

Afterwards, create a client/index.html file and add ZingGrid! The grid is configured to:

  • Enable editing by adding editor-controls attribute to the ZingGrid tag
  • Set columns to display using ZGColgroup and ZGColumn (_id column is excluded)
  • Set ZGParam[src] to specify the CRUD endpoint
  • Set ZGParam[idKey] to configure the key from the default (id) to the one MongoDB uses (_id)
<!doctype html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Basic</title>
  <script src="https://cdn.zinggrid.com/zinggrid.min.js"></script>
</head>

<body>
    
  <zing-grid caption="CRUD NestJS Demo" editor-controls>
    <zg-colgroup>
      <zg-column index="name"></zg-column>
      <zg-column index="age" type="number"></zg-column>
    </zg-colgroup>
    <zg-data>
      <zg-param name="src" value="http://localhost:3000/api/users"></zg-param>
      <zg-param name="idKey" value="_id"></zg-param>
    </zg-data>
  </zing-grid>
</body>
</html>

Create NestJS Config

Create the NestJS config file at the root level. Name this file nest.config.ts, and set the content to:

module.exports = {
  async headers() {
    return [
      { 
        source: '/:path*{/}?',
        headers: [
          {
            key: 'Access-Control-Allow-Origin',
            value: 'http://localhost:8082',
          },
        ],
      },
    ]
  },
}

Run App

All that's left to do is run the app! To do so, run the command:

npm run start

View your demo at localhost:3000.

NestJS Integrated Grid

Here is our complete grid that is successfully integrated with NestJS CRUD app:

Check out the full code here!

Top

[integrations: nestJS]