آموزش React NextJS و TypeScript با ساخت یک پروژه کامل

در این پست صفر تا صد ساخت و راه اندازی یک وبسایت را با ری‌اکت و تایپ اسکریپت یاد خواهیم گرفت. این یک پروژه متوسط رو به بالا است که طی آن با کدنویسی، طراحی دیتابیس و ریلیشن‌ها، احراز هویت کاربر، ارتباط دو طرفه با وب سوکت، کانفیگ وب سرور و پروکسی، دستورات لینوکس، نکات امنیتی و خیلی از نکات مهم راه اندازی یک وبسایت کامل، آشنا خواهیم شد.

سورس کد نهایی پروژه را می‌توانید در گیت هاب مشاهده کرده و دانلود کنید.

آموزش ویدیویی از تمام مراحل کار

برای نشان دادن تمامی جزئیات کار، ویدیویی از همه مراحل راه اندازی و کدنویسی این پروژه تهیه شده است:

پروسه کلی تقریبا به این صورت است:

  1. اول سرور و دامنه و محیط کدنویسی را آماده می‌کنیم.

  2. سپس بخش بک اند و API پروژه را کد‌نویسی می‌کنیم.

  3. سپس بخش فرانت و Client پروژه را کدنویسی می‌کنیم.

  4. و در نهایت پروژه را روی سرور بارگذاری و به دامنه متصل می‌کنیم.

آماده سازی محیط کدنویسی

من چون سیستم عامل ویندوز دارم از ترمینال جدید ویندوز و WSL که یه سیستم لینوکس روی ویندوز استفاده میکنم (نسخه Ubuntu رو نصب کردم). راه اندازیش نسبتا راحته و عملکردش بنظر من عالیه واقعا دست مایکروسافت درد نکنه.

windows subsystems for linux

از خود ویندوز یا مک یا لینوکس هم می تونید استفاده کنید و فرقی نداره. ادیتور هم VSCode استفاده میکنم که اونم توسط مایکروسافت ساخته شده. نکته پرو: برای vscode اکستنشن vim ـه که میشه فعالش کرد و از خیلی از قابلیت های vim داخل vscode استفاده کرد.

vim علاوه بر اینکه سرعت کار با کیبورد رو فوق العاده زیاد میکنه توی اکثر سرور های لینوکس موجوده و وقتی آدم کار باهاشو بلده خیلی راحت فایل ها رو روی سرور ویرایش میکنه. اگه خواستین بررسی کنین: آموزش آنلاین ویم و شبیه ساز ویم برای VSCode
(برای این پروژه ویم اصلا ضروری نیست)

زبان برنامه نویسی این پروژه TypeScript ـه که اساسا جاوا اسکریپته فقط سیستم تایپ چکینگ و اتوکامپلیت و کلی چیزای دیگه برای راحتی کدنویسی بهش اضافه شده. اگه تاحالا باهاش آشنا نشدین یا مقاومت کردین که آشنا نشین الان دیگه وقتشه قبل از شروع پروژه بررسیش کنین.

تهیه و آماده سازی سرور و دامنه

قبل از شروع بگم خرید سرور و دامنه کاملا آپشناله و فقط اگه خواستین به صورت آنلاین راه اندازی کنین لازمه.

خرید دامنه و سرور

برای شروع می تونیم یه سرور ابری با حداقل قیمت تهیه کنیم که کارمون رو راه بندازه. مزیت سرور ابری اینه که بسته به تعداد کاربران و نیازمون می تونیم منابع و هزینه رو کم یا زیاد کنیم. هر ارائه دهنده ای دوست داشتید می تونید انتخاب کنید مثلا برای این پروژه من از ابرآروان استفاده کردم چون نسب به ایرانسرور و … کمترین قیمت رو داشت. نسخه ubuntu هم انتخاب کردم که مطابق سیستمم باشه.

بعد از تهیه سرور با استفاده از ssh لاگین میشیم و مطمئن میشیم که به سرور دسترسی وجود داره و پکیج های مورد نیاز رو نصب می کنیم:

گیت:

sudo apt install -y git

وب سرور nginx:

sudo apt install -y nginx

آخرین ورژن nodejs (زمان نوشتن این 18 بود)

curl -sL https://deb.nodesource.com/setup_18.x | sudo -E bash -
sudo apt install -y nodejs

