diff --git a/__old_docs/pages/docs/plugins/database/pagination.mdx b/__old_docs/pages/docs/plugins/database/pagination.mdx
deleted file mode 100644
index a3e0b8c50..000000000
--- a/__old_docs/pages/docs/plugins/database/pagination.mdx
+++ /dev/null
@@ -1,350 +0,0 @@
-import { Callout } from 'nextra/components';
-
-# 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.
-
-import { Steps } from 'nextra/components';
-
-
-
-### Arguments
-
-File: `show/dto/show.args.ts`
-
-Arguments used as container for fields to create query in GraphQL schema.
-
-```ts
-import { ArgsType } from '@nestjs/graphql';
-
-import { PaginationArgs } from '@/types/database/pagination.type';
-
-@ArgsType()
-export class ShowBlogCategoriesArgs extends PaginationArgs {}
-```
-
-#### InputType
-
-If you want to use `InputType` you can use `PaginationInput` from `pagination.type`.
-
-```ts
-import { ArgsType } from '@nestjs/graphql';
-
-import { PaginationInput } from '@/types/database/pagination.type';
-
-@InputType()
-export class ShowBlogCategoriesArgs extends PaginationInput {}
-```
-
-### Object
-
-File: `show/dto/show.obj.ts`
-
-Object used as container for fields to create return values query in GraphQL schema.
-
-```ts
-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.
-
-```ts
-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 databaseServices: DatabaseService) {}
-
- async show({
- cursor,
- first,
- last,
- }: ShowBlogCategoriesArgs): Promise {}
-}
-```
-
-### Initial values for pagination
-
-We will use `inputPaginationCursor()` to create initial values for pagination.
-
-```ts {7-9, 20-35}
-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 databaseServices: DatabaseService) {}
-
- async show({
- cursor,
- first,
- last,
- }: ShowBlogCategoriesArgs): Promise {
- const pagination = await inputPaginationCursor({
- cursor,
- database: blog_categories,
- databaseService: this.databaseService,
- first,
- last,
- primaryCursor: {
- direction: SortDirectionEnum.asc,
- column: '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.
-
-```ts {27-29}
-@Injectable()
-export class ShowBlogCategoriesService {
- constructor(private databaseServices: DatabaseService) {}
-
- async show({
- cursor,
- first,
- last,
- }: ShowBlogCategoriesArgs): Promise {
- const pagination = await inputPaginationCursor({
- cursor,
- database: blog_categories,
- databaseService: this.databaseService,
- first,
- last,
- primaryCursor: {
- direction: SortDirectionEnum.asc,
- column: 'id',
- schema: blog_categories.id,
- },
- defaultSortBy: {
- direction: SortDirectionEnum.asc,
- column: 'position',
- },
- });
-
- const edges = await this.databaseService.db.query.blog_categories.findMany({
- ...pagination,
- });
- }
-}
-```
-
-#### Where argument
-
-If you want to use `where` argument you can pass it to the `findMany()` method using `and()` method from `drizzle-orm`.
-
-```ts {2, 38}
-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 databaseServices: DatabaseService) {}
-
- async show({
- cursor,
- first,
- last,
- }: ShowBlogCategoriesArgs): Promise {
- const pagination = await inputPaginationCursor({
- cursor,
- database: blog_categories,
- databaseService: this.databaseService,
- first,
- last,
- primaryCursor: {
- direction: SortDirectionEnum.asc,
- column: '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),
- });
- }
-}
-```
-
-### 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.
-
-```ts {2, 10, 45-47, 49}
-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 databaseServices: DatabaseService) {}
-
- async show({
- cursor,
- first,
- last,
- }: ShowBlogCategoriesArgs): Promise {
- const pagination = await inputPaginationCursor({
- cursor,
- database: blog_categories,
- databaseService: this.databaseService,
- first,
- last,
- primaryCursor: {
- direction: SortDirectionEnum.asc,
- column: 'id',
- schema: blog_categories.id,
- },
- defaultSortBy: {
- direction: SortDirectionEnum.asc,
- column: 'position',
- },
- });
-
- const edges = await this.databaseService.db.query.blog_categories.findMany({
- ...pagination,
- });
-
- 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.
-
-```ts {4}
-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.
-
-```ts {2-6}
-return outputPagination({
- edges: edges.map(edge => ({
- ...edge,
- name: edge.title,
- description: edge.description,
- })),
- totalCount,
- first,
- cursor,
- last,
-});
-```
-
-
-
-## Output
-
-```JSON
-{
- "edges": [],
- "pageInfo": {
- "hasNextPage": false,
- "startCursor": "",
- "endCursor": "",
- "totalCount": 0,
- "count": 0
- }
-}
-```
diff --git a/apps/docs/content/docs/dev/database-migrations.mdx b/apps/docs/content/docs/dev/database-migrations.mdx
index 70a42016f..b9a38cb84 100644
--- a/apps/docs/content/docs/dev/database-migrations.mdx
+++ b/apps/docs/content/docs/dev/database-migrations.mdx
@@ -9,7 +9,7 @@ You don't need to worry about migration when you're using VitNode, we will handl
When you install, update or remove a plugin, VitNode will automatically generate and apply the migration to your database.
-### Manual
+## Manual
If you're making a plugins and you don't want to restart the server, you can generate and apply the migration manually.
diff --git a/apps/docs/content/docs/dev/database-pagination.mdx b/apps/docs/content/docs/dev/database-pagination.mdx
new file mode 100644
index 000000000..0b782135a
--- /dev/null
+++ b/apps/docs/content/docs/dev/database-pagination.mdx
@@ -0,0 +1,156 @@
+---
+title: Pagination
+description: How to use cursor based pagination with database.
+---
+
+
+ VitNode doesn't support the `offset` pagination. You can create your own
+ offset pagination.
+
+
+## Data Transfer Object (DTO)
+
+We will create a `DTO` to create arguments and return values for the query.
+
+### Object
+
+Object used as container for fields to create return values query in GraphQL schema.
+
+```ts title="show/dto/show.args.ts"
+import { Field, Int, ObjectType } from '@nestjs/graphql';
+import { PageInfo } from 'vitnode-backend'; // [!code highlight]
+
+@ObjectType()
+export class ShowTest {
+ @Field(() => Int)
+ id: number;
+
+ @Field(() => String)
+ name: string;
+}
+
+@ObjectType()
+export class ShowTestObj {
+ @Field(() => [ShowTest])
+ edges: ShowTest[];
+
+ @Field(() => PageInfo) // [!code highlight]
+ pageInfo: PageInfo; // [!code highlight]
+}
+```
+
+### Arguments
+
+Arguments used as container for fields to create query in GraphQL schema.
+
+```ts title="show/dto/show.args.ts"
+import { ArgsType } from '@nestjs/graphql';
+import { PaginationArgs } from 'vitnode-backend'; // [!code highlight]
+
+@ArgsType()
+// [!code word:PaginationArgs]
+export class ShowTestArgs extends PaginationArgs {}
+```
+
+## Service
+
+Inside service file we will create a `show` method that will return a `ShowTestObj` object.
+
+```ts title="show/show.service.ts"
+import { Injectable } from '@nestjs/common';
+
+import { ShowTestArgs } from './dto/show.args';
+import { ShowTestObj } from './dto/show.obj';
+
+@Injectable()
+export class ShowTestService {
+ async show({ cursor, first, last }: ShowTestArgs): Promise {}
+}
+```
+
+### Initial values for pagination
+
+Use `inputPaginationCursor()` to create initial values for pagination.
+
+```ts title="show/show.service.ts"
+import { Injectable } from '@nestjs/common';
+// [!code word:inputPaginationCursor]
+import { inputPaginationCursor, SortDirectionEnum } from 'vitnode-backend';
+
+import { ShowTestArgs } from './dto/show.args';
+import { ShowTestObj } from './dto/show.obj';
+
+import { test_table } from '../../admin/database/schema/test';
+import { DatabaseService } from '@/database/database.service';
+
+@Injectable()
+export class ShowTestService {
+ constructor(private readonly databaseService: DatabaseService) {}
+
+ async show({ cursor, first, last }: ShowTestObj): Promise {
+ const pagination = await inputPaginationCursor({
+ cursor,
+ database: test_table,
+ databaseService: this.databaseService,
+ first,
+ last,
+ primaryCursor: {
+ column: 'id',
+ schema: test_table.id,
+ },
+ defaultSortBy: {
+ direction: SortDirectionEnum.asc,
+ column: 'created',
+ },
+ });
+ }
+}
+```
+
+### Query from database
+
+Use `findMany()` method to get the data from the database with `with` argument to get the related data.
+
+```ts title="show/show.service.ts"
+const edges = await this.databaseService.db.query.test_table.findMany({
+ ...pagination,
+});
+```
+
+#### Where argument
+
+If you want to use `where` argument you can pass it to the `findMany()` method using `and()` method from `drizzle-orm`.
+
+```ts title="show/show.service.ts"
+const where = lte(test_table.created, new Date()); // [!code highlight]
+
+const edges = await this.databaseService.db.query.test_table.findMany({
+ ...pagination,
+ where: and(pagination.where, where), // [!code highlight]
+});
+```
+
+### 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.
+
+
+```ts title="show/show.service.ts"
+const totalCount = await this.databaseService.db
+ .select({ count: count() })
+ .from(test_table);
+
+return outputPagination({ edges, totalCount, first, cursor, last });
+```
+
+If you have `where` argument you need to pass also to the `count()` query.
+
+```ts {4}
+const totalCount = await this.databaseService.db
+ .select({ count: count() })
+ .from(test_table)
+ .where(where); // [!code highlight]
+```
diff --git a/apps/docs/content/docs/dev/database-service.mdx b/apps/docs/content/docs/dev/database-service.mdx
index 507bdf096..34360e23e 100644
--- a/apps/docs/content/docs/dev/database-service.mdx
+++ b/apps/docs/content/docs/dev/database-service.mdx
@@ -3,10 +3,106 @@ title: Database in Service
description: How to use database in service
---
+To get access to the database in a service, you need to inject the `DatabaseService` into the service constructor.
+
+```ts title="show.service.ts"
+import { Injectable } from '@nestjs/common';
+
+import { DatabaseService } from '@/database/database.service';
+
+@Injectable()
+export class ExampleService {
+ constructor(private readonly database: DatabaseService) {} // [!code highlight]
+}
+```
+
## Query
-VitNode supports [query by Drizzle ORM](https://orm.drizzle.team/docs/rqb) in service.
+VitNode supports [query by Drizzle ORM](https://orm.drizzle.team/docs/rqb) in service to get data with relations from the database.
```ts title="show.service.ts"
+import { Injectable } from '@nestjs/common';
+
+import { DatabaseService } from '@/database/database.service';
+
+@Injectable()
+export class ExampleService {
+ constructor(private readonly database: DatabaseService) {}
+
+ async create(): Promise {
+ // [!code word:this.database.db.query.core_test_table.findMany]
+ const data = await this.database.db.query.core_test_table.findMany({
+ where: (table, { eq }) => eq(table.id, 1),
+ });
+ }
+}
+```
+
+Check [Drizzle ORM documentation - Query](https://orm.drizzle.team/docs/rqb) for more information.
+
+## Select
+
+```ts
+await this.database.db.select().from(core_test_table);
+```
+
+Check [Drizzle ORM documentation - Select](https://orm.drizzle.team/docs/select) for more information.
+
+## Insert
+
+```ts
+await this.database.db.insert(core_test_table).values({ name: 'Andrew' });
+```
+Check [Drizzle ORM documentation - Insert](https://orm.drizzle.team/docs/insert) for more information.
+
+## Update
+
+```ts
+await this.database.db
+ .update(core_test_table)
+ .set({ name: 'Mr. Dan' })
+ .where(eq(core_test_table.name, 'Dan'));
+```
+
+Check [Drizzle ORM documentation - Update](https://orm.drizzle.team/docs/update) for more information.
+
+## Delete
+
+```ts
+await this.database.db
+ .delete(core_test_table)
+ .where(eq(core_test_table.name, 'Dan'));
+```
+
+Check [Drizzle ORM documentation - Delete](https://orm.drizzle.team/docs/delete) for more information.
+
+## Operations
+
+[Drizzle ORM documentation - Operations](https://orm.drizzle.team/docs/operators) supports many operations like `eq`, `neq`, `gt`, `gte`, `lt`, `lte`, `in`, `nin`, `like`, `ilike`, `is`, `isNot`, `between`, `notBetween`, etc.
+
+## Joins
+
+```ts
+await this.database.db
+ .select()
+ .from(core_test_table)
+ .join(core_test_table2)
+ .on(eq(core_test_table.id, core_test_table2.id));
```
+
+Check [Drizzle ORM documentation - Joins](https://orm.drizzle.team/docs/delete) for more information.
+
+## Magic SQL
+
+You can use magic SQL to create raw SQL query with Drizzle ORM.
+
+```ts
+import { sql } from 'drizzle-orm';
+const id = 69;
+await db.execute(
+ sql`select * from ${usersTable} where ${usersTable.id} = ${id}`,
+);
+```
+
+Check [Drizzle ORM documentation - Magic SQL](https://orm.drizzle.team/docs/sql) for more information.
diff --git a/apps/docs/content/docs/dev/meta.json b/apps/docs/content/docs/dev/meta.json
index 9b9d11eef..0b786c07d 100644
--- a/apps/docs/content/docs/dev/meta.json
+++ b/apps/docs/content/docs/dev/meta.json
@@ -14,6 +14,7 @@
"database",
"database-migrations",
"database-service",
+ "database-pagination",
"--- GraphQL ---",
"graphql",
"modules",
diff --git a/packages/backend/src/functions/pagination.ts b/packages/backend/src/functions/pagination.ts
index 6d37ad469..0ab65b95f 100644
--- a/packages/backend/src/functions/pagination.ts
+++ b/packages/backend/src/functions/pagination.ts
@@ -165,6 +165,6 @@ export async function inputPaginationCursor({
return {
where,
orderBy,
- limit: first || last ? ((last ? last + 1 : first) ?? 0 + 1) : undefined,
+ limit: first || last ? ((last ? last + 1 : first) ?? 0) + 1 : undefined,
};
}