nextjs config image
20 تیر 1402,Api Wrapping In NextJs

Api Wrapping In NextJs

ApiNextJsApi WrappingRestApi

تووی این پست قصد داریم که بک شیوه جدید برای مدیریت Api ارائه کنیم.
همچنین قصد داریم در این وبلاگ در مورد  Api Wrapping  در  NextJs  صحبت کنیم.

آشنایی با Api

REST یک «شیوه معماری» یا «الگوی طراحی» برای APIها است. از زمان اختراع اینترنت، افراد برنامه‌های کاربردی و صفحات وب مختلفی برای کسب اطلاعات در خصوص منابع گوناگون را مورد استفاده قرار داده‌اند. اگرچه، تاکنون کم‌تر کسی به این مسئله توجه کرده است که این داده‌ها از کجا می‌آیند؟ این داده‌ها از سرورها (Server) دریافت می‌شوند و اپلیکیشن‌ها برای دریافت و ارسال داده‌ها با وب‌سرورها در ارتباط هستند. یک REST API راهی برای ارتباط دو سیستم کامپیوتری مثل یک مرورگر وب و سرورها از طریق HTTP است. در این مطلب به نحوه ارتباط یک کلاینت با سرور به سبک REST برای استخراج اطلاعات مورد نیاز پرداخته و به سوالاتی نظیر «REST چیست»، «RESTful API چیست» یا «REST API چیست» پاسخ داده شده است.

همونطور که خیلی هامون میدونیم، Api هارو (چه در ری اکت چه در نکست یا نود جی اس یا اکسپرس) با Axios مدیریت میکنن.

البته مدیریت api به 5 روش ممکنه که در یک پست جداگانه بیشتر به اون میپردازیم (همراه با قطعه کد😍).

وقتی صحبت از api و کار با اونها در فرانت (یا اپلیکیشنی میشه که با end user سروکار داره) میشه، خیلی مهمه که کد ما تمیز و خوانا باشه و همچنین قابل توسعه باشه ( Clean code و همچنین اصل Open/Close).

برای همین همیشه سعی کردم در پروژه هام یا از React Query استفاده کنم یا خودم یک کامپوننت یا هوکی بنویسم تا بتونم فول داینامیک از کدم همه جا استفاده کنم (و عملا هم همین شد، تووی همه پروژه هام قطعه کدی رو همیشه مینویسم که نمایانگر دستخط من بوده و مدیریت api ها رو بسیار راحت تر میکنه).

تا اینکه پارسال در پروژه ای برای اولین بار به ارور CORS برخورد کردم!

پروژه هایی که تا اونموقع انجام میدادم (به سبب اتفاق یا شانس یا هرچی نمیدونم😁😒)، معمولا سمت بک، دسترسیش باز بود و تا بحال برخورد نکرده بودم.

وقتی به این ارور خوردم با کلی سرچ، به این نتیجه رسیدم که باید از proxy در package.json استفاده کرد و نباید بصورت مستقیم api رو فراخوانی کرد. (برای ری کت)

proxy: "http://localhost:3000"

جلوتر که اومدم و با react query کار کردم، فهمیدم میشه یه مخزن درست کرد با کلی قابلیت خوب (یکیش مثل کاستوم کردن response های سمت سرور هست).

اما با پیشرفت در حوزه برنامه نویسی و آشنایی با نکست قضیه خیلی متفاوت شد!

بذارین دوباره برگردیم از اول!

مسئله چی بود؟ کسی یادش میاد؟🤔

درسته! میخواستیم به api وصل شیم.

شما وقتی میخواین یک api رو کال کنین، باید یه base url داشته باشین و سپس end point های مختلفی که تووی Swagger یا هرجای دیگه دارین رو به اون استرینگ اتچ کنین.

مثلا:

 https://api.nilldevelopers.com/api/login 

مثلا یه بار get user info یه بار post login params و.... فراخوانی های دیگه (با متدهای چهارگانه PUT, POST, GET, DELETE).

این نشون میده شما پارامترهای متغیر زیادی دارین (یا میشه اینجوری گفت، هر پارمترتون متاثر از یک عمله) اینجا اگه hard code هندل کرده باشی میری توو دیوار!

کد کثیف، باعث پیچیدگی میشه و خب هرچی برنامت بزرگتر میشه، این گلوله برف بزرگ و بزرگتر میشه تا جایی که دیگه نمیتونی توسعه بدی و به فکر نوشتن از اول میوفتی.

راه حل

کد تمیز برای فراخوانی و categorize کردن api ها.
اما نکته ای که باید اشاره میشد بهش و نشد این بود که:


’’  تووی ری اکت شما برای اینکه یک api که مشکل CORS داره رو مدیریت کنی دوراه داری: ‘‘

  • از پروکسی استفاده کنی در ری اکت یا از rewrite استفاده کنی در نکست

  • دسترسی دامنه خودتو در بکند باز کنی (که خطرناکه و پیشنهاد نمیشه)

 نکته: میشه base url رو مقدارشو بصورت hard code تعریف کرد، ولی از امنیت سایت رو میاره پایین چون مهاجم تمام موارد مورد نیاز برای شنود وبسایت شما رو داره! (پس از rewrite استفاده میکنیم).

