غالبًا ما يتم تقديم GraphQL كطريقة ثورية لتصميم واجهات برمجة تطبيقات الويب مقارنة بـ REST. ومع ذلك ، إذا ألقيت نظرة فاحصة على هذه التقنيات ، سترى أن هناك الكثير من الاختلافات بينها. GraphQL هو حل جديد نسبيًا ، تم فتح مصادره أمام مجتمع Facebook في عام 2015. واليوم ، لا تزال REST هي النموذج الأكثر شيوعًا المستخدم لتوفير واجهات برمجة التطبيقات والتشغيل البيني بين الخدمات المصغرة. هل سيتمكن GraphQL من تجاوز REST في المستقبل؟ دعونا نرى كيف يحدث تفاعل الخدمات الصغيرة من خلال واجهة برمجة تطبيقات GraphQL باستخدام Spring Boot ومكتبة
GQL .
لنبدأ بمثال الهندسة المعمارية لنظامنا. لنفترض أن لدينا ثلاث خدمات صغيرة تتواصل مع بعضها البعض من خلال عناوين URL المستلمة من تطبيقات Spring Cloud Eureka.

تمكين دعم GraphQL في Boot Spring
يمكننا بسهولة تمكين دعم GraphQL على جانب الخادم من تطبيق Spring Boot باستخدام المبتدئين. بعد إضافة Graphql-spring-boot-starter ، ستتوفر أداة GraphQL servlet تلقائيًا على / graphql. يمكننا تجاوز هذا المسار الافتراضي عن طريق تحديد خاصية graphql.servlet.mapping في ملف application.yml. نقوم أيضًا بتضمين GraphiQL ، IDE المستند إلى المستعرض لكتابة استعلامات GraphQL والتحقق منها واختبارها ومكتبة أدوات Java Graph Graph ، والتي تحتوي على مكونات مفيدة لإنشاء الاستعلامات والطفرات. بفضل هذه المكتبة ، سيتم استخدام جميع الملفات في مسار الفصل مع الامتداد .graphqls لإنشاء تعريف مخطط.
compile('com.graphql-java:graphql-spring-boot-starter:5.0.2') compile('com.graphql-java:graphiql-spring-boot-starter:5.0.2') compile('com.graphql-java:graphql-java-tools:5.2.3')
وصف مخطط GrpahQL
يحتوي كل وصف للمخطط على تعريف للأنواع والعلاقات بينها والعديد من العمليات التي تتضمن استعلامات للبحث عن الكائنات والطفرات لإنشاء البيانات أو تحديثها أو حذفها. نبدأ عادةً بتحديد النوع المسؤول عن مجال الكائن الموصوف. يمكنك تحديد ما إذا كان الحقل مطلوبًا مع
!
حرف أو إذا كان مصفوفة -
[…]
. يجب أن يحتوي الوصف على النوع المعلن عنه أو مرجع لأنواع أخرى متاحة في المواصفات.
type Employee { id: ID! organizationId: Int! departmentId: Int! name: String! age: Int! position: String! salary: Int! }
يحتوي الجزء التالي من تعريف المخطط على إعلانات الاستعلامات والطفرات. تعرض معظم الاستعلامات قائمة بالكائنات التي تم وضع علامة عليها على أنها [موظف] في المخطط. داخل نوع EmployeeQueries ، نعلن عن جميع طرق البحث ، بينما في نوع EmployeeMutations وطرق إضافة الموظفين وتحديثهم وحذفهم. إذا قمت بتمرير كائن بالكامل إلى طريقة ما ، يجب أن تقوم بتعريفه كنوع إدخال.
schema { query: EmployeeQueries mutation: EmployeeMutations } type EmployeeQueries { employees: [Employee] employee(id: ID!): Employee! employeesByOrganization(organizationId: Int!): [Employee] employeesByDepartment(departmentId: Int!): [Employee] } type EmployeeMutations { newEmployee(employee: EmployeeInput!): Employee deleteEmployee(id: ID!) : Boolean updateEmployee(id: ID!, employee: EmployeeInput!): Employee } input EmployeeInput { organizationId: Int departmentId: Int name: String age: Int position: String salary: Int }
تنفيذ الاستفسارات والطفرات
بفضل التكوين التلقائي لأدوات Java Graph Graph و Spring Boot GraphQL ، لا نحتاج إلى بذل الكثير من الجهد لتنفيذ الاستعلامات والطفرات في تطبيقنا. يجب أن يقوم فول الموظفين بتطبيق واجهة GraphQLQueryResolver. بناءً على ذلك ، سيتمكن Spring من العثور تلقائيًا على الطريقة الصحيحة واستدعاءها كرد على أحد استعلامات GraphQL التي تم الإعلان عنها داخل المخطط. إليك الفصل الذي يحتوي على تنفيذ الردود على الاستفسارات:
@Component public class EmployeeQueries implements GraphQLQueryResolver { private static final Logger LOGGER = LoggerFactory.getLogger(EmployeeQueries.class); @Autowired EmployeeRepository repository; public List employees() { LOGGER.info("Employees find"); return repository.findAll(); } public List employeesByOrganization(Long organizationId) { LOGGER.info("Employees find: organizationId={}", organizationId); return repository.findByOrganization(organizationId); } public List employeesByDepartment(Long departmentId) { LOGGER.info("Employees find: departmentId={}", departmentId); return repository.findByDepartment(departmentId); } public Employee employee(Long id) { LOGGER.info("Employee find: id={}", id); return repository.findById(id); } }
إذا كنت تريد الاتصال ، على سبيل المثال ، طريقة الموظف (رقم التعريف الطويل) ، اكتب الاستعلام التالي. لاختباره في تطبيقك ، استخدم GraphiQL ، المتاح على / graphiql.