برای دامنه هم ارائه دهنده های زیادی وجود داره مثلا دامنه های .ir رو می تونید از سایت nic.ir تهیه کنید. برای من چون این پروژه صرفا آموزشیه می خوام از ساب دامنه یکی از دامنه های قبلیم استفاده کنم و دیگه دامنه جدید خریداری نکنم.

ساختار و کدنویسی پروژه

این پروژه دو تا بخش داره:

  1. بخش بک اند یا API که با TypeScript کدنویسی و در محیط NodeJS در سرور اجرا میشه.

  2. بخش کلاینت وب که اونم با TypeScript کدنویسی میشه ولی هم در محیط NodeJS و هم در محیط مرورگر اجرا میشه.

با این ساختار در آینده میشه خیلی راحت کلاینت های بیشتری مثل اندروید و ای او اس و … به پروژه اضافه کرد که همه از یک بک اند و API استفاده می کنند.

کدنویسی بخش کلاینت میتونه فقط برای مرورگر باشه و اجباری نیست که حتما در سرور هم اجرا بشه. علت اجرا کردنش در سرور (NodeJS) اینه که HTML و محتوای صفحه رو در سرور آماده کنیم و به مرورگر بفرستیم که سرعت و تجربه کاربری بهتری ارائه بدیم (به این میگن SSR). در غیر این صورت مرورگر بعد از باز کردن صفحه اول باید کد جاوا اسکریپت رو اجرا کنه و جاوا اسکریپت محتوا رو از API دریافت کنه و بعدش تازه برای کاربر نمایش بده (به اینم میگن CSR).

SSR در مقایسه با CSR

این موضوع هم کنده و هم برای SEO خوب نیست چون کار ربات سخت میشه اول باید جاوا اسکریپت اجرا کنه بعدا بتونه محتوای صفحه رو ببینه و ایندکس کنه.
(پروژه ما چون حالت محتوایی نداره SEO و SSR اهمیتی ندارن ولی تو خیلی از پروژه ها مثل فروشگاه، وبلاگ و … این قضیه خیلی مهمه پس جهت یادگیری من توضیح دادم)

git و ستاپ اولیه

برای سادگی هر دو بخش API و Client رو داخل یه Monorepo قرار میدیم به عبارت دیگه بجای اینکه هر بخش رو جدا داخل گیت ریپازیتوری (repository) خودش قرار بدیم کل پروژه رو یه ریپازیتوری می کنیم که اصطلاحا میگن Monorepo.

monorepo در مقایسه با multi repo

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

git init
git branch -M main
git remote add origin ...آدرس ریپازیتوری...

برای بخش کلاینت از Next.JS استفاده میکنیم که با این دستور کد های اولیه رو برای ما ایجاد میکنه:

npx create-next-app@latest --typescript

برای بخش API هم از TypeORM استفاده می کنیم:

npx typeorm init --name api --database sqlite

راستی اگه روی سیستمون git و nodejs نصب نباشه دستورات بالا اجرا نمیشه پس اول چک کنید که نصب باشن. حداقل نسخه موردنیاز نود برای nextjs موقع ساختن این آموزش 14 بود.

حالا دوتا فولدر next و api (یا هر اسمی که موقع اجرای دستورات وارد کرده باشین) با محتوای اولیه اش اضافه شدن به پروژه. بریم کامیت و پوش کنیم روی گیت هاب:

git add .
git commit -m "first commit"
git push -u origin main

بخش API پروژه

توی این بخش باید دیتابیس داشته باشیم، احراز هویت انجام بدیم و درخواست های http کلاینت (بخش next) مثل دریافت لیست پیام ها یا ثبت پیام جدید رو انجام بدیم. همچنین موقع ثبت پیام جدید باید از طریق وب سوکت به بقیه کاربرای آنلاین بفرستیمش. سورس این بخش در گیت هاب:

سورس کد بک اند چت آنلاین

برای دیتابیس از TypeORM و SQLite استفاده کردیم و دو تا مدل Message و User داریم که ارتباط one-to-many بینشون وجود داره. یعنی یه کاربر می تونه چندتا پیام داشته باشه. One User has Many Messages

برای پاسخ دهی به درخواست های http از express و برای ارتباط دو طرفه وب سوکت از ws استفاده کردیم (وب سوکت برای ارسال پیام جدید از سرور به مرورگره).