در مورد rewite میتونی  اینجا  بیشتر مطالعه کنی 😉

بیا یکم بیشتر با طرز رفتار next در پوشه api آشنا بشیم (اصلا نگران نباش تمام چیزایی که توضیح دادیم از قبل تووی یک ریپازیتوری آماده برات گذاشتیم. فقط همراهمون باش تا بیشتر یاد بگیری).

خوشبختانه next در مدیریت api در middleware هم route base کار میکنه.
خب این ینی چی؟
ینی تو میتونی داخل پوشه api پوشه هایی داشته باشی که هر پوشه میشه یک end point از اون api هایی که بهت تووی swagger دادن و باید بهشون وصل شی.

این به همینجا ختم نمیشه! تو حتی میتونی داخل اون پوشه های هم مجددا پوشه تعریف کنی و دقیقا همون ساختاری که بهت تحویل دادند رو پیاده سازی کنی.
مورد مثبت دیگه اینکه میتونی فایلی بنام middleware.ts در روت app پروژت تعریف کنی و بگی به ازای درخواست ها برای تو لاگ ثبت کنه.
و حتی میتونی تعداد درخواست های کاربر رو محدود کنی (مثلا 10 در خواست در دقیقه، اگه بیشتر شد کاربر ban میشه برای مدتی)
خب حرف زدن کافیه تا اینجا!
موافق باشی کمی دست به کد بشیم.
اول از همه یه پروژه نکست ایجاد میکنیم:

npx create-next-app@latest

موقعی که داره کلون میکنه، گزینه app route رو yes بزن جلوتر بهش نیاز داریم. وقتی تموم شد، پروژه رو که با vscode باز کنی ساختار پروژه ای شبیه اسکرین شات زیر میبینی.

vscode screenshot

داخل فولدر app یه پوشه بنام api داریم من برای اینکه این مقاله کوتاه تر بشه که همه حوصله داشته باشیم تا تهش رو بخونیم، یک مثال رو کامل توضیح میدم، بقیش رو میسپارم به خودت و ریپازیتوری که نوشتم (این گوی و این میدان!).
داخل پوشه hello که خود نکست برات درست کرده، یک فایل ts هست (اگه در ابتدا هنگام کلون شدن تیک ts رو زده باشی) محتویات ابتدایی اون به اینصورته:

1export async function GET(request: Request) {
2    return new Response('Hello, Next.js!')
3}

خب ما چندتا مطلب رو میخواهیم اضافه کنیم:

  •  فچ کردن api مدنظر خودمون 
  •  مشاهده لاگ middleware 
  •  نوشتن متد POST, GET, DELETE, PUT 
  •  محدود کردن تعداد درخواست کاربر 

Fetch Api

خب بیاین گزینه اول رو پیاده سازی کنیم. برای اینکار باید از fetch استفاده کنیم:

1import {NextResponse} from 'next/server'
2const DATA_SOURCE_URL = 'https://jsonplaceholder.typicode.com/todos'
3
4export async function GET() {
5    const res = await fetch(DATA_SOURCE_URL)
6    const todos = await res.json()
7    return NextResponse.json(todos)
8}

 یک نکته ای در مورد فچ حتما رعایت کنین: 


’’ وقتی دارین دیتارو فچ میکنین حتما پارامتر control abort رو بنویسین و در نظر بگیرین. (در این مقاله نمیخوایم زیاد گسترش بدیم سخنمون رو برای همین پیشنهاد میکنم در مورد این مطلب سرچ کنین. برای استفاده من قطعه کدش رو قرار میدم. باشد که رستگار شوید!!) ‘‘

1const controller = new AbortController();
2const signal = controller.signal;
3fetch(apiUrl, { signal })
4.then((res) => res.json())
5    

خب برای تست بریم سراغ پستمن (اگه نداری، از اینجا میتونی به راحتی دانلودش کنی)

postman log screenshot

همونطور که در تصویر مشاهده میکنیم، api ما با موفقیت fetch شد. حالا برای استفاده اون در کد کافیه end point مورد نظرمون رو بعد از localhost قرار بدیم.

در حال حاضر end point ما:

 http://localhost:3000  /api  /hello 

هست
خب مثل اینکه غول مرحله اول با موفقیت شکست داده شد!😎💪

Middleware Log

تووی مرحله دوم ما میخواهیم فایل middleware.ts رو بنویسیم تا بتونیم لاگهای api مون رو مشاهده کنیم (دقت کنید، ما لاگ تمام end point هامون رو دریافت میکنیم چون تعریف میکنیم که لاگ اونایی رو به ما بده که بعد از دامنه، /api/<endpoint> دارند)
کدمون به شکل زیر میشه:

