๐Ÿ› ๏ธ VitNode is still in development! You can try it out, but it is not recommended to use it now in production.
๐Ÿ”Œ Plugins
Database
Pagination

Pagination

VitNode provide a pagination system using cursor pagination to help you retrieve data from the database.

โš ๏ธ

VitNode doesn't support the offset pagination.

Here is an example of how to use the pagination system based on blog_categories table.

Data Transfer Object (DTO)

We will create a DTO to create arguments and return values for the query.

Arguments

File: show/dto/show.args.ts

Arguments used as container for fields to create query in GraphQL schema.

import { ArgsType, Field, Int } from "@nestjs/graphql";
 
@ArgsType()
export class ShowBlogCategoriesArgs {
  @Field(() => Int, { nullable: true })
  cursor: number | null;
 
  @Field(() => Int, { nullable: true })
  first: number | null;
 
  @Field(() => Int, { nullable: true })
  last: number | null;
}

Object

File: show/dto/show.obj.ts

Object used as container for fields to create return values query in GraphQL schema.

import { Field, Int, ObjectType } from "@nestjs/graphql";
 
import { PageInfo } from "@/types/database/pagination.type";
import { TextLanguage } from "@/types/database/text-language.type";
 
@ObjectType()
export class ShowBlogCategories {
  @Field(() => Int)
  id: number;
 
  @Field(() => [TextLanguage])
  name: TextLanguage[];
 
  @Field(() => [TextLanguage], { nullable: true })
  description: TextLanguage[] | null;
}
 
@ObjectType()
export class ShowBlogCategoriesObj {
  @Field(() => [ShowBlogCategories])
  edges: ShowBlogCategories[];
 
  @Field(() => PageInfo)
  pageInfo: PageInfo;
}

ShowBlogCategories object is used to create a list of blog_categories objects.

Query GraphQL

Service

File: show/show.service.ts

Inside service file we will create a show method that will return a ShowBlogCategoriesObj object.

import { Injectable } from "@nestjs/common";
 
import { ShowBlogCategoriesArgs } from "./dto/show.args";
import { ShowBlogCategoriesObj } from "./dto/show.obj";
 
import { DatabaseService } from "@/plugins/database/database.service";
 
@Injectable()
export class ShowBlogCategoriesService {
  constructor(private databaseService: DatabaseService) {}
 
  async show({
    cursor,
    first,
    last
  }: ShowBlogCategoriesArgs): Promise<ShowBlogCategoriesObj> {}
}

Initial values for pagination

We will use inputPaginationCursor() to create initial values for pagination.

import { Injectable } from "@nestjs/common";
 
import { ShowBlogCategoriesArgs } from "./dto/show.args";
import { ShowBlogCategoriesObj } from "./dto/show.obj";
 
import { DatabaseService } from "@/plugins/database/database.service";
import { inputPaginationCursor } from "@/functions/database/pagination";
import { blog_categories } from "../../admin/database/schema/categories";
import { SortDirectionEnum } from "@/types/database/sortDirection.type";
 
@Injectable()
export class ShowBlogCategoriesService {
  constructor(private databaseService: DatabaseService) {}
 
  async show({
    cursor,
    first,
    last
  }: ShowBlogCategoriesArgs): Promise<ShowBlogCategoriesObj> {
    const pagination = await inputPaginationCursor({
      cursor,
      database: blog_categories,
      databaseService: this.databaseService,
      first,
      last,
      primaryCursor: { order: "ASC", key: "id", schema: blog_categories.id },
      defaultSortBy: {
        direction: SortDirectionEnum.asc,
        column: "position"
      }
    });
  }
}

Query from database

We will use findMany() method to get the data from the database with with argument to get the related data.

@Injectable()
export class ShowBlogCategoriesService {
  constructor(private databaseService: DatabaseService) {}
 
