Hapi.js هو إطار لبناء تطبيقات الويب. هذا المنشور يحتوي على جميع الأساسيات لبداية ساخنة. للأسف ، المؤلف ليس كاتباً على الإطلاق ، لذلك سيكون هناك الكثير من الشفرات والكلمات القليلة.
MVP
ضع مجموعة من التبعيات:
npm i @hapi/hapi @hapi/boom filepaths hapi-boom-decorators
- hapi / hapi - في الواقع ، خادمنا
- hapi / boom - وحدة لتوليد الإجابات القياسية
- hapi-boom-decorators - مساعد لـ hapi / boom
- filepaths - أداة مساعدة تقرأ المجلدات بشكل متكرر
إنشاء بنية مجلد ومجموعة من بدء الملفات:

في ./src/routes/ نضيف وصفًا لنقاط النهاية api ، ملف واحد - نقطة نهاية واحدة:
./src/server.js - الوحدة النمطية التي تصدر الخادم نفسه.
في ./server.js كل ما نقوم به هو استدعاء createServer ()
#!/usr/bin/env node const createServer = require('./src/server'); createServer();
نطلق
node server.js
وتحقق:
curl http://127.0.0.1:3030/ {"result":"ok","message":"Hello World!"} curl http://127.0.0.1:3030/test {"statusCode":404,"error":"Not Found","message":"Not Found"}
في البرية
في مشروع حقيقي ، على الأقل ، نحتاج إلى قاعدة بيانات ومسجل وترخيص ومعالجة الأخطاء والمزيد.
إضافة تسلسل
ORM sequelize متصل كوحدة نمطية:
... const Sequelize = require('sequelize'); ... await server.register([ ... { plugin: require('hapi-sequelizejs'), options: [ { name: config.db.database,
تصبح قاعدة البيانات متاحة داخل المسار من خلال مكالمة:
async function response(request) { const model = request.getModel('_', '_'); }
حقن وحدات إضافية في الطلب
من الضروري اعتراض حدث "onRequest" ، والذي بداخله سنحقن التكوين والمسجل في كائن الطلب:
... const Logger = require('./libs/Logger'); ... async function createServer(logLVL=config.logLVL) { ... const logger = new Logger(logLVL, 'my-hapi-app'); ... server.ext({ type: 'onRequest', method: async function (request, h) { request.server.config = Object.assign({}, config); request.server.logger = logger; return h.continue; } }); ... }
بعد ذلك ، داخل معالج الطلب ، سنتمكن من الوصول إلى التكوين والمسجل وقاعدة البيانات ، دون الحاجة إلى إضافة شيء ما في نص الوحدة النمطية:
وبالتالي ، سيتلقى معالج الطلب عند الإدخال كل ما هو ضروري للمعالجة ، ولن يكون من الضروري تضمين الوحدات النمطية نفسها في كل مرة من وقت لآخر.
ترخيص
يتم اعتماد في hapi في شكل وحدات.
... const AuthBearer = require('hapi-auth-bearer-token'); ... async function createServer(logLVL=config.logLVL) { ... await server.register([ AuthBearer, ... ]); server.auth.strategy('token', 'bearer-access-token', {
وأيضًا داخل المسار ، يلزمك تحديد نوع التفويض المطلوب استخدامه:
module.exports = { method: 'GET', path: '/', auth: 'token',
إذا تم استخدام عدة أنواع من التراخيص:
auth: { strategies: ['token1', 'token2', 'something_else'] },
خطأ في التعامل
افتراضيًا ، يولد ذراع التطوّر الأخطاء بطريقة معيارية ، غالبًا ما تكون هذه الإجابات بحاجة إلى التفاف بتنسيقها الخاص.
server.ext('onPreResponse', function (request, h) {
مخططات البيانات
هذا موضوع صغير ولكنه مهم جدا. تسمح لك مخططات البيانات بالتحقق من صحة الطلب وصحة الاستجابة. إلى أي مدى تصف هذه المخططات جيدًا ، فستكون المبادلات والاختبارات الذاتية ذات جودة عالية.
يتم وصف جميع مخططات البيانات من خلال joi. فلنقدم مثالًا لترخيص المستخدم:
const Joi = require('@hapi/joi'); const Boom = require('boom'); async function response(request) {
الاختبار:
curl -X GET "http://localhost:3030/auth?login=pupkin@gmail.com&password=12345"

أرسل الآن بدلاً من البريد ، فقط تسجيل الدخول:
curl -X GET "http://localhost:3030/auth?login=pupkin&password=12345"

إذا لم تتطابق الإجابة مع مخطط الاستجابة ، فسيقع الخادم أيضًا في خطأ 500.
إذا بدأ المشروع بمعالجة أكثر من طلب واحد في الساعة ، فقد يكون من الضروري الحد من التحقق من الردود ، كما هو التحقق عملية كثيفة الاستخدام للموارد. هناك معلمة لهذا: "عينة"
module.exports = { method: 'GET', path: '/auth', options: { handler: response, validate: { query: requestScheme }, response: { sample: 50, schema: responseScheme } } };
على هذا النحو ، سيتم التحقق من صحة 50 ٪ فقط من الطلبات.
من المهم للغاية وصف القيم الافتراضية والأمثلة ، في المستقبل سوف يتم استخدامها لإنشاء الوثائق و autotests.
اختيال / OpenAPI
نحتاج إلى مجموعة من الوحدات الإضافية:
npm i hapi-swagger @hapi/inert @hapi/vision
نحن ربطها إلى server.js
... const Inert = require('@hapi/inert'); const Vision = require('@hapi/vision'); const HapiSwagger = require('hapi-swagger'); const Package = require('../package'); ... const swaggerOptions = { info: { title: Package.name + ' API Documentation', description: Package.description }, jsonPath: '/documentation.json', documentationPath: '/documentation', schemes: ['https', 'http'], host: config.swaggerHost, debug: true }; ... async function createServer(logLVL=config.logLVL) { ... await server.register([ ... Inert, Vision, { plugin: HapiSwagger, options: swaggerOptions }, ... ]); ... });
وفي كل مسار تحتاج إلى وضع علامة "api":
module.exports = { method: 'GET', path: '/auth', options: { handler: response, tags: [ 'api' ],
الآن على الموقع http: // localhost: 3030 / وثائق ، ستكون هناك واجهة ويب مع الوثائق متاحة ، وعلى الموقع http: // localhost: 3030 / documentation.json .json.

اختبار السيارات جيل
إذا وصفنا نوعيًا خطط الطلب والاستجابة ، فقمنا بإعداد قاعدة بيانات أولية تتوافق مع الأمثلة الموضحة في أمثلة الطلب ، ثم ، وفقًا للمخططات المعروفة ، يمكنك إنشاء الطلبات تلقائيًا والتحقق من رموز استجابة الخادم.
على سبيل المثال ، في GET: / auth معلمات تسجيل الدخول وكلمة المرور متوقعة ، سنأخذها من الأمثلة التي حددناها في الرسم البياني:
const requestScheme =Joi.object({ login: Joi.string().email().required().example('pupkin@gmail.com'), password: Joi.string().required().example('12345') });
وإذا كان الخادم يستجيب بـ HTTP-200-OK ، فسنفترض أن الاختبار قد مر.
لسوء الحظ ، لم يكن هناك وحدة مناسبة جاهزة ؛ عليك التحدث قليلاً:
لا تنسى التبعيات:
npm i request-promise mocha sync-request
وحول package.json
... "scripts": { "test": "mocha", "dbinit": "node ./scripts/dbInit.js" }, ...
نتحقق من:
npm test

وإذا كان القفل عبارة عن نظام بيانات ما ، أو إذا كانت الإجابة غير متطابقة مع المخطط:

ولا تنسَ أن الاختبارات ستصبح عاجلاً أم آجلاً حساسة للبيانات الموجودة في قاعدة البيانات. قبل إجراء الاختبارات ، تحتاج إلى مسح قاعدة البيانات على الأقل.
مصادر كاملة