بناء تطبيقات الويب الكاملة (Full Stack) باستخدام حزمة Reno Expo المبسطة
مقدمة: تبسيط رحلة بناء التطبيقات المتكاملة
قد يكون الشروع في بناء أي مشروع جديد من الصفر مهمة شاقة ومربكة. فقبل أن تتمكن حتى من البدء في كتابة الأكواد لاختبار فكرتك، هناك العديد من القرارات التي يجب اتخاذها. كيف ستبني الواجهة الأمامية (front end)؟ هل ستعتمد على CSS عادي، أم إطار عمل (framework)؟ هل ستستخدم HTML و JavaScript الخام، أم مكتبة أو إطار عمل مثل Vue أو React أو Angular أو Svelte؟ وما هي لغة الواجهة الخلفية (back end) التي ستستخدمها؟ هل هي JavaScript أو Ruby أو PHP أو Python أو شيء آخر؟ ربما تفضل نهج «بلا خادم» (serverless)؟
وماذا عن قاعدة البيانات؟ هل ستكون علائقية (Relational) مثل MySQL أو PostgreSQL؟ أم غير علائقية (NoSQL) مثل Mongo؟ أو ربما شيء «سهل» مثل Firebase؟ وكيف ستتعامل مع المصادقة (authentication)؟ ربما تكامل Passport الذي يجمع شاشات تسجيل الدخول عبر Facebook و Google و Github و LinkedIn؟
في كل مرة تراودني فكرة رائعة لتطبيق صغير أرغب في بنائه بنفسي، أشعر بالإرهاق بسبب كثرة الخيارات والقرارات التي يجب اتخاذها. لذلك، خصصت بعض الوقت للتفكير في حزمة التطوير المثالية (ideal stack) بالنسبة لي، بما في ذلك اعتبارات المصادقة والنشر، وجمعت كل ذلك في حزمة واحدة سهلة الإعداد نسبيًا: Reno Expo.
ما هو Reno Expo؟
يرمز Reno Expo إلى React و Node.js و Express و PostgreSQL. كما يستخدم Sequelize كـ ORM (مخطط الكائنات العلائقي) لقاعدة البيانات. في جوهره، هو تطبيق Express بسيط جدًا يحتوي على تطبيق Create React App مدمج للواجهة الأمامية. تم تصميمه ليتم نشره على Heroku ويحتوي على واجهة بسيطة جدًا لتسجيل المستخدمين الجدد وتسجيل الدخول، باستخدام JWT (رموز الويب JSON) للمصادقة.
وبصرف النظر عن ذلك، فهو بمثابة لوحة بيضاء تمامًا. لقد تركته فارغًا عمدًا فيما يتعلق بتصميم CSS، حتى أتمكن من توصيل أي مكتبات تصميم أريدها أو كتابة CSS الخاص بي حسب الحاجة. بالإضافة إلى الإصدار الخام تمامًا من Reno Expo، قمت أيضًا بإنشاء مشروع freeCodeCamp، وهو «المكتبة الشخصية» (Personal Library)، باستخدام هذه الحزمة. وهو بمثابة مثال لكيفية دمج إطار عمل CSS، وهو Ant Design في هذه الحالة، ويوفر أيضًا بعض الأمثلة لتوسيع قاعدة البيانات باستخدام ترحيلات Sequelize.
أين يمكنني الحصول على Reno Expo؟
يمكن العثور على كود التطبيقين هنا:
يحتوي كل منهما على ملف README.md مفصل، ولكنني سأشرح أيضًا كيفية البدء بكليهما، وسأوضح كيف يبني التطبيق الأخير على حزمة البدء في هذه المقالة.
كيف تبدو التطبيقات؟
يمكن العثور على أمثلة لكلا التطبيقين هنا:
حزمة البدء بسيطة جدًا من الناحية الجمالية. كما ذكرت سابقًا، تحتوي على الحد الأدنى من التصميم، ولم أكن أمزح. تُظهر «المكتبة الشخصية» كيف يمكن للمرء دمج إطار عمل CSS للحصول على بعض التحسينات السهلة في التصميم بأقل جهد.
البدء مع Reno Expo: المتطلبات والإعداد الأولي
للعمل مع Reno Expo، ستحتاج إلى تثبيت ما يلي على جهاز الكمبيوتر المحلي الخاص بك:
gitNodenpm(يتم تجميعه مع تحميلNodeالخاص بك)PostgreSQL
استخدم أحدث الإصدارات من كل منها إذا كنت تبدأ من الصفر، ولكن إذا كان لديك بالفعل إصدارات أخرى من هذه البرامج على نظامك، فقد تعمل بشكل جيد. للتسجيل، لقد كنت أطور باستخدام هذه الإصدارات: Node 8.16 و npm 6.14 و Postgres 10، وكانت عمليات النشر الخاصة بي على Heroku هي أحدث الإصدارات المستقرة من كل هذه البرامج. إذا واجهت مشكلات في استخدام إصدارات مختلفة، فاستخدم هذه الإصدارات أو حاول البحث عن الاختلافات في سجلات التغيير المناسبة لمساعدتك في حل المشكلة.
سيتطلب تثبيت Postgres الخاص بك أيضًا مستخدمًا صالحًا لديه امتيازات إنشاء قاعدة البيانات. إعداد هذا خارج نطاق هذه المقالة، ولكن يمكنك العثور على الأدلة ذات الصلة ببيئتك من خلال البحث عبر الإنترنت عن «getting started with postgres windows/mac/ubuntu» وما إلى ذلك.
تثبيت حزمة البدء: الواجهة الأمامية والخلفية
لتهيئة حزمة البدء، سنستخدم طرفيتين (terminals). لاحقًا، سأشارك خدعة لتشغيل بيئة التطوير الخاصة بك من طرفية واحدة، ولكن في الوقت الحالي سنبقي الواجهة الأمامية والخلفية منفصلتين.
إعداد الواجهة الخلفية (Backend Setup)
في الطرفية الأولى، ومن الدليل الذي ترغب في إنشاء تطبيقك الجديد فيه، قم بتنفيذ الأمر التالي لاستنساخ المستودع:
git clone git@github.com:JacksonBates/reno-expo.git
ثم انتقل إلى هذا المجلد الجديد:
cd reno-expo
قم بإنشاء نسخة من ملف .env.example:
cp .env.example .env
ستحتاج إلى تعديل متغيرات التطوير في ملف .env الجديد. إليك مثال على التكوين:
DEVELOPMENT_DATABASE=database_development
DEVELOPMENT_DATABASE_USERNAME=sequelize
DEVELOPMENT_DATABASE_PASSWORD=password
يمكن أن تكون قاعدة بيانات التطوير أي اسم تريده، ولكن يجب أن يتطابق اسم المستخدم وكلمة المرور مع ما قمت بتكوينه لتثبيت Postgres المحلي الخاص بك.
الآن قم بتثبيت حزم npm للواجهة الخلفية:
npm i
بعد ذلك، سنقوم بإنشاء قاعدة البيانات باستخدام Sequelize. ملاحظة: إذا لم يتم إعداد تثبيت Postgres الخاص بك بشكل صحيح، فهذا هو أول جزء سيتعثر…
npx sequelize-cli db:create
سيؤدي هذا إلى إنشاء قاعدة البيانات بالاسم الذي حددته في ملف .env الخاص بك. الآن يمكننا إنشاء الجدول لمستخدمينا:
npx sequelize-cli db:migrate
إذا نجح هذا، يجب أن ترى بعض مخرجات الطرفية مثل:
== 20200606113054-create-user: migrating =======
== 20200606113054-create-user: migrated (0.074s)
إذا نجح كل هذا، يمكنك الآن بدء تشغيل خادم الواجهة الخلفية باستخدام npm start.
إعداد الواجهة الأمامية (Frontend Setup)
يجب أن يكون إعداد الواجهة الأمامية أبسط. في الطرفية الثانية، انتقل إلى مجلد client:
cd reno-expo/client
قم بتثبيت وحدات Node:
npm i
الآن قم بتشغيل تطبيق React باستخدام npm start.
تشغيل التطبيق من طرفية واحدة
إذا تم تهيئة كل شيء بشكل صحيح، في المستقبل يمكنك بسهولة تشغيل كل من تطبيق React وخادم Express بأمر واحد من طرفية واحدة:
npm run dev
التحقق من عمل التطبيق
في متصفحك المفضل، قم بزيارة localhost:3000 وسترى صفحة «Home» أساسية جدًا وبعض الروابط إلى صفحة Admin و Login. يجب أن تكون صفحة Admin مقفلة حتى تقوم بتسجيل الدخول. سيتطلب منك تسجيل الدخول إنشاء حساب مستخدم أولاً. انقر على «I don't have an account» وقم بإنشاء حساب عبر نموذج التسجيل. يمكنك الآن تسجيل الدخول واختبار وصولك إلى صفحة Admin. إذا كان كل شيء يعمل، يمكنك البدء في تطوير تطبيقك!
بناء تطبيقات أكثر تعقيدًا باستخدام Reno Expo
لإنشاء «المكتبة الشخصية» (Personal Library)، كانت هناك 3 أشياء رئيسية يجب القيام بها:
- تثبيت وتطبيق إطار عمل
CSSالخاص بـAnt Design. - إنشاء مسارات (
routes) / طرق عرض (views) جديدة للواجهة الأمامية. - توسيع واجهة برمجة التطبيقات (
API) بنماذج (database models) ووحدات تحكم (controllers) جديدة لقاعدة البيانات.
تكامل Ant Design
بعد تثبيت Ant Design باستخدام npm i antd، أضفت السطر التالي إلى ملف App.css الموجود في مجلد client/styles:
@import"~antd/dist/antd.css";
يضمن هذا أن يكون تصميم Ant Design متاحًا في جميع أنحاء التطبيق. يحتوي مستودع «المكتبة الشخصية» على جميع التعديلات، ولكن إليك بعض الأمثلة على الأنماط التي يمكنك استخدامها. بالطبع، يمكنك كتابة CSS الخاص بك، أو استخدام أطر عمل أخرى مثل Material-UI أو Bootstrap أو غيرها، وما يلي هو مجرد توضيح.
تطبيق تخطيط الواجهة (Layout Implementation)
إليك مثال على كيفية تطبيق تخطيط أساسي باستخدام Ant Design:
import React from "react";
import { Layout, Menu } from "antd";
import { Link } from "react-router-dom";
import { useAuth } from "../../context/auth";
const { Content, Sider, Footer } = Layout;
export default function AppLayout(props) {
const { setAuthTokens } = useAuth();
const logout = (e) => {
e.preventDefault();
setAuthTokens();
localStorage.removeItem("tokens");
};
return (
{props.children}
);
}
بصرف النظر عن وظيفة صغيرة تتعامل مع رمز المصادقة (auth token)، يستخدم باقي هذا الكود المكونات التي يوفرها Ant Design لإنشاء تطبيق بشريط جانبي ثابت للتنقل، ومحتوى يتم عرضه ديناميكيًا اعتمادًا على المكون النشط.
تحميل المكونات النشطة: فهم خاصية children
أين يتم تحميل المكون النشط؟ إليك مثال على مكون PublicRoute:
import React from "react";
import { Route } from "react-router-dom";
import { AppLayout } from "../layouts";
export default function PublicRoute({ children, ...rest }) {
return (
{children}
);
}
لدينا أعلاه مثال على مكون PublicRoute. هناك بعض مكونات المسار الأخرى التي أستخدمها، ولكن فهمها يجب أن يكون مباشرًا بما فيه الكفاية بناءً على هذا المكون. PublicRoute الخاص بنا هو مكون <Route> من React-Router يلتف حول التخطيط من الأعلى. يعرض ملف App.js أمثلة على استخدام هذه المسارات العامة، على سبيل المثال:
<PublicRoute exact path="/">
<Home />
</PublicRoute>
في الملفين الأولين، يمكننا رؤية إشارة إلى children. children هي خاصية مدمجة في React تشير إلى المكونات الفرعية المتداخلة داخل المكونات الأصلية. في الأمثلة أعلاه، نرى مكون <Home> كمكون فرعي لـ PublicRoute. في ملف PublicRoute.js، نرى الإشارة إلى children، سواء في الخصائص أو يتم تمريرها مباشرة إلى مكون <AppLayout>. وأخيرًا في ملف AppLayout.js، يحتوي مكون <Content> أيضًا على children. في جميع هذه الحالات، يشير children إلى مكون <Home> الذي تم تمريره من App.js. عمليًا، هذا يعني أن أي من المكونات التي تم تمريرها من App.js على المسارات العامة أو الخاصة سيتم عرضها في منطقة المحتوى في التخطيط الخاص بنا، مع ترك الشريط الجانبي للتنقل دون تغيير.
يجب أن توفر الملفات الأخرى في مجلد client أمثلة وافرة لكيفية استبدال أشياء مثل نموذج تسجيل الدخول بإطار عمل Ant Design بعد بعض التعديلات الصغيرة.
إجراء تغييرات على الواجهة الخلفية: API وقاعدة البيانات
الشيء الرئيسي الآخر الذي يجب تطويره عند العمل مع Reno Expo هو واجهة برمجة التطبيقات (API) نفسها؛ فبعد كل شيء، من المفيد أن تكون قادرًا على تسجيل المستخدم وتسجيل دخوله، ولكن معظم التطبيقات تتطلب أكثر من ذلك لتكون مفيدة حقًا. لأغراض نسختي من «المكتبة الشخصية»، احتجنا إلى تنفيذ عدد من نقاط نهاية API الجديدة، وإنشاء بعض جداول قواعد البيانات الجديدة لتخزين بيانات الكتب والتعليقات.
تجدر الإشارة هنا إلى أنني في هذه الأمثلة أعمل بشكل عكسي. عادةً ما أقوم بإنشاء ترحيلات ونماذج قاعدة البيانات أولاً، ثم أبني طرق وحدة التحكم (controller methods) ومسارات API بعد ذلك. أقدمها «بشكل عكسي» هنا حتى نتمكن من تتبع المنطق من هدفنا إلى كيفية تنفيذه قطعة قطعة.
يحتوي ملف reno-expo-books/app/router/router.js على جميع المسارات للمشروع، ولكن سأشارك مثالين هنا للتوضيح.
مسارات API (API Routes)
// Public route
app.get("/api/books", booksController.getBooks);
// Private route
app.get("/api/user/books", [authJwt.verifyToken], booksController.getUserBooks);
إضافة المسارات العامة بسيطة بما فيه الكفاية، نحتاج فقط إلى تحديد طريقة HTTP، ونقطة نهاية API، وطريقة وحدة التحكم التي ستتعامل مع الطلب، على سبيل المثال، طلب GET إلى api/books يتم التعامل معه بواسطة طريقة getBooks في booksController. المصادقة باستخدام JWT التي لدينا بالفعل متاحة من حزمة بدء Reno Expo تجعل المسارات الخاصة بسيطة جدًا أيضًا. كل ما نحتاج إلى فعله هو تضمين الوسيط (middleware) للتحقق من الرمز المميز، [authJwt.verifyToken] في المثال أعلاه.
وحدات التحكم (Controllers): معالجة الطلبات
وحدات التحكم لنقاط النهاية هذه، أي الكود الذي يعالج الطلبات، هي أيضًا واضحة نسبيًا، على الرغم من أن استخدام Sequelize لأول مرة قد يتطلب بعض التعلم. إليك مثال على طريقة getBooks العامة المشار إليها أعلاه:
const db = require("../../models/index");
const Sequelize = require("sequelize");
const Book = db.Book;
const BookComment = db.BookComment;
// Public routes
exports.getBooks = (req, res) => {
Book.findAll({
where: {
userId: null
},
attributes: {
include: [
[
Sequelize.fn("COUNT", Sequelize.col("BookComments.id")),
"commentcount",
],
],
exclude: ["createdAt", "updatedAt"],
},
include: {
model: BookComment,
attributes: [],
},
group: ["Book.id"],
})
.then((data) => res.status(200).json(data))
.catch((error) => res.send(error));
};
توفر الاستيرادات في أعلى الملف نماذج قاعدة البيانات ومكتبة Sequelize. تبدو طريقة getBooks معقدة، لكنها تتكون من أجزاء بسيطة نسبيًا. أولاً، نستدعي نموذج Book. النموذج هو تمثيل ORM لجدول الكتب؛ وسننظر في كيفية إنشاء هذا الجدول قريبًا. يوفر Sequelize، مثل معظم أدوات ORM، ليس فقط مخططًا أو وصفًا للجدول، ولكن أيضًا طرقًا يمكن استدعاؤها على النموذج. في هذه الحالة، نستدعي Book.findAll({...})، والذي سيعيد جميع الكتب التي يمكنه العثور عليها وتتطابق مع معلمات معينة نمررها إليه.
في هذه الحالة بالذات، أردت الحصول على شيء مثل هذا:
[
{
"id": 1,
"title": "The Hobbit",
"commentcount": 3
},
{
"id": 2,
"title": "The Lord of the Rings",
"commentcount": 2
}
]
في طريقة findAll، نمرر أولاً معلمات where. إذا كنت على دراية بلغة SQL، فيجب أن يكون واضحًا جدًا ما يفعله هذا. في المثال أعلاه، نريد جميع الكتب التي يكون فيها userId فارغًا (null). هذا لأننا نريد فقط إرجاع الكتب العامة من وحدة التحكم هذه، لذلك فقط تلك التي لا يوجد مستخدم مرتبط بها.
بعد ذلك، تصف attributes شكل الاستجابة، أو البيانات التي نتوقعها. قسم exclude أسهل في الفهم، لذلك سأشرحه أولاً. يحتوي جدول الكتب على أعمدة لختمَي الوقت created_at و updated_at لكل سجل. نظرًا لأننا لا نريد هذه في استجابة JSON الخاصة بنا، يمكننا حذفها صراحةً في قسم exclude.
الجزء include من خصائصنا أكثر تعقيدًا. في SQL الخام، سنقوم بحساب عدد التعليقات المرتبطة بهذا الشكل:
SELECT "Books"."id",
"Books"."title",
COUNT("BookComments"."id") AS "commentcount"
FROM "Books"
JOIN "BookComments"
ON "BookComments"."bookId" = "Books"."id"
GROUP BY "Books"."id";
تحسب دالة SQL COUNT جميع السجلات في عمود BookComments.id، وتحد دالة GROUP BY التعليقات المحسوبة لكل كتاب، حسب المعرف (ID). تجدر الإشارة إلى أن commentcount ليس عمودًا في جدول الكتب، بل هو عمود محسوب مشتق من الشيء الذي نطلب من قاعدة البيانات حسابه لنا. يمنحنا Sequelize الوصول إلى دالة العد (count function) عبر مكتبته. يتم تضمين الدالة ذات الصلة في الخصائص أعلاه بهذا الشكل:
[Sequelize.fn("COUNT", Sequelize.col("BookComments.id")), "commentcount"]
أي: «استدعِ دالة Sequelize المسماة COUNT، احسب الأعمدة لـ BookComments.id، وسمِ العمود الذي تم إنشاؤه commentcount». يوفر هذا دالة العد الخاصة بنا تمامًا كما في إصدار SQL. كل ما تبقى هو تضمين group: ['Book.id'] كمعلمة إضافية لطريقة findAll في نموذج Book.
الجزء الآخر الذي ربما لاحظته في معلمات طريقة findAll هو هذا:
include: {
model: BookComment,
attributes: [],
},
هذا صحيح، include آخر. لاحظ أن هذا ليس متداخلاً في attributes، ولكنه نظير له. يعمل هذا include بنفس طريقة عبارة JOIN في SQL أعلاه. هذا يعني أننا نريد تضمين نموذج BookComment، لكننا لا نحتاج إلى إضافة أي خصائص (attributes) لأننا لا نريد الإشارة إلى أي من الأعمدة التي يحتوي عليها مباشرة؛ نحن نستخدمه فقط في دالة العد.
إجراء تغييرات على قاعدة البيانات: الترحيلات (Migrations)
الشيء الأخير الذي نحتاج إلى فهمه لنكون منتجين مع Sequelize هو الترحيلات لإجراء تغييرات على قاعدة البيانات. يمكن اعتبار الترحيلات بمثابة التحكم في المصدر لقاعدة البيانات الخاصة بك. بينما يمكنك تعديل قاعدة البيانات مباشرة عن طريق إنشاء جداول جديدة، وإضافة أعمدة، وتقديم قيود، وتغيير أنواع البيانات أو أي شيء آخر، فإن استخدام الترحيلات يسمح لك بإجراء تغييرات تجريبية مع القدرة على التراجع عنها بسهولة، والقدرة على مشاركة تطويرك على قاعدة بيانات مع أشخاص آخرين لا يضطرون إلى الكفاح للحفاظ على قواعد بياناتهم المحلية، والإنتاج، متزامنة.
الترحيلات هي في الأساس كود يخبر قاعدة البيانات الخاصة بك بكيفية التغيير، وكيفية التراجع عن التغيير الذي تم إدخاله، إذا كان ذلك ضروريًا. إليك الترحيل لإنشاء جدول الكتب:
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Books', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
title: {
type: Sequelize.STRING
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Books');
}
};
هذا الترحيل هو وحدة تصدر دالتين: up و down. تقوم دالة up بإجراء التغييرات، بينما تقوم دالة down بإلغاء أي من التغييرات التي تم إدخالها. لذا نرى هنا أن up تنشئ جدولًا يسمى ‘Books‘ مع الأعمدة id و title و created_at و updated_at. يحتوي كل عمود أيضًا على بعض الخصائص المرتبطة به، مثل نوع البيانات، وما إذا كان يمكن أن يحتوي على قيمة فارغة (null)، على سبيل المثال. تقوم دالة down ببساطة بحذف الجدول.
لن أشارك كل الترحيل التالي، أي الترحيل لإنشاء جدول تعليقات الكتب (Book Comment)، ولكنني سأعرض مقتطفًا من دالة up التي تحدد عمود bookId:
bookId: {
type: Sequelize.INTEGER,
onDelete: "CASCADE",
references: {
model: {
tableName: "Books"
},
key: "id",
},
allowNull: false,
},
الأشياء المثيرة للاهتمام التي يجب ملاحظتها هنا هي خاصية onDelete، والتي تم تعيينها على «CASCADE»، وخاصية references التي تربط جدول الكتب عبر عمود id. هذا ينشئ العلاقة بين الكتاب وتعليقاته. تخبر خاصية onDelete قاعدة البيانات بما يجب فعله إذا تم حذف كتاب: يجب أن يتسلسل إجراء الحذف هذا إلى جميع التعليقات المرتبطة. أي، إذا حذفت «الهوبيت»، فسيتم حذف جميع التعليقات المتعلقة بـ «الهوبيت» أيضًا.
نماذج قاعدة البيانات (Database Models)
الشيء الأخير الذي يجب الانتباه إليه هو أن هذه الترحيلات مدعومة أيضًا بالنماذج (models). يبدو نموذج BookComment بهذا الشكل:
"use strict";
module.exports = (sequelize, DataTypes) => {
const BookComment = sequelize.define("BookComment", {
comment: DataTypes.TEXT,
bookId: {
type: DataTypes.INTEGER,
references: {
model: "Books",
key: "id",
},
},
}, {});
BookComment.associate = function (models) {
// associations can be defined here
BookComment.belongsTo(models.Book, {
foreignKey: "bookId",
});
};
return BookComment;
};
ستلاحظ بعض أوجه التشابه بين هذا والترحيل. يحتوي على جزأين، تعريف النموذج (Model definition) وارتباطات النموذج (model associations). تساعد هذه على تعزيز العلاقة بين الجداول المختلفة حسب الضرورة.
إنشاء الترحيلات والنماذج
لإنشاء جداول من الصفر، يمكنك استخدام أمر مثل هذا:
npx sequelize-cli model:generate --name Post --attributes post:text
سيؤدي هذا تلقائيًا إلى إنشاء هيكل نموذج وهيكل ترحيل لجدول posts مع عمود ‘post‘. يمكنك بعد ذلك ملء الترحيل والنموذج بأي تفاصيل أعمدة أو ارتباطات أخرى ذات صلة. إذا كنت ترغب فقط في تعديل جدول موجود، على سبيل المثال، لتغيير نوع البيانات، أو إضافة عمود، يمكنك استخدام أمر لإنشاء ترحيل فقط:
npx sequelize-cli migration:generate --name add-userId-to-posts
يمكنك بعد ذلك إجراء التغييرات على النموذج الموجود وملف الترحيل الجديد حسب الضرورة.
تطبيق تغييرات قاعدة البيانات
مجرد كتابة الكود لتحديث قاعدة البيانات لا يكفي، بل تحتاج أيضًا إلى تشغيل الترحيلات لكل قاعدة بيانات يعمل عليها الكود الخاص بك، أي جهاز التطوير المحلي الخاص بك، وربما خادم الاختبار (staging server) إذا كان لديك واحد، وأيضًا خادم الإنتاج (production server) الخاص بك. الأمر لتشغيل هذه الترحيلات هو:
npx sequelize-cli db:migrate
يمكنك التراجع عن الترحيلات أيضًا:
npx sequelize-cli db:migrate:undo
الخلاصة التقنية
Reno Expo يقدم حلاً أنيقًا ومبسطًا للمطورين الذين يسعون لبناء تطبيقات Full Stack بكفاءة. من خلال تجميع تقنيات قوية ومجربة مثل React للواجهة الأمامية، و Node.js مع Express للواجهة الخلفية، و PostgreSQL كقاعدة بيانات علائقية، مدعومة بـ Sequelize ORM للمرونة، يوفر هذا الكيت أساسًا متينًا. يكمن جماله في بساطته ومرونته، حيث يتجنب التعقيدات غير الضرورية التي قد توجد في أطر عمل MVC الأكثر شمولاً مثل Rails أو Laravel. هذا النهج يمنح المطورين تحكمًا أكبر وشفافية في الكود، مما يجعله مثاليًا للمشاريع التي تتطلب تخصيصًا عاليًا أو للمطورين الذين يفضلون فهم كل جزء من حزمتهم. القدرة على النشر بسهولة على Heroku والمصادقة المدمجة عبر JWT تزيد من جاذبيته، مما يجعله خيارًا ممتازًا للانطلاق السريع في مشاريع الويب المتكاملة.
نتمنى لكم برمجة سعيدة! هذا كل شيء! كما ذكرت أعلاه، أنا شخصيًا أقوم بنشر كل ما أقوم بإنشائه باستخدام هذه التقنيات على Heroku، وهناك تعليمات مفصلة لخصوصيات نشرها على Heroku في ملف README.md الخاص بـ Reno Expo. يتضمن هذا أيضًا الأوامر لتشغيل الترحيلات على خادم Heroku.
هناك الكثير لتستوعبه هنا. ولكن إذا فهمت Express و React بشكل أساسي، وكنت مستعدًا للتعمق في وثائق Sequelize عند الحاجة، يمكنك بناء أي شيء تقريبًا يمكنك تخيله يستفيد من قاعدة بيانات علائقية باستخدام حزمة البدء هذه. إنه ليس مكتمل الميزات تمامًا مثل إطار عمل MVC مناسب مثل Rails أو Laravel أو Sails أو Nest، لكنني أحب حقيقة أن هناك فوضى أقل و «سحر» أقل مخفيًا في الأجزاء الداخلية لهذا. إنه، بعد كل شيء، مجرد Create React App مجمع مع خادم خفيف وحزمة ORM. والباقي متروك لك.
إذا وصلت إلى نهاية هذه المقالة، وخاصة إذا قمت ببناء أي شيء باستخدام Reno Expo، فأنا أحب أن أسمع منك. يمكنك الاتصال بي على Twitter: @JacksonBates.