برای احراز هویت از jwt یا JSON Web Token و کوکی برای پیاده سازی سیستم Access Token و Refresh Token استفاده کردیم:

  • موقع ثبت نام و ورود اطلاعات کاربر رو تبدیل به دوتا توکن jwt می کنیم. یکی access و یکی refresh که تاریخ انقضای access خیلی کوتاه تنظیم شده مثلا 30 دقیقه و تاریخ انقضای refresh خیلی زیاده مثلا 1 سال.

  • توکن access رو میدیم به مرورگر و میگیم بده به جاوا اسکریپت که هر موقع درخواستی خواست بهمون بفرسته این توکن رو هم بفرسته که هویت کاربر رو بدونیم.

  • توکن refresh رو هم میدیم به مرورگر ولی میگیم نده به جاوا اسکریپت (بخاطر امنیت) و فقط خودت اتوماتیک وار موقع درخواست تمدید توکن access بفرستش به ما.

  • توی کلاینت و کد جاوا اسکریپت ما اون توکن access رو که موقع ثبت نام یا ورود گرفتیم میذاریمش توی یه متغیر لوکال که فقط توی حافظه باشه بدون اینکه ذخیرش کنیم مثلا تو localStorage یا کوکی نمیذاریمش یا بدون اینکه بذاریمش تو یه مغیر گلوبال مثل window چون اگه هکر یه راهی برای اجرای کد پیدا کرد (و واقعا اینکارو میکنن)، خیلی راحت می تونه محتوای localStorage یا کوکی و آبجکت های گلوبال رو بخونه و access توکن رو به دست بیاره. هر موقع خواستیم درخواستی بفرستیم این توکن رو هم باهاش میفرستیم.

  • حالا که توی کلاینت توکن access رو ذخیره نکردیم موقع رفرش کردن صفحه این توکن از دست میره و خب نمی خوایم که با هر رفرش کاربر رو مجبور کنیم مجددا ورود انجام بده. به همین خاطر همون درخواست تمدید توکن که قبل تر بهش اشاره کردم رو می فرستیم به API بدون هیچ توکنی ولی چون مرورگر یه refresh توکن داره که به ما نداده و اتوماتیک وار فقط موقع این درخواست اونو میفرسته، سمت API یه توکن access جدید به ما میده.

  • هر موقع هم توی کلاینت دیدیم توکن access منقضی شده، یه درخواست تمدید دیگه میفرستیم و توکن جدید میگیریم.

توکن access و توکن refresh سیستم احراز هویت

موقع انجام درخواست هایی که نیاز به احراز هویت داره، در مرورگر اطلاعات کاربر رو مثل آیدی و نام رو با یه هدر همراه درخواست اصلی به بک اند می فرستیم و تو بک اند اون هدر رو چک میکنیم و هویت کاربری که این درخواست رو انجام میده به دست میاریم.

چون هدر درخواست http می تونه هر چیزی باشه یه هکر می تونه مثلا آیدی یه کاربر دیگه رو از طریق اون هدر به ما ارسال کنه پس یه راهی میخوایم که از جعلی نبودن هدر مطمئن باشیم و JWT دقیقا برای همین کاره. اطلاعات رو سمت سرور تبدیل به توکن می کنیم و میدیمش به مرورگر که هر موقع درخواستی داشت اون توکن رو به ما بفرسته و سمت سرور اعتبار توکن رو کنترل می کنیم.

نمونه JWT:

json web token

جزئیات کدنویسیش رو توی ویدیو می تونید ببینید.

بخش Client پروژه

توی این بخش هم باید درخواست احراز هویت، ثبت و نمایش پیام ها و دریافت پیام جدید از طریق وب سوکت رو انجام بدیم. سورس این بخش در گیت هاب:

ساختار بخش کلاینت

اگه با React آشنا نیستین باید حتما اول اونو یه بررسی بکنین. NextJS هم یه فریم ورک بر پایه React ـه که کلی از کار ها رو برای ما انجام میده. مثل SSR و اجرا شدن سمت سرور یا Code Splitting یا سیستم Router و خیلی چیزای دیگه.