  async show({
    cursor,
    first,
    last
  }: ShowBlogCategoriesArgs): Promise<ShowBlogCategoriesObj> {
    const pagination = await inputPaginationCursor({
      cursor,
      database: blog_categories,
      databaseService: this.databaseService,
      first,
      last,
      primaryCursor: { order: "ASC", key: "id", schema: blog_categories.id },
      defaultSortBy: {
        direction: SortDirectionEnum.asc,
        column: "position"
      }
    });
 
    const edges = await this.databaseService.db.query.blog_categories.findMany({
      ...pagination,
      with: {
        title: true,
        description: true
      }
    });
  }
}

Where argument

If you want to use where argument you can pass it to the findMany() method using and() method from drizzle-orm.

import { Injectable } from "@nestjs/common";
import { and, lte } from "drizzle-orm";
 
import { ShowBlogCategoriesArgs } from "./dto/show.args";
import { ShowBlogCategoriesObj } from "./dto/show.obj";
 
import { DatabaseService } from "@/plugins/database/database.service";
import { inputPaginationCursor } from "@/functions/database/pagination";
import { blog_categories } from "../../admin/database/schema/categories";
import { SortDirectionEnum } from "@/types/database/sortDirection.type";
 
@Injectable()
export class ShowBlogCategoriesService {
  constructor(private databaseService: DatabaseService) {}
 
  async show({
    cursor,
    first,
    last
  }: ShowBlogCategoriesArgs): Promise<ShowBlogCategoriesObj> {
    const pagination = await inputPaginationCursor({
      cursor,
      database: blog_categories,
      databaseService: this.databaseService,
      first,
      last,
      primaryCursor: { order: "ASC", key: "id", schema: blog_categories.id },
      defaultSortBy: {
        direction: SortDirectionEnum.asc,
        column: "position"
      }
    });
 
    const where = lte(blog_categories.created, new Date());
 
    const edges = await this.databaseService.db.query.blog_categories.findMany({
      ...pagination,
      where: and(pagination.where, where),
      with: {
        title: true,
        description: true
      }
    });
  }
}

Return values

We will use outputPagination() to create return values for the query. Remember to create a query totalCount to get the total count of the query.

import { Injectable } from "@nestjs/common";
import { count } from "drizzle-orm";
 
import { ShowBlogCategoriesArgs } from "./dto/show.args";
import { ShowBlogCategoriesObj } from "./dto/show.obj";
 
import { DatabaseService } from "@/plugins/database/database.service";
import {
  inputPaginationCursor,
  outputPagination
} from "@/functions/database/pagination";
import { blog_categories } from "../../admin/database/schema/categories";
import { SortDirectionEnum } from "@/types/database/sortDirection.type";
 
@Injectable()
export class ShowBlogCategoriesService {
  constructor(private databaseService: DatabaseService) {}
 
  async show({
    cursor,
    first,
    last
  }: ShowBlogCategoriesArgs): Promise<ShowBlogCategoriesObj> {
    const pagination = await inputPaginationCursor({
      cursor,
      database: blog_categories,
      databaseService: this.databaseService,
      first,
      last,
      primaryCursor: { order: "ASC", key: "id", schema: blog_categories.id },
      defaultSortBy: {
        direction: SortDirectionEnum.asc,
        column: "position"
      }
    });
 
    const edges = await this.databaseService.db.query.blog_categories.findMany({
      ...pagination,
      with: {
        title: true,
        description: true
      }
    });
 
    const totalCount = await this.databaseService.db
      .select({ count: count() })
      .from(blog_categories);
 
    return outputPagination({ edges, totalCount, first, cursor, last });
  }
}

If you have where argument you need to pass it to the count() method.

const totalCount = await this.databaseService.db
  .select({ count: count() })
  .from(blog_categories)
  .where(where);

Modyfy return values

If you want to modify the return values you can use map() method to modify the return values.

return outputPagination({
  edges: edges.map(edge => ({
    ...edge,
    name: edge.title,
    description: edge.description
  })),
  totalCount,
  first,
  cursor,
  last
});

Output

{
  "edges": [],
  "pageInfo": {
    "hasNextPage": false,
    "startCursor": "",
    "endCursor": "",
    "totalCount": 0,
    "count": 0
  }
}