Автоматически генерируемая CMS использую вашу готовую GraphQL схему

image

В моей предыдущей статье «ну Русском. Оригинал» я рассказывал как можно вдвое сократить свой код и время, если вы используете GraphQL вместе с Mongoose.


Сегодня речь также пойдет о технологии GraphQL и если вы работаете с ней то данная статья сможет помочь вам сохранить приличное количество времени на разработку. Оригинал статьи на английском вы можете найту по ссылке.


Ссылка на сам модуль: graphql-auto-generating-cms.


Как вы уже наверное догадались данный модуль использует вашу готовую GraphQL схему для генерации полнофункциональной CMS. Вам не нужно будет тратить кучу времени на разработку административной панели, и вы сможете больше сосредоточиться на бизнес процессах вашего проекта и на его архитектуре.


Важным достоинством данного модуля является то что он не требует от вас никаких изменений в вашем готовом коде или архитектуре проекта, что делает его интеграцию максимально простым даже на уже готовых проектах.


У вас есть два способа использовать данный модуль.


  1. Первый максимально быстрый и подойдет для нового проекта. От вас требуется только GraphQL схема и следование легкому паттерну по наименованию GraphQL методов и типов.
  2. И второй способ который не требует от вас следованию никаких паттернов, и может быть легко интегрирован в уже готовый проект. Все что от вас нужно предоставить конфигурационный объект вместе с GraphQL схемой.

На данный момент модуль не поддерживает GraphQLList, вложенные объекты и загрузку файлов, их поддержка будет реализована в будущих версиях. На данный момент вы можете легко обойти эти ограничения используя самописные функции и компоненты которыми вы можете дополнить CMS, мы рассмотрим как это сделать в данной статье.


Исходя из всего выше перечисленного давайте разделим текущую статью на несколько пунктов:


  • Общие правила
  • Подготовка к работе
  • Первый способ использования, с паттерном
  • Второй способ использования, с конфигурационным объектом
  • Дополнение CMS своими компонентами и функциями


Общие правила
Модуль будет использовать graphQL тип как сущность которая будет доступна в боковом меню CMS.
Чтобы исключить типы которые используются только как вложенные объекты. Соответственно за сущность будут взяты только те типы у которых есть один «Query» метод и хотя бы один «Mutation» метод.
Каждый тип может иметь только один «Query» [find], который будет использоваться для получения массива объектов, или для получение одного объекта с использованием аргумента «id» или »_id» в зависимости какой тип базы данных вы используете. Так же тип может иметь один или несколько из следующих «Mutation» методов [create, update, remove].


Query метод должен обязательно поддерживать следующие аргументы:


{
offset: number,
limit: number,
id: number || _id: string // в зависимости от вашего хранилища
}

Аргументы «offset» и «limit» будут использованы для нумерации страниц и что бы получать данные с сервера по порциям, а не скажем получить сразу 50т. объектов из за чего ваше приложение просто повиснет.
На серверной стороне это может выглядеть следующим образом:


let {offset, limit} = args; 

return new Promise((resolve, reject) => {
   Ingredients.find(query).skip(offset).limit(limit).exec((err, res) => err ? reject(err) : resolve(res));
});

И аргумент «id» будет использован чтобы получить один экземпляр. Если вы будете использовать метод вроде «findOne» чтобы вернуть один элемент используя «id» убедитесь что вы вернете его внутри массива, так как graphQL будет ожидать именно массив, даже если это один элемент.


Подготовка к работе
Установка:
npm i -S graphql-auto-generating-cms


На серверной стороне мы должны запустить middleware через которое будем обрабатывать GrpahQL схему, в примере ниже мы будем использовать URL »/graphql_cms_endpoint» в качестве конечной точки:


…
import express from 'express';
import graphqlCMS from 'graphql-auto-generating-cms/lib/middleware';
import schema from '../schema';
const printSchema = require('graphql/utilities/schemaPrinter').printSchema;
let app = express();

let config = {schema: printSchema(schema)}
app.use('/graphql_cms_endpoint', graphqlCMS(config));
…
app.listen(port)

После этого мы можем использовать наш React компонент на клиентской стороне. Вы можете загрузить его по отдельной ссылке или как обычный React компонент.


Внутри роутера:


...
import GraphqlCMS from 'graphql-auto-generating-cms';

export default (
     window.scrollTo(0, 0)} history={browserHistory}>
        

