ActiveResource.js: การสร้าง JavaScript SDK อันทรงพลังสำหรับ JSON API ของคุณอย่างรวดเร็ว
เผยแพร่แล้ว: 2022-03-11บริษัทของคุณเพิ่งเปิดตัว API และตอนนี้ต้องการสร้างชุมชนผู้ใช้รอบๆ คุณทราบดีว่าลูกค้าส่วนใหญ่จะทำงานใน JavaScript เนื่องจากบริการที่ API ของคุณมอบให้ทำให้ลูกค้าสร้างเว็บแอปพลิเคชันได้ง่ายขึ้น แทนที่จะเขียนทุกอย่างด้วยตนเอง Twilio เป็นตัวอย่างที่ดีของเรื่องนี้
คุณยังทราบด้วยว่า RESTful API ของคุณอาจเรียบง่าย ผู้ใช้ต้องการวางแพ็คเกจ JavaScript ที่จะทำทุกอย่างเพื่อพวกเขา พวกเขาไม่ต้องการเรียนรู้ API ของคุณและสร้างคำขอแต่ละรายการที่พวกเขาต้องการเอง
คุณกำลังสร้างห้องสมุดรอบ ๆ API ของคุณ หรือบางทีคุณแค่เขียนระบบการจัดการสถานะสำหรับเว็บแอปพลิเคชันที่โต้ตอบกับ API ภายในของคุณเอง
ไม่ว่าจะด้วยวิธีใด คุณไม่ต้องการที่จะทำซ้ำตัวเองซ้ำแล้วซ้ำเล่าทุกครั้งที่ CRUD หนึ่งในทรัพยากร API ของคุณ หรือแย่กว่านั้นคือ CRUD ทรัพยากรที่เกี่ยวข้องกับทรัพยากรเหล่านั้น สิ่งนี้ไม่ดีสำหรับการจัดการ SDK ที่กำลังเติบโตในระยะยาว และไม่ใช่การใช้เวลาของคุณให้เกิดประโยชน์
คุณสามารถใช้ ActiveResource.js ซึ่งเป็นระบบ JavaScript ORM สำหรับการโต้ตอบกับ API แทนได้ ฉันสร้างมันขึ้นมาเพื่อเติมเต็มความต้องการที่เรามีในโครงการ: เพื่อสร้าง JavaScript SDK ในไม่กี่บรรทัดเท่าที่เป็นไปได้ สิ่งนี้เปิดใช้งานประสิทธิภาพสูงสุดสำหรับเราและสำหรับชุมชนนักพัฒนาของเรา
มันขึ้นอยู่กับหลักการที่อยู่เบื้องหลัง Ruby บน ActiveRecord ORM แบบง่ายของ Rails
หลักการ JavaScript SDK
มีสองแนวคิดของ Ruby on Rails ที่แนะนำการออกแบบ ActiveResource.js:
- “Convention over configuration:” ตั้งสมมติฐานเกี่ยวกับธรรมชาติของปลายทางของ API ตัวอย่างเช่น หากคุณมีทรัพยากร
Product
ทรัพยากรนั้นจะสอดคล้องกับปลายทาง/products
วิธีนั้นจะไม่ใช้เวลาซ้ำๆ ในการกำหนดค่าคำขอ SDK แต่ละรายการไปยัง API ของคุณ นักพัฒนาสามารถเพิ่มทรัพยากร API ใหม่ด้วยการสืบค้น CRUD ที่ซับซ้อนให้กับ SDK ที่กำลังเติบโตของคุณในเวลาไม่กี่นาที ไม่ใช่ชั่วโมง - “ยกระดับโค้ดที่สวยงาม:” DHH ผู้สร้างของ Rails กล่าวว่าดีที่สุด—มีบางสิ่งที่ยอดเยี่ยมเกี่ยวกับโค้ดที่สวยงามสำหรับตัวมันเอง ActiveResource.js ห่อคำขอที่น่าเกลียดในบางครั้งในภายนอกที่สวยงาม คุณไม่จำเป็นต้องเขียนโค้ดที่กำหนดเองเพื่อเพิ่มตัวกรองและการแบ่งหน้า และรวมความสัมพันธ์ที่ซ้อนกับความสัมพันธ์กับคำขอ GET คุณไม่จำเป็นต้องสร้างคำขอ POST และ PATCH ที่เปลี่ยนแปลงคุณสมบัติของอ็อบเจ็กต์และส่งไปยังเซิร์ฟเวอร์เพื่อทำการอัปเดต ให้เรียกใช้เมธอดบน ActiveResource แทน: ไม่ต้องเล่น JSON อีกต่อไปเพื่อรับคำขอที่คุณต้องการ แต่ต้องทำอีกครั้งในครั้งต่อไป
ก่อนที่เราจะเริ่ม
สิ่งสำคัญคือต้องทราบว่าในขณะที่เขียนนี้ ActiveResource.js ใช้งานได้กับ API ที่เขียนตามมาตรฐาน JSON:API เท่านั้น
หากคุณไม่คุ้นเคยกับ JSON:API และต้องการจะปฏิบัติตาม มีไลบรารีดีๆ มากมายสำหรับสร้างเซิร์ฟเวอร์ JSON:API
ที่กล่าวว่า ActiveResource.js เป็น DSL มากกว่าตัวห่อหุ้มสำหรับมาตรฐาน API ใดมาตรฐานหนึ่ง อินเทอร์เฟซที่ใช้โต้ตอบกับ API ของคุณสามารถขยายได้ ดังนั้นบทความในอนาคตจึงครอบคลุมถึงวิธีใช้ ActiveResource.js กับ API ที่กำหนดเองของคุณ
กำลังตั้งค่า
ในการเริ่มต้น ให้ติดตั้ง active-resource
ในโครงการของคุณ:
yarn add active-resource
ขั้นตอนแรกคือการสร้าง ResourceLibrary
สำหรับ API ของคุณ ฉันจะใส่ ActiveResource
ทั้งหมดของฉันไว้ในโฟลเดอร์ src/resources
:
// /src/resources/library.js import { createResourceLibrary } from 'active-resource'; const library = createResourceLibrary('http://example.com/api/v1'); export default library;
พารามิเตอร์ที่จำเป็นเพียงอย่างเดียวในการสร้าง createResourceLibrary
คือ URL รูทของ API ของคุณ
เราจะสร้างอะไร
เราจะสร้างไลบรารี JavaScript SDK สำหรับ API ของระบบจัดการเนื้อหา นั่นหมายความว่าจะมีผู้ใช้ โพสต์ ความคิดเห็น และการแจ้งเตือน
ผู้ใช้จะสามารถอ่าน สร้าง และแก้ไขโพสต์ได้ อ่าน เพิ่ม และลบความคิดเห็น (ในโพสต์หรือความคิดเห็นอื่นๆ) และรับการแจ้งเตือนเมื่อมีโพสต์และความคิดเห็นใหม่
ฉันจะไม่ใช้ไลบรารี่เฉพาะใดๆ เพื่อจัดการมุมมอง (React, Angular เป็นต้น) หรือสถานะ (Redux เป็นต้น) แทนที่จะแยกการสอนแบบเป็นนามธรรมเพื่อโต้ตอบกับ API ของคุณผ่าน ActiveResource
เท่านั้น
แหล่งข้อมูลแรก: Users
เราจะเริ่มต้นด้วยการสร้างทรัพยากร User
เพื่อจัดการผู้ใช้ CMS
ขั้นแรก เราสร้างคลาสทรัพยากร User
ด้วย attributes
บางอย่าง:
// /src/resources/User.js import library from './library'; class User extends library.Base { static define() { this.attributes('email', 'userName', 'admin'); } } export default library.createResource(User);
สมมติว่าตอนนี้คุณมีจุดสิ้นสุดการตรวจสอบสิทธิ์ ซึ่งเมื่อผู้ใช้ส่งอีเมลและรหัสผ่านแล้ว จะส่งคืนโทเค็นการเข้าถึงและ ID ของผู้ใช้ ปลายทางนี้ได้รับการจัดการโดยบางฟังก์ชัน requestToken
เมื่อคุณได้รับ ID ผู้ใช้ที่ตรวจสอบสิทธิ์แล้ว คุณต้องการโหลดข้อมูลของผู้ใช้ทั้งหมด:
import library from '/src/resources/library'; import User from '/src/resources/User'; async function authenticate(email, password) { let [accessToken, userId] = requestToken(email, password); library.headers = { Authorization: 'Bearer ' + accessToken }; return await User.find(userId); }
ฉันตั้งค่า library.headers
ให้มีส่วนหัว Authorization
ด้วย accessToken
ดังนั้นคำขอทั้งหมดในอนาคตโดย ResourceLibrary
ของฉันจะได้รับอนุญาต
ส่วนต่อมาจะกล่าวถึงวิธีการตรวจสอบผู้ใช้และตั้งค่าโทเค็นการเข้าถึงโดยใช้คลาสทรัพยากร User
เท่านั้น
ขั้นตอนสุดท้ายของการ authenticate
คือการร้องขอ User.find(id)
สิ่งนี้จะส่งคำขอไปยัง /api/v1/users/:id
และการตอบสนองอาจมีลักษณะดังนี้:
{ "data": { "type": "users", "id": "1", "attributes": { "email": "[email protected]", "user_name": "user1", "admin": false } } }
การตอบสนองจาก authenticate
จะเป็นตัวอย่างของคลาส User
จากที่นี่ คุณสามารถเข้าถึงแอตทริบิวต์ต่างๆ ของผู้ใช้ที่ผ่านการตรวจสอบสิทธิ์ได้ หากต้องการแสดงแอตทริบิวต์ดังกล่าวที่ใดที่หนึ่งในแอปพลิเคชัน
let user = authenticate(email, password); console.log(user.id) // '1' console.log(user.userName) // user1 console.log(user.email) // [email protected] console.log(user.attributes()) /* { email: '[email protected]', userName: 'user1', admin: false } */
ชื่อแอตทริบิวต์แต่ละชื่อจะกลายเป็น camelCased เพื่อให้เหมาะสมกับมาตรฐานทั่วไปของ JavaScript คุณสามารถรับแต่ละรายการโดยตรงเป็นคุณสมบัติของวัตถุ user
หรือรับแอตทริบิวต์ทั้งหมดโดยเรียก user.attributes()
การเพิ่มดัชนีทรัพยากร
ก่อนที่เราจะเพิ่มทรัพยากรที่เกี่ยวข้องกับคลาส User
เช่น การแจ้งเตือน เราควรเพิ่มไฟล์ src/resources/index.js
ที่จะจัดทำดัชนีทรัพยากรทั้งหมดของเรา สิ่งนี้มีประโยชน์สองประการ:
- จะล้างการนำเข้าของเราโดยอนุญาตให้เราทำลาย
src/resources
สำหรับทรัพยากรหลายรายการในคำสั่งนำเข้าเดียว แทนที่จะใช้คำสั่งนำเข้าหลายรายการ - มันจะเริ่มต้นทรัพยากรทั้งหมดบน
ResourceLibrary
ที่เราจะสร้างโดยการเรียกlibrary.createResource
ในแต่ละอัน ซึ่งจำเป็นสำหรับ ActiveResource.js เพื่อสร้างความสัมพันธ์
// /src/resources/index.js import User from './User'; export { User };
การเพิ่มทรัพยากรที่เกี่ยวข้อง
ตอนนี้ มาสร้างทรัพยากรที่เกี่ยวข้องสำหรับ User
การ Notification
ขั้นแรกให้สร้างคลาส Notification
ที่เป็นของ belongsTo
User
:
// /src/resources/Notification.js import library from './library'; class Notification extends library.Base { static define() { this.belongsTo('user'); } } export default library.createResource(Notification);
จากนั้นเราเพิ่มลงในดัชนีทรัพยากร:
// /src/resources/index.js import Notification from './Notification'; import User from './User'; export { Notification, User };
จากนั้นเชื่อมโยงการแจ้งเตือนกับคลาส User
:
// /src/resources/User.js class User extends library.Base { static define() { /* ... */ this.hasMany('notifications'); } }
ตอนนี้เมื่อเราได้ผู้ใช้กลับมาจาก authenticate
แล้ว เราสามารถโหลดและแสดงการแจ้งเตือนทั้งหมดได้:
let notifications = await user.notifications().load(); console.log(notifications.map(notification => notification.message));
นอกจากนี้เรายังสามารถรวมการแจ้งเตือนในคำขอเดิมสำหรับผู้ใช้ที่ตรวจสอบสิทธิ์:
async function authenticate(email, password) { /* ... */ return await User.includes('notifications').find(userId); }
นี่เป็นหนึ่งในตัวเลือกมากมายที่มีใน DSL
การตรวจสอบ DSL
มาพูดถึงสิ่งที่สามารถขอได้อยู่แล้วจากโค้ดที่เราได้เขียนไว้จนถึงตอนนี้
คุณสามารถสอบถามกลุ่มผู้ใช้หรือผู้ใช้รายเดียว
let users = await User.all(); let user = await User.first(); user = await User.last(); user = await User.find('1'); user = await User.findBy({ userName: 'user1' });
คุณสามารถแก้ไขการสืบค้นโดยใช้เมธอดเชิงสัมพันธ์แบบลูกโซ่ได้:
// Query and iterate over all users User.each((user) => console.log(user)); // Include related resources let users = await User.includes('notifications').all(); // Only respond with user emails as the attributes users = await User.select('email').all(); // Order users by attribute users = await User.order({ email: 'desc' }).all(); // Paginate users let usersPage = await User.page(2).perPage(5).all(); // Filter users by attribute users = await User.where({ admin: true }).all(); users = await User .includes('notifications') .select('email', { notifications: ['message', 'createdAt'] }) .order({ email: 'desc' }) .where({ admin: false }) .perPage(10) .page(3) .all(); let user = await User .includes('notification') .select('email') .first();
โปรดสังเกตว่า คุณสามารถเขียนการสืบค้นโดยใช้ตัวแก้ไขแบบลูกโซ่จำนวนเท่าใดก็ได้ และคุณสามารถสิ้นสุดการสืบค้นด้วย .all()
, .first( .first()
, .last()
หรือ .each .each()
คุณสามารถสร้างผู้ใช้ในเครื่องหรือสร้างบนเซิร์ฟเวอร์:
let user = User.build(attributes); user = await User.create(attributes);
เมื่อคุณมีผู้ใช้ที่คงอยู่ คุณสามารถส่งการเปลี่ยนแปลงไปยังผู้ใช้เพื่อบันทึกบนเซิร์ฟเวอร์:
user.email = '[email protected]'; await user.save(); /* or */ await user.update({ email: '[email protected]' });
คุณยังสามารถลบออกจากเซิร์ฟเวอร์:
await user.destroy();
DSL พื้นฐานนี้ขยายไปสู่ทรัพยากรที่เกี่ยวข้องเช่นกัน ตามที่ฉันจะสาธิตในบทช่วยสอนที่เหลือ ตอนนี้ เราสามารถใช้ ActiveResource.js เพื่อสร้าง CMS ที่เหลือได้อย่างรวดเร็ว: โพสต์และความคิดเห็น
กำลังสร้างกระทู้
สร้างคลาสทรัพยากรสำหรับ Post
และเชื่อมโยงกับคลาส User
:
// /src/resources/Post.js import library from './library'; class Post extends library.Base { static define() { this.belongsTo('user'); } } export default library.createResource(Post);
// /src/resources/User.js class User extends library.Base { static define() { /* ... */ this.hasMany('notifications'); this.hasMany('posts'); } }
เพิ่ม Post
ไปยังดัชนีทรัพยากรด้วย:
// /src/resources/index.js import Notification from './Notification'; import Post from './Post'; import User from './User'; export { Notification, Post, User };
จากนั้นผูกทรัพยากร Post
ลงในแบบฟอร์มเพื่อให้ผู้ใช้สร้างและแก้ไขโพสต์ เมื่อผู้ใช้เยี่ยมชมแบบฟอร์มเพื่อสร้างโพสต์ใหม่ ทรัพยากรของ Post
จะถูกสร้างขึ้น และทุกครั้งที่มีการเปลี่ยนแปลงแบบฟอร์ม เราจะนำการเปลี่ยนแปลงไปใช้กับ Post
:

import Post from '/src/resources/Post'; let post = Post.build({ user: authenticatedUser }); onChange = (event) => { post.content = event.target.value; };
ถัดไป เพิ่มการเรียกกลับ onSubmit
ลงในแบบฟอร์มเพื่อบันทึกโพสต์ไปยังเซิร์ฟเวอร์ และจัดการข้อผิดพลาดหากความพยายามบันทึกล้มเหลว:
onSubmit = async () => { try { await post.save(); /* successful, redirect to edit post form */ } catch { post.errors().each((field, error) => { console.log(field, error.message) }); } }
การแก้ไขกระทู้
เมื่อโพสต์ได้รับการบันทึกแล้ว โพสต์นั้นจะเชื่อมโยงกับ API ของคุณเป็นทรัพยากรบนเซิร์ฟเวอร์ของคุณ คุณสามารถบอกได้ว่าทรัพยากรยังคงอยู่บนเซิร์ฟเวอร์หรือไม่โดยเรียก persisted
:
if (post.persisted()) { /* post is on server */ }
สำหรับทรัพยากรที่คงอยู่ ActiveResource.js สนับสนุนแอตทริบิวต์สกปรก ซึ่งคุณสามารถตรวจสอบได้ว่าแอตทริบิวต์ของทรัพยากรมีการเปลี่ยนแปลงจากค่าบนเซิร์ฟเวอร์หรือไม่
หากคุณเรียกใช้ save()
บนทรัพยากรที่คงอยู่ จะทำการร้องขอ PATCH
ที่มีเฉพาะการเปลี่ยนแปลงที่เกิดขึ้นกับทรัพยากร แทนที่จะส่งชุดแอตทริบิวต์และความสัมพันธ์ทั้งหมดของทรัพยากรไปยังเซิร์ฟเวอร์โดยไม่จำเป็น
คุณสามารถเพิ่มแอตทริบิวต์ที่ติดตามไปยังทรัพยากรได้โดยใช้การประกาศ attributes
มาติดตามการเปลี่ยนแปลงของ post.content
:
// /src/resources/Post.js class Post extends library.Base { static define() { this.attributes('content'); /* ... */ } }
ตอนนี้ ด้วยโพสต์ที่ยังคงอยู่ในเซิร์ฟเวอร์ เราสามารถแก้ไขโพสต์ได้ และเมื่อคลิกปุ่มส่ง ให้บันทึกการเปลี่ยนแปลงไปยังเซิร์ฟเวอร์ นอกจากนี้เรายังสามารถปิดใช้งานปุ่มส่งได้หากยังไม่มีการเปลี่ยนแปลง:
onEdit = (event) => { post.content = event.target.value; } onSubmit = async () => { try { await post.save(); } catch { /* display edit errors */ } } disableSubmitButton = () => { return !post.changed(); }
มีวิธีการจัดการความสัมพันธ์แบบเอกพจน์ เช่น post.user()
หากเราต้องการเปลี่ยนผู้ใช้ที่เชื่อมโยงกับโพสต์:
await post.updateUser(user);
นี่เทียบเท่ากับ:
await post.update({ user });
แหล่งข้อมูลความคิดเห็น
ตอนนี้สร้าง Comment
คลาสทรัพยากรและเชื่อมโยงกับ Post
โปรดจำข้อกำหนดของเราว่าความคิดเห็นสามารถตอบสนองต่อโพสต์หรือความคิดเห็นอื่นได้ ดังนั้นแหล่งข้อมูลที่เกี่ยวข้องสำหรับความคิดเห็นจึงมีความหลากหลาย:
// /src/resources/Comment.js import library from './library'; class Comment extends library.Base { static define() { this.attributes('content'); this.belongsTo('resource', { polymorphic: true, inverseOf: 'replies' }); this.belongsTo('user'); this.hasMany('replies', { as: 'resource', className: 'Comment', inverseOf: 'resource' }); } } export default library.createResource(Comment);
อย่าลืมเพิ่ม Comment
ใน /src/resources/index.js
ด้วย
เราจะต้องเพิ่มบรรทัดในคลาส Post
ด้วย:
// /src/resources/Post.js class Post extends library.Base { static define() { /* ... */ this.hasMany('replies', { as: 'resource', className: 'Comment', inverseOf: 'resource' }); } }
inverseOf
ที่ส่งผ่านไปยังคำนิยาม hasMany
สำหรับการ replies
บ่งชี้ว่าความสัมพันธ์นั้นผกผันของ polymorphic belongsTo
ข้อกำหนดของ resource
คุณสมบัติ inverseOf
ของความสัมพันธ์มักใช้เมื่อทำการดำเนินการระหว่างความสัมพันธ์ โดยปกติ คุณสมบัตินี้จะถูกกำหนดโดยอัตโนมัติโดยใช้ชื่อคลาส แต่เนื่องจากความสัมพันธ์แบบ polymorphic สามารถเป็นหนึ่งในหลายคลาสได้ คุณต้องกำหนดตัวเลือก inverseOf
ด้วยตัวเอง เพื่อให้ความสัมพันธ์แบบ polymorphic มีฟังก์ชันการทำงานเหมือนกันทั้งหมด
การจัดการความคิดเห็นในโพสต์
DSL เดียวกันกับที่ใช้กับทรัพยากรยังใช้กับการจัดการทรัพยากรที่เกี่ยวข้องด้วย ตอนนี้เราได้ตั้งค่าความสัมพันธ์ระหว่างโพสต์และความคิดเห็นแล้ว มีหลายวิธีที่เราสามารถจัดการความสัมพันธ์นี้ได้
คุณสามารถเพิ่มความคิดเห็นใหม่ในโพสต์:
onSubmitComment = async (event) => { let comment = await post.replies().create({ content: event.target.value, user: user }); }
คุณสามารถเพิ่มการตอบกลับความคิดเห็น:
onSubmitReply = async (event) => { let reply = await comment.replies().create({ content: event.target.value, user: user }); }
คุณสามารถแก้ไขความคิดเห็น:
onEditComment = async (event) => { await comment.update({ content: event.target.value }); }
คุณสามารถลบความคิดเห็นออกจากโพสต์:
onDeleteComment = async (comment) => { await post.replies().delete(comment); }
การแสดงโพสต์และความคิดเห็น
สามารถใช้ SDK เพื่อแสดงรายการโพสต์ที่มีการแบ่งหน้า และเมื่อมีการคลิกโพสต์ โพสต์จะถูกโหลดในหน้าใหม่พร้อมความคิดเห็นทั้งหมด:
import { Post } from '/src/resources'; let postsPage = await Post .order({ createdAt: 'desc' }) .select('content') .perPage(10) .all();
ข้อความค้นหาด้านบนจะดึงข้อมูลโพสต์ล่าสุด 10 รายการ และเพื่อเพิ่มประสิทธิภาพ คุณลักษณะเดียวที่โหลดคือ content
หากผู้ใช้คลิกปุ่มเพื่อไปยังหน้าถัดไปของโพสต์ ตัวจัดการการเปลี่ยนแปลงจะเรียกข้อมูลหน้าถัดไป ที่นี่เรายังปิดการใช้งานปุ่มหากไม่มีหน้าถัดไป
onClickNextPage = async () => { postsPage = await postsPage.nextPage(); if (!postsPage.hasNextPage()) { /* disable next page button */ } };
เมื่อมีการคลิกลิงก์ไปยังโพสต์ เราจะเปิดหน้าใหม่โดยการโหลดและแสดงโพสต์พร้อมข้อมูลทั้งหมด รวมทั้งความคิดเห็น หรือที่เรียกว่าการตอบกลับ รวมถึงการตอบกลับข้อความตอบกลับเหล่านั้น:
import { Post } from '/src/resources'; onClick = async (postId) => { let post = await Post.includes({ replies: 'replies' }).find(postId); console.log(post.content, post.createdAt); post.replies().target().each(comment => { console.log( comment.content, comment.replies.target().map(reply => reply.content).toArray() ); }); }
การเรียก .target()
บนความสัมพันธ์แบบ hasMany
เช่น post.replies()
จะส่งคืน ActiveResource.Collection
ของความคิดเห็นที่โหลดและจัดเก็บไว้ในเครื่อง
ความแตกต่างนี้มีความสำคัญ เนื่องจาก post.replies().target().first()
จะส่งคืนความคิดเห็นแรกที่โหลด ในทางตรงกันข้าม post.replies().first()
จะส่งคืนสัญญาสำหรับความคิดเห็นหนึ่งข้อที่ร้องขอจาก GET /api/v1/posts/:id/replies
คุณยังสามารถขอคำตอบสำหรับโพสต์แยกต่างหากจากคำขอสำหรับโพสต์เองได้ ซึ่งจะช่วยให้คุณแก้ไขคำถามของคุณได้ คุณสามารถลูกโซ่ตัวดัดแปลง เช่น order
, select
, includes
, where
, perPage
, page
เมื่อทำการสืบค้น hasMany
ความสัมพันธ์มากมาย เช่นเดียวกับที่คุณสามารถทำได้เมื่อทำการสืบค้นทรัพยากรด้วยตัวเอง
import { Post } from '/src/resources'; onClick = async (postId) => { let post = await Post.find(postId); let userComments = await post.replies().where({ user: user }).perPage(3).all(); console.log('Your comments:', userComments.map(comment => comment.content).toArray()); }
การปรับเปลี่ยนทรัพยากรหลังจากที่ได้รับการร้องขอ
บางครั้งคุณต้องการนำข้อมูลจากเซิร์ฟเวอร์มาแก้ไขก่อนใช้งาน ตัวอย่างเช่น คุณสามารถห่อ post.createdAt
ในวัตถุ moment()
เพื่อให้คุณสามารถแสดงวันที่เวลาที่ใช้งานง่ายสำหรับผู้ใช้เกี่ยวกับเวลาที่สร้างโพสต์:
// /src/resources/Post.js import moment from 'moment'; class Post extends library.Base { static define() { /* ... */ this.afterRequest(function() { this.createdAt = moment(this.createdAt); }); } }
ไม่เปลี่ยนรูป
หากคุณทำงานกับระบบการจัดการสถานะที่สนับสนุนวัตถุที่ไม่เปลี่ยนรูป ลักษณะการทำงานทั้งหมดใน ActiveResource.js สามารถทำให้ไม่เปลี่ยนรูปได้โดยการกำหนดค่าไลบรารีทรัพยากร:
// /src/resources/library.js import { createResourceLibrary } from 'active-resource'; const library = createResourceLibrary( 'http://example.com/api/v1', { immutable: true } ); export default library;
การวนกลับ: การเชื่อมโยงระบบการตรวจสอบสิทธิ์
ในการสรุป ฉันจะแสดงวิธีผสานรวมระบบตรวจสอบผู้ใช้ของคุณเข้ากับ User
ActiveResource
ของคุณ
ย้ายระบบตรวจสอบโทเค็นของคุณไปยังจุดปลาย /api/v1/tokens
เมื่ออีเมลและรหัสผ่านของผู้ใช้ถูกส่งไปยังปลายทางนี้ ข้อมูลของผู้ใช้ที่ตรวจสอบสิทธิ์พร้อมโทเค็นการให้สิทธิ์จะถูกส่งตอบกลับ
สร้างคลาสทรัพยากร Token
ที่เป็นของ User
:
// /src/resources/Token.js import library from './library'; class Token extends library.Base { static define() { this.belongsTo('user'); } } export default library.createResource(Token);
เพิ่ม Token
ใน /src/resources/index.js
จากนั้นเพิ่มวิธีสแตติก authenticate
ให้กับคลาสทรัพยากร User
ของคุณและเชื่อมโยง User
กับ Token
:
// /src/resources/User.js import library from './library'; import Token from './Token'; class User { static define() { /* ... */ this.hasOne('token'); } static async authenticate(email, password) { let user = this.includes('token').build({ email, password }); let authUser = await this.interface().post(Token.links().related, user); let token = authUser.token(); library.headers = { Authorization: 'Bearer ' + token.id }; return authUser; } }
เมธอดนี้ใช้ resourceLibrary.interface()
ซึ่งในกรณีนี้คืออินเทอร์เฟซ JSON:API เพื่อส่งผู้ใช้ไปที่ /api/v1/tokens
สิ่งนี้ถูกต้อง: ปลายทางใน JSON:API ไม่ต้องการให้โพสต์ประเภทเดียวที่โพสต์ไปและกลับเป็นประเภทที่ตั้งชื่อตาม ดังนั้นคำขอจะเป็น:
{ "data": { "type": "users", "attributes": { "email": "[email protected]", "password": "password" } } }
การตอบสนองจะเป็นผู้ใช้ที่ได้รับการรับรองความถูกต้องพร้อมกับโทเค็นการตรวจสอบสิทธิ์:
{ "data": { "type": "users", "id": "1", "attributes": { "email": "[email protected]", "user_name": "user1", "admin": false }, "relationships": { "token": { "data": { "type": "tokens", "id": "Qcg6yI1a5qCxXgKWtSAbZ2MIHFChHAq0Vc1Lo4TX", } } } }, "included": [{ "type": "tokens", "id": "Qcg6yI1a5qCxXgKWtSAbZ2MIHFChHAq0Vc1Lo4TX", "attributes": { "expires_in": 3600 } }] }
จากนั้นเราใช้ token.id
เพื่อตั้งค่าส่วนหัว Authorization
ของห้องสมุดและส่งคืนผู้ใช้ซึ่งเหมือนกับการขอผู้ใช้ผ่าน User.find()
เหมือนที่เราทำก่อนหน้านี้
ตอนนี้ ถ้าคุณโทร User.authenticate(email, password)
คุณจะได้รับผู้ใช้ที่ตรวจสอบสิทธิ์แล้วตอบกลับ และคำขอทั้งหมดในอนาคตจะได้รับอนุญาตด้วยโทเค็นการเข้าถึง
ActiveResource.js เปิดใช้งานการพัฒนา JavaScript SDK อย่างรวดเร็ว
ในบทช่วยสอนนี้ เราได้สำรวจวิธีที่ ActiveResource.js สามารถช่วยให้คุณสร้าง JavaScript SDK ได้อย่างรวดเร็วเพื่อจัดการทรัพยากร API ของคุณและทรัพยากรที่เกี่ยวข้องที่หลากหลาย ซึ่งบางครั้งซับซ้อนในบางครั้ง คุณสามารถดูคุณสมบัติทั้งหมดเหล่านี้และเอกสารเพิ่มเติมใน README สำหรับ ActiveResource.js
ฉันหวังว่าคุณจะพอใจกับความสะดวกในการดำเนินการเหล่านี้ และคุณจะใช้ (และอาจจะสนับสนุน) ห้องสมุดของฉันสำหรับโครงการในอนาคตของคุณ หากมันตรงกับความต้องการของคุณ ด้วยจิตวิญญาณของโอเพ่นซอร์ส ฝ่ายประชาสัมพันธ์ยินดีต้อนรับเสมอ!