前言:前幾天我分享了如何從自己開發的第一支使用者註冊GraphQL API,到前端Next.js應用串接所需的套件安裝及使用方式。今天,我們將開始將這個API串接到我們的應用程式中,讓它真正運作起來。
建立路由目錄和頁面文件
在app資料夾下建立sign-in資料夾
在sign-in資料夾下建立page.tsx文件
使用快捷指令rfc
建立頁面模板雛形
在page.tsx文件中輸入rfc
來建立React函式元件的模板雛形。這樣可以減少輸入起始手勢的時間,快速建立頁面基礎。
import { ApolloClient, InMemoryCache, createHttpLink } from "@apollo/client";
import { setContext } from "@apollo/client/link/context";
// NOTE zustand
import useTokenStore from "@/zustand/useTokenStore";
export const uri =
process.env.NODE_ENV === "production"
? "uri"
: "uri";
const httpLink = createHttpLink({
uri,
});
const authLink = setContext((_, { headers }) => {
// const token = "";
const token = useTokenStore.getState().token;
const authorization = token ? `Bearer ${token}` : "";
return {
headers: {
...headers,
authorization,
},
};
});
const cache = new InMemoryCache({
typePolicies: {
Query: {},
},
});
const client = new ApolloClient({
link: authLink.concat(httpLink),
cache,
});
export default client;
在app資料夾下創建ApolloProviderWrapper.tsx文件,來設置ApolloProvider
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";
import AppHeader from "@/components/Layouts/Basic/AppHeader";
import AppFooter from "@/components/Layouts/Basic/AppFooter";
import { noto_sans_tc, noto_sans_mono, noto_serif_tc } from "@/fonts";
import ApploProvider from "@/provider/Applo";
const inter = Inter({ subsets: ["latin"] });
import clsx from "clsx";
export const metadata: Metadata = {
title: "",
description: "",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="zh-TW">
<ApploProvider>
<body
className={clsx(
noto_sans_tc.className,
noto_sans_tc.variable,
noto_serif_tc.variable,
noto_sans_mono.variable,
inter.className
)}
>
<AppHeader />
<div className=" flex-1">
{children}
</div>
<AppFooter />
</body>
</ApploProvider>
</html>
);
}
sign-in
頁面中使用GraphQL API在page.tsx中添加GraphQL註冊請求
由於Next.js預設頁面是採用 use server
,不過這個頁面有使用到狀態,以及表單的處理,所以則需要在頁面最開頭加入 use client
宣告這個頁面客戶端渲染
"use client";
import React from "react";
import MemberLayout from "@/components/Layout/member";
import SectionTitle from "@/components/SectionTitle";
import { Controller, SubmitHandler, useForm } from "react-hook-form";
import { SchemaType, resolver, defaultValues } from "@/form/signIn";
import useMutaionLogin from "@/apollo/hooks/useMutationSignIn";
import { useRouter } from "next/navigation";
export default function Page() {
const { handleSubmit, control, reset } = useForm<SchemaType>({
resolver,
defaultValues,
});
const { push } = useRouter();
const [Mutation, { loading }] = useMutaionLogin();
return (
<MemberLayout>
<div className="min-h-screen h-full py-10 container">
<SectionTitle title="登入" subTitle="Login" />
<div className=" w-2/3 m-auto h-2/3 flex justify-center items-center">
<form onSubmit={handleSubmit(_onSubmit)}>
<label className="input input-bordered flex items-center gap-2 mb-3">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
className="w-4 h-4 opacity-70"
>
<path d="M8 8a3 3 0 1 0 0-6 3 3 0 0 0 0 6ZM12.735 14c.618 0 1.093-.561.872-1.139a6.002 6.002 0 0 0-11.215 0c-.22.578.254 1.139.872 1.139h9.47Z" />
</svg>
<label htmlFor="username">帳號</label>
<Controller
control={control}
name="username"
render={({ field, fieldState: { error } }) => {
return (
<>
<input
{...field}
id="username"
type="text"
className="grow input-lg input-primary"
placeholder=""
/>
{error?.message && (
<span className=" text-rose-600">{error?.message}</span>
)}
</>
);
}}
/>
</label>
<label className="input input-bordered flex items-center gap-2 mb-3">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
className="w-4 h-4 opacity-70"
>
<path
fillRule="evenodd"
d="M14 6a4 4 0 0 1-4.899 3.899l-1.955 1.955a.5.5 0 0 1-.353.146H5v1.5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1-.5-.5v-2.293a.5.5 0 0 1 .146-.353l3.955-3.955A4 4 0 1 1 14 6Zm-4-2a.75.75 0 0 0 0 1.5.5.5 0 0 1 .5.5.75.75 0 0 0 1.5 0 2 2 0 0 0-2-2Z"
clipRule="evenodd"
/>
</svg>
<label htmlFor="password">密碼</label>
<Controller
control={control}
name="password"
render={({ field, fieldState: { error } }) => {
return (
<>
<input
{...field}
type="password"
id="password"
className="grow"
/>
{error?.message && (
<span className=" text-rose-600">{error?.message}</span>
)}
</>
);
}}
/>
</label>
<button type="submit" className="btn btn-primary">
登入
</button>
</form>
</div>
</div>
</MemberLayout>
);
}
現在,你的Next.js應用已經能夠串接到GraphQL API,並且可以進行使用者註冊了。這樣,你就完成了從API開發到前端串接的整個流程。希望這個過程能夠對你有所幫助!
參考資源
JWT Token
Next.js
Layout Children Props