Browse Source

docs: Add paginated list guide

Michael Bromley 2 years ago
parent
commit
5b1426fe30

+ 0 - 1
docs/docs/guides/how-to/multi-vendor-marketplaces/index.md

@@ -1,6 +1,5 @@
 ---
 title: "Multi-vendor Marketplaces"
-showtoc: true
 ---
 
 # Multi-vendor Marketplaces

+ 192 - 0
docs/docs/guides/how-to/paginated-list/index.mdx

@@ -0,0 +1,192 @@
+---
+title: "Paginated lists"
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+Vendure's list queries follow a set pattern which allows for pagination, filtering & sorting. This guide will demonstrate how
+to implement your own paginated list queries.
+
+## API definition
+
+Let's start with defining the GraphQL schema for our query. In this example, we'll image that we have defined a [custom entity](/guides/developer-guide/database-entity/) to
+represent a `ProductReview`. We want to be able to query a list of reviews in the Admin API. Here's how the schema definition
+would look:
+
+```ts title="src/plugins/reviews/api/api-extensions.ts"
+import gql from 'graphql-tag';
+
+export const adminApiExtensions = gql`
+type ProductReview implements Node {
+  id: ID!
+  createdAt: DateTime!
+  updatedAt: DateTime!
+  product: Product!
+  productId: ID!
+  text: String!
+  rating: Float!
+}
+
+type ProductReviewList implements PaginatedList {
+  items: [ProductReview!]!
+  totalItems: Int!
+}
+
+# Generated at run-time by Vendure
+input ProductReviewListOptions
+
+extend type Query {
+   productReviews(options: ProductReviewListOptions): ProductReviewList!
+}
+`;
+```
+
+Note that we need to follow these conventions:
+
+- The type must implement the `Node` interface, i.e. it must have an `id: ID!` field.
+- The list type must be named `<EntityName>List` and must implement the `PaginatedList` interface.
+- The list options input type must be named `<EntityName>ListOptions`.
+
+Given this schema, at runtime Vendure will automatically generate the `ProductReviewListOptions` input type, including all the
+filtering & sorting fields. This means that we don't need to define the input type ourselves.
+
+## Resolver
+
+Next, we need to define the resolver for the query.
+
+```ts title="src/plugins/reviews/api/admin.resolver.ts"
+import { Args, Query, Resolver } from '@nestjs/graphql';
+import { Ctx, PaginatedList, RequestContext } from '@vendure/core';
+
+import { ProductReview } from '../entities/product-review.entity';
+import { ProductReviewService } from '../services/product-review.service';
+
+@Resolver()
+export class ProductReviewAdminResolver {
+    constructor(private productReviewService: ProductReviewService) {}
+
+    @Query()
+    async productReviews(
+        @Ctx() ctx: RequestContext,
+        @Args() args: any,
+    ): Promise<PaginatedList<ProductReview>> {
+        return this.productReviewService.findAll(ctx, args.options || undefined);
+    }
+}
+```
+
+## Service
+
+Finally, we need to implement the `findAll()` method on the `ProductReviewService`. Here we will use the
+[`ListQueryBuilder`](/reference/typescript-api/data-access/list-query-builder/) to build the list query. The
+`ListQueryBuilder` will take care of
+
+```ts title="src/plugins/reviews/services/product-review.service.ts"
+import { Injectable } from '@nestjs/common';
+import { InjectConnection } from '@nestjs/typeorm';
+import { ListQueryBuilder, ListQueryOptions, PaginatedList, RequestContext } from '@vendure/core';
+
+import { ProductReview } from '../entities/product-review.entity';
+
+@Injectable()
+export class ProductReviewService {
+    constructor(
+        private listQueryBuilder: ListQueryBuilder,
+    ) {}
+
+    findAll(ctx: RequestContext, options?: ListQueryOptions<ProductReview>): Promise<PaginatedList<ProductReview>> {
+        return this.listQueryBuilder
+            .build(ProductReview, options, { relations: ['product'], ctx })
+            .getManyAndCount()
+            .then(([items, totalItems]) => ({ items, totalItems }));
+    }
+}
+```
+
+## Usage
+
+Given the above parts of the plugin, we can now query the list of reviews in the Admin API:
+
+
+<Tabs>
+<TabItem value="Query" label="Query" default>
+
+```graphql
+query {
+  productReviews(
+    options: {
+      // highlight-start
+      skip: 0
+      take: 10
+      sort: {
+        createdAt: DESC
+      }
+      filter: {
+        rating: {
+          between: { start: 3, end: 5 }
+        }
+      }
+      // highlight-end
+  ) {
+    totalItems
+    items {
+      id
+      createdAt
+      product {
+        name
+      }
+      text
+      rating
+    }
+  }
+}
+```
+
+</TabItem>
+<TabItem value="Response" label="Response" >
+
+```json
+{
+  "data": {
+    "productReviews": {
+      "totalItems": 3,
+      "items": [
+        {
+          "id": "12",
+          "createdAt": "2023-08-23T12:00:00Z",
+          "product": {
+            "name": "Smartphone X"
+          },
+          "text": "The best phone I've ever had!",
+          "rating": 5
+        },
+        {
+          "id": "42",
+          "createdAt": "2023-08-22T15:30:00Z",
+          "product": {
+            "name": "Laptop Y"
+          },
+          "text": "Impressive performance and build quality.",
+          "rating": 4
+        },
+        {
+          "id": "33",
+          "createdAt": "2023-08-21T09:45:00Z",
+          "product": {
+            "name": "Headphones Z"
+          },
+          "text": "Decent sound quality but uncomfortable.",
+          "rating": 3
+        }
+      ]
+    }
+  }
+}
+```
+
+</TabItem>
+</Tabs>
+
+In the above example, we are querying the first 10 reviews, sorted by `createdAt` in descending order, and filtered to only
+include reviews with a rating between 3 and 5.