Или в качестве компонента:



В свойстве «endpoint» мы указываем тот же URL который мы использовали на серверной стороне.
В свойстве «graphql» ссылка на ваш GraphQL API.
Второй вариант хорошо подходит если вы скажем хотите дополнить CMS функциями авторизации и тп. Вы просто вставите его как дочерний компонент в Layout с навигационной панелью, системой авторизации и тп.


Первый способ использования, с паттерном
Чтобы использовать быстрый способ генерации вашей CMS, без дополнительных конфигураций, вам нужно следовать следующему простому паттерны в наименовании GrpahQL «Query» и «Mutation» методах.
[graphql Type name]_[action]


пример:


productType_find
productType_create
productType_update
productType_remove

Сортировка пунктов меню и input полей в CMS будет идентично порядку свойств в объекте.


Например если в нашей схеме следующий порядок типов:


{
productType: {},
userType: {},
categoryType: {},
...
}

То в боковой панели, пункты меню будут иметь тот же порядок:


productType
userType
categoryType

То же самое относится к порядку полей типов, их порядок будет взят за основу построения UI для страницы просмотра одного экземпляра элемента, или для добавления нового элемента:



let productType = new GraphQLObjectType({
    name: 'productType',
    fields: {
        _id: {type: GraphQLString},
        title: {type: GraphQLString},
        shortDescription: {type: GraphQLString},
        price: {type: GraphQLString},
        isPublished: {type: GraphQLBoolean},
        createdAt: {type: GraphQLString},
        updatedAt: {type: GraphQLString},
        bulletPoints: {type: GraphQLString},
        scienceShort: {type: GraphQLString},
        scienceFull: {type: GraphQLString},
    }
});

Соответственно если вы хотите изменить порядок пунктов в боковом меню, или поменять порядок input полей, вы просто должны поменять местами свойства в GrpahQL схеме.