1import {NextResponse} from "next/server";
2
3
4const allowedOrigins = process.env.NODE_ENV === 'production'
5    ? ['upsite.com']
6    : ['http://localhost:3000','https://www.google.com']
7
8export function middleware(request: Request) {
9    // The configuration at the end of the file does the same thing.
10    //if (request.url.includes('/api/')){}
11    /*
12        const regex = new RegExp('/api/*')
13        if (regex.test(request.url)) {
14
15        }
16    */
17    const origin = request.headers.get('origin')
18    console.log(origin)
19
20    if (origin && !allowedOrigins.includes(origin)) {
21        return new NextResponse(null, {
22            status: 400,
23            statusText: 'Bad Request',
24            headers: {
25                'Content-Type': 'text/plain'
26            }
27        })
28    }
29
30    console.log('Middleware!')
31    console.log(request.method)
32    console.log(request.url)
33
34
35    return NextResponse.next()
36}
37
38// just print we call /api
39export const config = {
40    matcher: '/api/:path*'
41}

خب اینم از غول مرحله دوم! دیدی چه راحت شکستش دادیم :)))

CRUD Data

مرحله سوم از ما خواسته شد که GET, POST, DELETE, PUT بنویسیم. خب از این چهار تا، ما یکیشو نوشتیم. پس بریم سراغ سه تای دیگش.
برای نوشتن این سه متد کدمون به شرح زیره:

1export async function DELETE(request: Request) {
2    const {id} = await request.json()
3
4    if (!id) return NextResponse.json({"message": "Todo Id Requested"})
5    await fetch('https://jsonplaceholder.typicode.com/todos/id', {
6        method: 'DELETE',
7        headers: {
8            'Content-Type': 'application/json',
9            'API-Key': API_KEY
10        }
11    })
12
13
14    return NextResponse.json({'message': 'Todo id deleted!'})
15}
16
17export async function POST(request: Request) {
18    const {userId, title} = await request.json()
19
20    if (!userId || !title) return NextResponse.json({"message": "missng req data"})
21    const res = await fetch('https://jsonplaceholder.typicode.com/todos', {
22        method: 'POST',
23        headers: {
24            'Content-Type': 'application/json',
25            'API-Key': API_KEY
26        }
27        , body: JSON.stringify({
28            userId, title, completed: false
29        })
30    })
31    const newTodo = await res.json()
32
33    return NextResponse.json(newTodo)
34}
35
36export async function PUT(request: Request) {
37    const {userId, id, title, completed}: Todo = await request.json()
38
39    if (!userId || !id || !title ) return NextResponse.json({"message": "missng req data"})
40    const res = await fetch('https://jsonplaceholder.typicode.com/todos', {
41        method: 'PUT',
42        headers: {
43            'Content-Type': 'application/json',
44            'API-Key': API_KEY
45        }
46        , body: JSON.stringify({
47            userId, title, completed: false
48        })
49    })
50    const updatedTodo = await res.json()
51
52    return NextResponse.json(updatedTodo)
53}

Limit User REQUEST

رسیدیم به قصر پادشاه! سکانس آخر:
محدود کردن درخواست کاربر!
برای اینکه بخواهیم درخواست کاربر رو محدود کنیم ابتدا نیاز داریم که یک پکیج به نام limiter نصب کنیم:

npm install limiter

بد از این کار یک پوشه جدید در api ایجاد میکنیم به نام config 
داخل این پوشه ما باید فایلی داشته باشیم به نام limiter.ts پس زودتر دست به کارشو بسازش!

1import {RateLimiter} from "limiter";
2
3export const limiter = new RateLimiter({
4    tokensPerInterval: 3,
5    interval: 'min',
6    fireImmediately: true // remove token immediately
7})

شما وقتی سه تا درخواست ارسال میکنین، همه چیز اوکی هست ولی وقتی چهارمی رو سمت ما میفرستی، ما خط خطی تحویلت میدیم!
این نشون میده که باید مراقب باشی! ما درخواست های بیشتر از 3 تا در دقیقه رو چک میکنیم و میبندیم و دیگه پاسخ نمیدیم تا تایم مدنظر سپری بشه.
یکسری ممکنه بپرسن که این ممکنه کجا استفاده بشه ازش؟
خب میتونم بهتون بگم بهترین جایی که استفاده میشه در ثبت order های خرید و فروشه (جاهایی مثل بورس که تعداد درخواستها بسیار بالاست و شما میخواین باری که به سمت سرور میاد رو در هممون مبدا مدیریت کنین.)

postman ban api screenshot

خب، قصه ما به سر رسید، کلاغه به خونش نرسید!
امیدواریم بتونیم به زودی به لپتاپ ها (یا گوشی هاتون😁) برگردیم با مطالب بروزتر و کاربردی تر. :)))
آقا ما هنوزم سر قولمون هستیم و فراموش نکردیم!  لینک ریپو😁

icon drow down
خدمات ما
پروژه های اخیر
طراحی ست اداری شرکت hydout
طراحی Low Poly زومجی
طراحی UI/UX شرکت Rainbow
طراحی ست اداری شرکت hydout
طراحی ست اداری شرکت hydout
طراحی ست اداری شرکت hydout
طراحی ست اداری شرکت hydout
طراحی ست اداری شرکت hydout
طراحی ست اداری شرکت hydout
طراحی ست اداری شرکت hydout
طراحی ست اداری شرکت hydout
طراحی ست اداری شرکت hydout

پروژه جدید داری؟