تحتاج الفاصوليا المسؤولة عن تنفيذ طرق الطفرات إلى تنفيذ واجهة GraphQLMutationResolver. على الرغم من اسم EmployeeInput ، فإننا نواصل استخدام كائن مجال الموظف نفسه الذي تم إرجاعه بواسطة الطلب.
@Component public class EmployeeMutations implements GraphQLMutationResolver { private static final Logger LOGGER = LoggerFactory.getLogger(EmployeeQueries.class); @Autowired EmployeeRepository repository; public Employee newEmployee(Employee employee) { LOGGER.info("Employee add: employee={}", employee); return repository.add(employee); } public boolean deleteEmployee(Long id) { LOGGER.info("Employee delete: id={}", id); return repository.delete(id); } public Employee updateEmployee(Long id, Employee employee) { LOGGER.info("Employee update: id={}, employee={}", id, employee); return repository.update(id, employee); } }
وهنا نستخدم GraphiQL لاختبار الطفرات. فيما يلي أمر يضيف موظفًا جديدًا ويقبل الرد باستخدام معرف الموظف واسمه.
في هذا الصدد ، أوقف ترجمة هذه المقالة مؤقتًا وأكتب "الاستنساخ الغنائي" ، ولكني في الواقع استبدل وصف جزء الخدمات المصغرة من خلال عميل Apollo ، للتفاعل من خلال مكتبات GQL و Unirest - مكتبات لتنفيذ طلبات HTTP.
عميل GraphQL على Groovy.
لإنشاء استعلامات GraphQL في الخدمات الصغيرة التي تقدم الخدمات ، سأستخدم مُنشئو
الاستعلامات :
String queryString = DSL.buildQuery { query('employeesByDepartment', [departmentId: departmentId]) { returns { id name position salary } } }
ينشئ هذا البناء على DSL GQL استعلامًا عن النموذج:
{ employeesByDepartment (departmentId: 1) { id name position salary } }
علاوة على ذلك ، سأقوم بتنفيذ طلب HTTP على العنوان الذي تم تمريره إلى الطريقة.
سنكتشف كيفية تكوين عنوان الطلب بشكل أكبر.
(Unirest.post(serverUrl) .body(JsonOutput.toJson([query: queryString])) .asJson() .body.jsonObject['data']['employeesByDepartment'] as List) .collect { JsonUtils.jsonToData(it.toString(), Employee.class) }
بعد تلقي الرد ، نقوم بتحويله من JSONObject إلى عرض قائمة الموظف.
عميل GrpahQL للخدمة الصغيرة للموظفين
النظر في تنفيذ موظفي الخدمات المصغرة. في هذا المثال ، استخدمت عميل Eureka مباشرةً. يحصل eurekaClient على جميع مثيلات الخدمة الجارية المسجلة كخدمة موظف. ثم يختار عشوائيا بعض الأمثلة من تلك المسجلة (2). بعد ذلك ، يأخذ رقم المنفذ الخاص به ويشكل عنوان الطلب (3) ويمرره إلى كائن EmployeeGQL وهو عميل GraphQL على Groovy والذي تم وصفه في الفقرة السابقة.
@Component public class EmployeeClient { private static final Logger LOGGER = LoggerFactory.getLogger(EmployeeClient.class); private static final String SERVICE_NAME = "EMPLOYEE-SERVICE"; private static final String SERVER_URL = "http://localhost:%d/graphql"; Random r = new Random(); @Autowired private EurekaClient discoveryClient;
علاوة على ذلك ، "أنقل" الكلمة إلى المؤلف مرة أخرى ، أو بالأحرى أواصل ترجمة مقالته.أخيرًا ، يتم حقن EmployeeClient في فصل يستجيب لطلبات DepartmentQueries ويتم استخدامه داخل طلب الأقسام حسب منظمة الموظفين.
public List<Department> departmentsByOrganizationWithEmployees(Long organizationId) { LOGGER.info("Departments find: organizationId={}", organizationId); List<Department> departments = repository.findByOrganization(organizationId); for (int i = 0; i < departments.size(); i++) { departments.get(i).setEmployees(employeeClient.findByDepartment(departments.get(i).getId())); } return departments; }
قبل تقديم الطلبات اللازمة ، يجب أن نلقي نظرة على الرسم التخطيطي الذي تم إنشاؤه لخدمة القسم. يمكن أن يحتوي كل كائن في القسم على قائمة بالموظفين المعينين ، وقمنا أيضًا بتحديد نوع الموظف المشار إليه بواسطة نوع القسم.
schema { query: DepartmentQueries mutation: DepartmentMutations } type DepartmentQueries { departments: [Department] department(id: ID!): Department! departmentsByOrganization(organizationId: Int!): [Department] departmentsByOrganizationWithEmployees(organizationId: Int!): [Department] } type DepartmentMutations { newDepartment(department: DepartmentInput!): Department deleteDepartment(id: ID!) : Boolean updateDepartment(id: ID!, department: DepartmentInput!): Department } input DepartmentInput { organizationId: Int! name: String! } type Department { id: ID! organizationId: Int! name: String! employees: [Employee] } type Employee { id: ID! name: String! position: String! salary: Int! }
الآن يمكننا استدعاء استعلام الاختبار الخاص بنا مع قائمة الحقول المطلوبة باستخدام GraphiQL. يتوفر تطبيق خدمة القسم بشكل افتراضي على المنفذ 8091 ، أي يمكننا رؤيته على
http: // localhost: 8091 / graphiqlالخلاصة
ربما يكون GraphQL بديلاً مثيرًا للاهتمام لـ REST API القياسي. ومع ذلك ، لا ينبغي لنا اعتباره كبديل لـ REST. هناك العديد من الحالات التي قد يكون فيها GraphQL هو الخيار الأفضل ، ولكن تلك التي يكون فيها REST هو الخيار الأفضل. إذا لم يكن عملاؤك بحاجة إلى إرجاع جميع الحقول من جانب الخادم ، وعلاوة على ذلك لديك العديد من العملاء بمتطلبات مختلفة لنقطة إدخال واحدة ، فإن GraphQL هو خيار جيد. إذا نظرت إلى ما هو موجود في مجتمع الخدمات الصغيرة ، يمكنك أن ترى أنه لا يوجد الآن حل قائم على Java يسمح لك باستخدام GraphQL مع اكتشاف الخدمة أو الموازن أو بوابة API خارج الصندوق. في هذه المقالة ، عرضت مثالًا على استخدام GQL و Unirest لإنشاء عميل GraphQL مع Spring Cloud Eureka لاتصال الخدمات الصغيرة. نموذج كود لمؤلف مقال باللغة الإنجليزية على GitHub
github.com/piomin/sample-graphql-microservices.git .
مثال لي عندما تكون مع مكتبة GQL :
github.com/lynx-r/sample-graphql-microservices