Второй способ использования, с конфигурационным объектом
Преимущества второго способа:


  • Вам не нужно вносить никакие изменения в текущий код и/или архитектуру
  • Не обязательно использовать naming паттерн
  • Вы можете запретить любые «Mutation» методы для каждого типа
  • Вы можете изменить сортировку полей и бокового меню, без изменения в схеме, просто меняя порядок правил в свойстве «rules»
  • Вы можете задавать произвольное название пунктов бокового меню, и всех полей. Изначально в качестве названий пунктов меню будут использованы имена graphQL типов, и в качестве название полей, будет использовано название свойств типа
  • Вы можете запретить редактирование любых полей
  • Вы можете задать произвольно какие свойства будут использоваться внутри таблицы со списком документов. Изначально для поля [#/ID] будет использовано свойство «id» или »_id» типа, и в качестве [title] будет использовано второе свойство GraphQL типа
  • Вы можете изменять какое поле нужно использовать для конкретного свойства, к примеру использовать input или textarea, изменить input тип, скажем на «date» и тп

Давайте рассмотрим как это работает, все что вам для этого нужно, расширить конфигурационный объект на серверной стороне. Все поля кроме «schema» не обязательны к использованию.


let config = {schema: printSchema(schema)}
app.use('/graphql_cms_endpoint', graphqlCMS(config));

let config = {
    schema: printSchema(schema), 
    // ваша "printed" схема [required]

    exclude: ['paymentType', 'invoiceType'], 
    //graphql типы котрые вы хотите исключить из CMS

    rules: {
    // дерево правил для каждого или некоторых graphQL типов

        categoryType: { 
        // имя вашего graphQL типа

            label: 'Categories', 
            // кастомное имя которое будет использовано в боков меню

            listHeader: {
            // данные из этого свойства используются для таблицы объектов
            // первый столбец [id] и второй [title] 
            // вы можете использовать одно или несколько свойств для каждого столбца
            // в UI они будут отображаться как "String” + " " + "String”

                id: ['id'],
                title: ['description']
            },

            resolvers: {
            // если вы не хотите использовать naming паттерн
            // вы должны указать имя для каждого 
            // Query's и Mutation's метода для данного типа

                find: {
                    resolver: 'getCategories' 
                    // Query method name
                },
                create: {
                    resolver: 'addCategory'
                    // Mutation method name

                    allowed: true
                },
                update: {
                    resolver: 'updateCategory'
                    // Mutation method name

                    allowed: true
                },
                remove: {
                    allowed: false
                    // если вы не хотите предоставлять доступ
                    // к данному методу со стороны клиента
                    // вы можете его запретить, указав true 
                    // кром "find”, его нельзя запретить
                }
            },
            fields: {
                _id: {}, 
                sortNumber: {
                    label: 'custom field name to show in UI',
                    inputControl: 'input', 
                    // can be "input” or "textarea”

                    inputType: 'number', 
                    // can be any input type: date, text, file etc.

                    disabled: true, 
                    // запретить редактирование данного поля

                    exclude: false, 
                    // не отображать данное поле в CMS
                },
                name: {}, 
                // вы также можете использовать пустой объект
                // скажем если вы хотите просто отсортировать элементы

                createdAt: {},
                updatedAt: {},
                isPublished: {}
            }
        }
    }
}

Дополнение CMS своими компонентами и функциями
Вы также можете дополнить пункты меню CMS своими React компоненты с дополнительными функциями, скажем для каких нибудь кастомных решений, таких как статистика или общий dashboard.


Все что для этого нужно просто добавить еще одно свойство 'newMenuItems' на клиентской стороне, к компоненту:



Через которое мы отправляем массив с дополнительными пунктами меню и компонентами, структура объектов в массиве ниже:



let customPages = [
    {
       label: 'Custom Page',
       secret: 'uniqeForEachComponentSecret',
       view: {
           secret: 'sameUniqeComponentSecret',
           component: CustomDashboard // Ваш React компонент
       }
    }
]

Код выше будет выглядеть следующим образом:


7c2bafaeab5e4696b1a05ca9ff78fd60.png

Спасибо за внимание, надеюсь данный модуль будет вам полезен.
Чтобы быть в курсе обновлений или новых функций, вы можете добавить проект в избранные, на GitHub.


example code
GitHub


Несколько скриншотов рабочей версии:


2fa8e4d48d334ee39fe8b38072c51a7e.png
e7430f19f96447858fcffb8f7ebe0f3c.png
c302810fe16f40ca97f9380bd006f9ea.png
2bc1cb357e154cefa03ced12df5519a5.png
b5adf9a21fa549c586ebad9ef7ae7846.png

Комментарии (4)

  • 3 ноября 2016 в 05:53

    0

    Круто! Я только, что прикрутил GraphQL к UmbracoCMS и React-Relay клиент и влюбился в технологию. Следующим шагом надо ASP.NET Core с ними подружить, а там и ваш модуль поможет.

    А почему без Relay? Или когда начинали разработку его еще в доступе не было?

    • 3 ноября 2016 в 07:13 (комментарий был изменён)

      0

      Спасибо) Я уже успел поработать и с Relay и со всем остальным что связано с GraphQL. Вообще не могу вам посоветовать использовать Relay. Очень специфический продукт заточенный под нужды facebook. Он очень тесно связывается в архитектуру приложения, у него очень много недостатков, и он довольно сильно усложнен для всего лишь одной простой задачи для которой его используют 99% приложений, для отправки запросов на graphql сервер и ожидании ответа.

      Я посоветую вам Apollo, максимально простой инструмент, идеальный для 90% web приложений работающих с graphql API. Сравнивая Relay и Apollo, практически то же самое что Flux и Redux. Поэтому внимательно ознакомьтесь с обеими технологиями перед использованием Relay.
      Лично я в своих продакшн проектах использую именно Apollo.

      В данном модуле я не использовал ни Relay ни Apollo, чтобы не усложнять архитектуру приложения и сделать его максимально простым. Ну и самое главное, для запросов я использую свою функцию которая позволяет мне динамически строить graphql запросы, чего я не могу сделать ни в Relay ни в Apollo.

      Также пару часов назад опубликовал небольшой патч к модулю.
      Теперь все input в которых string.length > 100 будут автоматически переделаны в textarea, то же самое при наборе текста в input, если длина будет больше 100 символов, автоматически будет использован «textarea». И немного улучшено автоматическое определение полей где нужно использовать тип «date».

    • 3 ноября 2016 в 07:46

      0

      Да и если интересно, то для запросов использую один из моих модулей, можете найти тут. Думаю будет полезен.
  • 3 ноября 2016 в 10:37 (комментарий был изменён)

    0

    Насколько реально (и есть ли смысл) писать бэкенд для этого модуля на чем-то, отличном от NodeJS (например, на ASP.NET)?

© Habrahabr.ru