نحوه مصرف GraphQL API با Apollo Client در یک برنامه Reactjs

اکنون که متوجه شدیم GraphQL چیست و چرا برخی از شرکت ها آن را به RESTful API ترجیح می دهند، بیایید در مورد مصرف نقاط پایانی GraphQL در برنامه Reactjs خود صحبت کنیم.
اگر قسمت 1 این پست وبلاگ را نخوانده اید، می توانید این کار را اینجا انجام دهید (کلیک کنید)
عملیات GraphQL
برای انجام عملیات های مختلف مانند:
جهش:
ارسال داده هایی که از رابط کاربری جمع آوری کرده اید به پایگاه داده.
پرس و جو:
در حال واکشی داده ها به UI
ما همچنین عملیات های دیگری مانند Subscription
، اما شما از Mutation و Query زیاد استفاده خواهید کرد.
قبل از شروع کار با داده ها، بیایید پروژه خود را تنظیم کرده و آن را پیکربندی کنیم.
اگرچه راههای زیادی برای کار با GraphQL API در Reactjs وجود دارد، مانند استفاده از متد Fetch، React query یا Axios، ما از هیچکدام از اینها استفاده نمیکنیم، بلکه از Apollo Client از Apollo GraphQL استفاده میکنیم.
آپولو کلاینت چیست؟
Apollo Client به شما امکان می دهد وضعیت داده های خود را با GraphQL مدیریت کنید. این به شما امکان می دهد بدون استفاده از هیچ گونه مدیریت حالت خارجی، نقاط پایانی GraphQL را مصرف کنید.
اشاره: اگر قبلاً با React Query کار کردهاید، احتمالاً ایده استفاده از Apollo Client برای مصرف نقاط پایانی GraphQL را دارید، زیرا آنها سینتکس یکسانی دارند و مشکل مشابهی را حل میکنند.
حالا، بیایید شروع کنیم!
اجرا کن npx create next-app app-name
برای بوت استرپ یک پروژه React با Nextjs.
آن را پاک کنید، به سطح اصلی پروژه خود بروید و یک فایل جدید به نام ایجاد کنید Apollo-client.js
اکنون بسته های Apollo Client و “Js-cookies” را نصب کنید:
yarn add @apollo/client js-cookie
این دستور برخی از وابستگی ها را در پروژه ما نصب می کند و ما از آن استفاده می کنیم js-cookie library
برای ذخیره و بازیابی داده ها در حافظه مرورگر ما.
که در Apollo-client.js
، این کد را برای وارد کردن همه چیزهایی که برای راه اندازی Apollo Client نیاز داریم قرار دهید:
import { ApolloClient, InMemoryCache, HttpLink, from } from "@apollo/client";
import { onError } from "@apollo/client/link/error";
import Cookies from 'js-cookie'
import { setContext } from "@apollo/client/link/context";
اینها به ما امکان میدهند خطاها را مدیریت کنیم، دادههای حافظه پنهان، بازیابی، مدیریت وضعیت محلی و راه دور عملیاتمان و بازیابی کوکیها را به ما بدهد.
بیایید نشانه هدر را تنظیم کنیم. ایده این است که وقتی یک کاربر به برنامه شما وارد می شود، یک نشانه دسترسی به او داده می شود و ما اکنون می خواهیم نشانه دسترسی را روی هدر تنظیم کنیم.
توجه: این برای اکثر پروژهها اجباری است، به خصوص اگر خصوصی باشد، زیرا فقط به افراد تأیید شده اجازه دسترسی برای تعامل با APIها داده میشود.
بنابراین خود را به روز کنید apollo-client.js
فایل با:
const authLink = setContext((_, { headers }) => {
// get the authentication token from local storage if it exists
const authToken = Cookies.get("secret__");
// return the headers to the context so httpLink can read them
return {
headers: {
...headers,
authorization: `Bearer ${authToken}`
}
}
});
فایل apollo-client.js را فعلا رها کنید، فایل دیگری به نام ایجاد کنید env.local
که برای ذخیره کلیدهای خصوصی و مقادیر استفاده می شود.
که در env.local
فایل، اجازه دهید نقطه پایانی GraphQL خود را ذخیره کنیم:
NEXT_PUBLIC_API_URL=https://your-graphql-api.com
حالا به apollo-client برگردید و این کد را اضافه کنید:
const httpLink = new HttpLink({
uri: `${process.env.NEXT_PUBLIC_API_URL}/graphql`,
credentials: "include",
});
بیایید هر گونه خطای احتمالی را مدیریت کنیم apollo-client.js
فایل، اضافه کنید:
const errorLink = onError(({ graphQLErrors, networkError }) => {
if (graphQLErrors)
graphQLErrors.forEach(({ message, locations, path }) =>
console.log(
`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`,
),
);
if (networkError) console.log(`[Network error]: ${networkError}`);
});
اکنون، باید هر بار که به این apollo-client.js نیاز داریم، در دسترس قرار دهیم. در همان فایل apollo-client.js، اضافه کنید:
const client = new ApolloClient({
cache: new InMemoryCache(),
connectToDevTools: true,
link: authLink.concat(httpLink),
});
// Export it
export default client;
ما راه اندازی فایل apollo-client را انجام دادیم. اما آیا به این خط در مشتری توجه می کنید:
connectToDevTools: true
این به شما امکان می دهد API GraphQL خود را در مرورگر خود که توسط Apollo Client طراحی شده است، کاوش کنید. اگر مقدار را درست بگذارید، یک Tab جدید به نام خواهید دید Apollo
وقتی صفحه را بررسی می کنید به برگه شما اضافه می شود.
کد کامل برای apollo-client.js
این است:
import { ApolloClient, InMemoryCache, HttpLink, from } from "@apollo/client";
import { onError } from "@apollo/client/link/error";
import Cookies from 'js-cookie'
import { setContext } from "@apollo/client/link/context";
const authLink = setContext((_, { headers }) => {
// get the authentication token from local storage if it exists
const authToken = Cookies.get("secret__");
// return the headers to the context so httpLink can read them
return {
headers: {
...headers,
authorization: `Bearer ${authToken}`
}
}
});
const httpLink = new HttpLink({
uri: `${process.env.NEXT_PUBLIC_API_URL}/graphql`,
credentials: "include",
});
const errorLink = onError(({ graphQLErrors, networkError }) => {
if (graphQLErrors)
graphQLErrors.forEach(({ message, locations, path }) =>
console.log(
`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`,
),
);
if (networkError) console.log(`[Network error]: ${networkError}`);
});
const client = new ApolloClient({
cache: new InMemoryCache(),
connectToDevTools: true,
link: authLink.concat(httpLink),
});
// Export it
export default client;
اگر در یک صفحه باشیم، اکنون میتوانیم کار با نقاط پایانی GraphQL را شروع کنیم.
بیا شروع کنیم با Mutation
نحوه انجام: جهش در GraphQL
جهش به سادگی به معنای تغییر یا تغییر است.
وقتی در حال جمعآوری دادهها از کاربر و ارسال آن به Backend هستید، چیزی در حال تغییر است.
به عنوان مثال، داده های چنین کاربری از حالت خالی به حالت پر شده در حال تغییر است.
بنابراین به طور کلی، عملیات جهش GraphQL در frontend به این معنی است که شما در حال ارسال داده هستید. و بیایید این را با ساختن یک ساده نشان دهیم Register
جزء.
یک فایل جدید در داخل فایل ‘component’ ایجاد کنید و آن را فراخوانی کنید RegisterUser.jsx
و فرم ثبت نام خود را قرار دهید.
// RegisterUser.jsx
const RegisterUser = () => {
const [registerData, setRegisterData] = useState({
name: " ",
age: " ",
password: " "
})
// handle form change
const handleChange = (e) => {
setRegisterData({...registerData, [e.target.name]: e.target.value})
}
// Handle submit function
const handleRegister = (e) => {
e.preventDefault()
try {
if(registerData) {
console.log(registerData)
}
} catch(e) {
console.log(e)
}
}
return (
<section>
<form on submit={handleRegister}>
<input name="name" value={registerData.name} />
<input name="age" value={registerData.age} onChange={handleChange} />
<input name="password" value={registerData.password}
onChange={handleChange}
/>
<button type="submit" >Register</button>
</form>
</section>
)
}
این یک فرم ساده است، می توانید خودتان آن را سبک کنید. مورد بعدی ارسال فرم به Backend است.
ما نقطه پایانی برای این نداریم، بیایید فرض کنیم یک نقطه پایانی داریم، در اینجا چیزی است که باید انجام دهیم:
ایجاد جهش
وارد کنید useMutation
قلاب ارائه شده توسط Apollo Client
داده های کاربر را به عنوان متغیر به جهش منتقل کنید.
اشاره: نوشتن پرس و جو یا نحو جهش می تواند زمان بر باشد زیرا باید مطمئن شوید که همه چیز درست است. اما جای نگرانی نیست، GraphQL درونگرا است، از این وب سایت برای مشاهده و نوشتن پرس و جوها و جهش های خود استفاده کنید:
https://studio.apollographql.com/
، فقط GraphQL API خود را قرار دهید و همه چیز را در آن API به شما نشان می دهد.
همچنین می توانید با کلیک بر روی قرار دادن پیوند API خود در مرورگر از زمین بازی استفاده کنید تا چیزی شبیه به این را ببینید:
بنابراین، شما 3 گزینه برای بررسی درونی نقطه پایان خود دارید: Apollo DevTools، Apollo Studio یا زمین بازی پیش فرض.
بیا ادامه بدهیم.
ایجاد جهش
برای ایجاد جهش، gql را وارد کنید.
در فایل ثبت نام:
import {gql} from @apollo/client;
// Create the mutation
const REGISTER_MUTATION = gql`
mutation Register($name: String!, $password: String!, $age: String!) { registerUser(registerInput: { name: $email, password: $password, age: $age}) {
_id
age
name
} } `;
قبل از اینکه ادامه دهیم، اجازه دهید جهش بالا را توضیح دهم.
ما جهش را به عنوان ذخیره می کنیم “REGISTER_MUTATION”
که می توانید از هر چیزی استفاده کنید. سپس، ما از gql
صادر شده از apollo-client برای سریال سازی جهش به قالب کد قابل فهم جاوا اسکریپت.
سپس این خط: mutation Register($name: String!, $password: String!, $age: String!)
به این متغیرها می گویند: ($name: String!, $password: String!, $age: String!)
، یعنی داده های مورد انتظار برای ارسال به backend. علامت تعجب ”!”
نشان می دهد که داده مورد نیاز است، اگر آن را به باطن ارسال نکنید، خواهید دید “BAD REQUEST”
خطا
و در نهایت، در صورت موفقیت آمیز بودن عملیات، مقداری از داده ها را برمی گردانیم:
{
_id
age
name
}
بازگرداندن این داده ها اختیاری است.
حالا بیایید ادامه دهیم.
- وارد کنید
useMutation
قلاب از کلاینت Apollo که برای اجرای این عملیات از آن استفاده خواهیم کرد. - جهش را با استفاده از هوک آغاز کنید.
- و در نهایت ایجاد کنید
handleSubmit
تابعی که این داده ها را به باطن ارسال می کند.
Import {useMutation} from “@apollo/client”
// Initiate the mutation like this:
const [RegisterUser, {data, error, loading}] = useMutation(REGISTER_MUTATION, {
variables: {
// pass the form state here as values to the variables
age: registerData.age
name: registerData.name
Password: registerData.password
}
});
// handle Form submit
const handleSubmit = async () => {
// call the mutation when submit button is clicked in a try-catch block
try {
await RegisterUser()
} catch(error) {
console.log(error)
}
}
به این خط توجه کنید: {data, error, loading}
، این ابزارهای تخریب شده پیش فرض است که توسط کلاینت آپولو به شما داده می شود تا پیشرفت درخواست شما را پیگیری کنید.
-
data
– داده های بازگردانده شده توسط عملیات -
error
– اگر هنگام اجرای عملیات خطایی وجود داشته باشد -
loading
– اگر درخواست هنوز در حال پردازش است.
میتوانید از این ویژگیها در کامپوننت خود استفاده کنید، برای مثال، اگر درخواست همچنان در حال بارگیری است، میتوانید دکمه ارسال را غیرفعال کنید و در صورت عدم موفقیت، خطا نمایش داده شود.
در نهایت، به دکمه ارسال فرم بروید و تابع را در آنجا فراخوانی کنید:
<button type="button" onClick={handleSubmit} disabled={loading}>Register</button>
تمام شد و کار تمام شد.
اکنون کد کامل به صورت زیر است:
import {gql, useMutation} from @apollo/client;
const REGISTER_MUTATION = gql`
mutation Register($name: String!, $password: String!, $age: String!) { registerUser(registerInput: { name: $email, password: $password, age: $age}) {
_id
age
name
} } `;
export const RegisterUser = () => {
const [registerData, setRegisterData] = useState({
name: " ",
age: " ",
password: " "
})
// handle form change
const handleChange = (e) => {
setRegisterData({...registerData, [e.target.name]: e.target.value})
}
// Initiate the mutation like this:
const [RegisterUser, {data, error, loading}] = useMutation(REGISTER_MUTATION, {
variables: {
// pass the form state here as values to the variables
age: registerData.age
name: registerData.name
Password: registerData.password
}
});
// handle Form submit
const handleSubmit = async () => {
// call the mutation when submit button is clicked in a try-catch block
try {
await RegisterUser()
} catch(error) {
console.log(error)
}
}
return (
<section>
{/*If there is an error, return the error message */}
{error && <p className=”error”>{error.message}</p>
<form>
<input name="name" value={registerData.name} />
<input name="age" value={registerData.age} onChange={handleChange} />
<input name="password" value={registerData.password}
onChange={handleChange}
/>
<button type="button" onClick={handleSubmit}>Register</button>
</form>
</section>
)
}
حالا بیایید در مورد Query صحبت کنیم:
چگونه: در GraphQL پرس و جو کنید
هر زمان که می خواهید داده ها را از باطن واکشی کنید، از Query استفاده می کنید و از یک قلاب از apolloClient به نام useQuery
.
درست مانند جهشی که در بالا انجام دادیم، از همان نحو در اینجا استفاده خواهد شد. اما شایان ذکر است که برخی از Queries ممکن است به متغیرها نیاز داشته باشند در حالی که برخی دیگر نیازی به متغیر ندارند.
برای نشان دادن این، من از نمونه نشانه گذاری Query استفاده می کنم.
با فرض اینکه می خواهید تمام کاربرانی که در وب سایت شما ثبت نام کرده اند را واکشی کنید، پرس و جو به این صورت خواهد بود:
{
AllUsers {
// Return the data you need
age,
name
}
}
در پرس و جوی بالا، ما هیچ متغیری را پاس نکردیم، فقط مستقیماً واکشی کردیم.
سناریویی که میتوانید از یک متغیر در Query استفاده کنید، زمانی است که دادهها را برای یک کاربر خاص واکشی میکنید. به عنوان مثال، فرض کنید می خواهید داده های یک کاربر را با کاربر دریافت کنید _id
، پرس و جو چیزی شبیه به این خواهد بود:
user(_id: “12”} {
age
name
}
در اینجا ما متغیر را مستقیماً به آن پاس می دهیم، بنابراین کاربر با شناسه 12 را می بینیم.
تا زمانی که این دو نمونه معتبر هستند، اجرای چنین پرس و جوی به عنوان یک توسعهدهنده فرانتاند تمرین خوبی نیست. تصور کنید که چگونه متغیر را سخت کد می کنیم، باید پویا باشد.
بنابراین بیایید از یک نمونه خوب استفاده کنیم، فرض کنیم می خواهیم همه کاربران و یک کاربر خاص را واکشی کنیم، کد کامل به شرح زیر است:
Import {useQuery, gql} from “@apollo/client”
// store the query here
Const FETCH_ALL_USERS = gql`
AppUsers {
AllUsers {
age
name
}
}
`
const AllUsers = () => {
const {data, error, loading} = useQuery(FETCH_ALL_USERS) // we don’t need to pass any variables
return (
<section>
<h2>name : {data.AllUsers.name} </h2>
<h2>age : {data.AllUsers.age} </h2>
</section>
)
و ما تمام شده ایم.
توجه داشته باشید، داده ها را فقط با گفتن برنگردانید data.name
در این سناریو قبل از بازگشت، ساختار داده پرس و جو را بررسی کنید.
در موردی که شما نیاز به ارسال یک متغیر دارید:
Import {useQuery, gql} from “@apollo/client”
// store the query here
Const FETCH_USER = gql`
SingleUser(_id: string!) {
User(_id: $_id) {
age
name
}
}
`
const User = () => {
const {data, error, loading} = useQuery(FETCH_USER, {
// pass the id as variable
variables: {
_id: USER_ID_HERE
}
})
return (
<section>
<h2>name : {data.User.name} </h2>
<h2>age : {data.User.age} </h2>
</section>
)
و ما خوب هستیم.
در سری قبلی گفتم ما می توانیم وضعیت عملکرد خود را ردیابی کنیم که یکی از مزایای استفاده از GraphQL بر روی RESTful API است و نحوه استفاده از آن را دیده ایم. data
، loading
، و error
ویژگیها، اما کلاینت آپولو امکانات بیشتری را در اختیار شما قرار میدهد مانند:
-
onCompleted
: در صورت موفقیت آمیز بودن عمل کاری انجام دهید -
onError
: اگر خطایی وجود دارد کاری انجام دهید
و بسیاری موارد دیگر، در اینجا نحوه استفاده از آن مانند variables
:
const {data, error, loading} = useQuery(FETCH_USER, {
// pass the id as variable
variables: {
_id: USER_ID_HERE
},
onCompleted() {
// trigger something like a success modal or snackbar here
},
onError() {
// trigger something like an error modal or snackbar here}
})
پس همین! برای کسب اطلاعات بیشتر در مورد GraphQL، من به شما توصیه می کنم اسناد Apollo GraphQL را در اینجا بررسی کنید، آنها بهترین اسنادی را دارند که می توانید به آنها اعتماد کنید.
ممنون که خواندید و فراموش نکنید که سوال بپرسید، این را با چیزی به اشتراک بگذارید و من را دنبال کنید