برای درخواست های http از fetch خود مرورگر و swr استفاده کردیم. وب سوکت هم توسط خود مرورگر پشتیبانی میشه و نیازی به اضافه کردن کتابخونه خاصی نداره. برای استایل و ظاهر صفحه هم از Tailwind CSS استفاده کردیم. اضافه کردن tailwind به nextjs به این صورته:

npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p

توضیحات و کدنویسی کامل همه بخش ها توی ویدیو هست اگه خواستین ببینین.

آپلود و راه اندازی روی سرور

بعد از اینکه پروژه آماده شد و همه کد ها و تغییرات رو پوش کردیم، وقت راه اندازی روی سروره. با ssh به سرور لاگین میشیم و پروژه رو از طریق گیت روی سرور قرار میدیم:

git clone ...آدرس ریپازیتوری...

هر دو بخش پروژه رو با npm ci نصب می کنیم و فایل های .env رو با مقادیر مناسب ایجاد می کنیم. بخش next قبل از اجرا شدن نیاز به بیلد یا ساخته شدن داره که با استفاده از npm run build انجام میدیم. در نهایت هر دو بخش رو با npm run start اجرا می کنیم. برای اینکه بعد از بستن ssh همچنان درحال اجرا بمونه میتونیم از nohup استفاده کنیم به این صورت

nohup npm run start &

البته راه بهترش اینه که از سرویس هایی مثل systemd یا supervisord استفاده کنیم. با این سرویس ها حتی اگه به علت خطا یا هر دلیلی دستور npm run start متوقف بشه میشه تنظیم کرد که خود به خود مجددا اجرا بشه.

تنظیم وب سرور Nginx

حالا اگه آی پی سرور رو با پورت 3000 باز کنیم باید سایت رو مشاهده کنیم. برای اتصال دامنه به بخش های api و next پروژه می تونیم از وب سرور nginx استفاده کنیم. پس از نصب nginx روی سرور، این تنظیمات رو به nginx اضافه می کنیم:

map $http_upgrade $connection_upgrade {
  default upgrade;
  '' "";
}
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Authorization $http_authorization;
proxy_pass_header Authorization;


server {
  listen 80;
  server_name <اسم دامنه>;

  location / {
    proxy_pass http://localhost:3000;
  }
}

server {
  listen 80;
  server_name api.<اسم دامنه>;

  location / {
    proxy_pass http://localhost:3500;
  }
}

تنظیمات nginx رو توی مسیر /etc/nginx/sites-enabled/ باید قرار بدیم. فقط HTTP و پورت 80 رو فعال کردیم چون برای HTTPS قراره از cdn استفاده کنیم و نیازی نیست روی سرور خودمون هم سرتیفیکیت و پورت 443 رو فعال کنیم.

تنظیمات دامنه، CDN و HTTPS

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

بعد از انتخاب cdn باید دامنه رو توی پنلش ثبت کنیم و رکورد های DNS مورد نیاز رو قرار بدیم تا به سرور وصل بشه. یک رکورد A با عنوان @ (این دامنه هست) و مقدار آیپی سرور و یک رکورد A دیگه با عنوان api (این ساب دامنه هست) و مقدار اون هم آیپی سرور.

رکورد های DNS دامنه

بعدشم باید NS هایی که به ما میده رو روی دامنه خودمون ست کنیم. معمولا از طریق همون جایی که دامنه رو خریدیم (مثل nic.ir) امکان تغییر ان اس های دامنه وجود داره. بعد از ثبت ان اس ها باید منتظر بمونیم تا اعمال بشن.

در آخر هم باید تنظیمات مربوط به HTTPS رو انجام بدیم البته باید دقت کنید که چون ما روی سرور فقط HTTP رو فعال کردیم ارتباط بین cdn و سرور باید HTTP انتخاب بشه:

تنظیمات HTTPS دامنه

موقع انجام این مراحل برای من خیلی چالش ها و باگ ها پیش اومده که توی ویدیو سعی کردم نشون بدم. اگه برای شما هم مشکلاتی پیش اومد یا هر سوالی داشتید خوشحال میشم از طریق بخش پرسش و پاسخ مطرح کنید تا اگه در حد توانم بود پاسخ بدم.

درنهایت امیدوارم این مطلب برای شما مفید باشه و حداقل چند تا نکته یاد گرفته باشید. ✋🌹


کامنت ها