iT邦幫忙

2023 iThome 鐵人賽

DAY 16
0
Modern Web

由前向後,從前端邁向全端系列 第 16

16.【從前端到全端,Nextjs+Nestjs】使用Mock來模擬GraphQL

  • 分享至 

  • xImage
  •  

文章重點

  • 修改AppProvider以使用新的apollo-client.ts檔案
  • 在graphql-mocks.ts中創建模擬數據和GraphQL型別
  • 使用@apollo/clientuseQuery,展示如何在前端客戶端查詢模擬數據

本文

我們首先將設置apollo client給抽取出來,並放在apps\iron-ecommerce-next\libs\graphql\apollo-client.ts:

import { mockedCartItems, mockedProducts } from "./graphql-mocks";
import schema from "./schema.graphql";
import { ApolloLink, HttpLink } from "@apollo/client";
import {
	NextSSRApolloClient,
	NextSSRInMemoryCache,
	SSRMultipartLink
} from "@apollo/experimental-nextjs-app-support/ssr";

const mockedCache = new NextSSRInMemoryCache({
	typePolicies: {
		Query: {
			fields: {
				getProducts: {
					read() {
						return mockedProducts();
					}
				},
				getCartItems: {
					read() {
						return mockedCartItems();
					}
				}
			}
		}
	}
});

export const makeGraphqlClient = () => {
	const httpLink = new HttpLink({
		uri: "http://localhost:4200/graphql"
	});

	return new NextSSRApolloClient({
		cache: process.env.NODE_ENV === "development" ? mockedCache : new NextSSRInMemoryCache(),
		link:
			typeof window === "undefined"
				? ApolloLink.from([
						new SSRMultipartLink({
							stripDefer: true
						}),
						httpLink
				  ])
				: httpLink,
		typeDefs: schema
	});
};

由於我們import了graphql的檔案格式,我們需要定義types。(Link)我們創建graphql.d.ts在apps\iron-ecommerce-next\typings\graphql.d.ts:

declare module "*.graphql" {
	import { DocumentNode } from "graphql";
	const Schema: DocumentNode;

	export = Schema;
}

並修改我們的AppProvider,打開apps\iron-ecommerce-next\app\app-provider.tsx並修改

"use client";

import {
	ApolloNextAppProvider
} from "@apollo/experimental-nextjs-app-support/ssr";
import { CSSProvider } from "@master/css.react";
import { Theme } from "@radix-ui/themes";
import config from "master.css";
import { RecoilRoot } from "recoil";
import { makeGraphqlClient } from "../libs/graphql/apollo-client";

export default function AppProvider({ children }: { children: React.ReactNode }) {
	return (
		<ApolloNextAppProvider makeClient={makeGraphqlClient}>
			<RecoilRoot>
				<Theme>
					<CSSProvider config={config}>{children}</CSSProvider>
				</Theme>
			</RecoilRoot>
		</ApolloNextAppProvider>
	);
}

接著我們創建apps\iron-ecommerce-next\libs\graphql\graphql-mocks.ts:

import { makeVar } from "@apollo/client";
import { faker } from "@faker-js/faker";

export const mockedProducts = makeVar(
	Array(10)
		.fill(0)
		.map(() => ({
			id: faker.datatype.uuid(),
			name: faker.commerce.productName(),
			price: parseFloat(faker.commerce.price()),
			description: faker.commerce.productDescription(),
			imageUrl: faker.image.url()
		}))
);

export const mockedCartItems = makeVar(
	Array(10)
		.fill(0)
		.map(() => ({
			productId: faker.string.uuid(),
			productName: faker.commerce.productName(),
			price: parseFloat(faker.commerce.price()),
			quantity: faker.number.int({ min: 1, max: 5 })
		}))
);

打開apps\iron-ecommerce-next\app\products\products.client.tsx

"use client";

import { gql, useQuery } from "@apollo/client";
import { Flex } from "@radix-ui/themes";
import { useCartActions } from "../../store/actions/cart.actions";
import { Product } from "../../store/schemas/product.schema";

import ProductCard from "libs/iron-components/src/lib/ProductCard";
import { Get_ProductsQuery } from "../../__generated__/generated-hooks";

interface ProductsProps {
	products: Product[];
}

const ProductsClient = ({ products }: ProductsProps) => {
	const { addToCart } = useCartActions();
	const {data} = useQuery<Get_ProductsQuery>(gql`
		query GET_PRODUCTS {
			getProducts @client {
				id
				name
				description
				price
				imageUrl
			}
		}
	`);
	console.log(data);

	return (
		<Flex align="center" justify="center">
			<section className="w:60% p:1rem flex flex:wrap flex-direction:row gap:1rem jc:center">
				{products.map((product, i) => (
					<ProductCard
						key={product.id}
						title={product.name}
						price={`$${product.price.toFixed(2)}`}
						description={product.description}
						width="30%"
						imageUrl={product.imageUrl}
						onAddToCart={() =>
							addToCart({
								productId: product.id,
								productName: product.name,
								quantity: 1,
								price: product.price
							})
						}
					/>
				))}
			</section>
		</Flex>
	);
};

export default ProductsClient;

執行並觀看

 pnpm exec nx run iron-ecommerce-next:serve

https://ithelp.ithome.com.tw/upload/images/20231001/201089312SkIC3fEEl.png


總結

在本文中,我們展示了如何使用模擬數據來模擬真實的API回應


上一篇
15.【從前端到全端,Nextjs+Nestjs】在Nextjs 13設置並使用GraphQL
下一篇
17.【從前端到全端,Nextjs+Nestjs】加入測試
系列文
由前向後,從前端邁向全端30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言