API-based CMS подход

Перевод
Автор оригинала Levi Gable
Оригинал The API-Based CMS Approach
API-based CMS подход

Эта статья от Levi Gable. Levi погружается в идею отделения самой CMS от представления её содержимого. Levi объясняет, что это даёт свободу и приносит огромную пользу веб-разработчикам. Сайт может обрабатывать API и представлять содержимое тем способом, который вам нужен. Но что ещё более важно, API может обслуживать несколько платформ. Levi демонстрирует как с помощью одного API можно сделать не просто шаблонный сайт, но и React Native приложение, и даже приложение для Apple Watch.

В мире веб-разработки есть такой тренд, который называется API-based CMS. Так же известная как "несвязанная (decoupled)" или "безголовая (headless)". Она позволяет разработчикам отделить управление содержимым от реализации клиентской части и подключается к CMS через API. Это очень интересный подход к разработке сайтов и приложений, который предлагает больше свободы и гибкости. Я хочу показать вам свой процесс настройки и подключения такой CMS.

Для каждого типа содержимого необходимого вашему сайту, нужно проделать три шага:

  1. Создать собственный тип содержимого и настроить его поля;
  2. Настроить приложение на получение содержимого через API;
  3. Загрузить содержимое в шаблон вашей страницы;

Давай рассмотрим эти шаги более подробно, на примере того как я буду настраивать простой новостной сайт (демо) с главной страницей и страницами новостных статей.

1. Страница статьи

Создание пользовательского типа содержимого для статьи

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

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

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

API-based CMS подход

CMS которую я использовал в этом примере называется prismic.io, хотя любая CMS которая может отдавать данные в формате JSON могла бы быть использована здесь. Для примера, на CSS-Tricks есть целый ряд статей о WordPress JSON API и похоже что у Perch Runway 3 будут возможности "headless CMS". Prismic также использует JSON для создания типов контента и настройки их полей. JSON код моих типов контента, которые я использовал при написании этой статьи есть в проекте на Github'е. Вы можете найти их в директории "customTypes".

После того как вы создадите и настроите свой тип содержимого, создайте несколько статей, а в следующем разделе мы рассмотрим как получать эти статьи через API.

Настройка клиентской части приложения для запросов к API и загрузки статей

Это то место где API-based CMS на самом деле блистает. Вы можете создать свой сайт используя любую предпочитаемую технологию. Многие API-based CMS предоставляют специальные SDK для основных технологий, в которых есть все необходимые методы для запросов к API и разбора возвращаемых данных. Это позволяет легко начать разработку вашего проекта.

Не важно на чём вы пишете код, вам по-любому нужно выполнить запрос к API и получить данные которые вы хотите отобразить. Я разрабатывал этот пример используя SDK NodeJS, который предоставил мне prismic.io.

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

app.route('/:uid').get(function(req, res) {
  var uid = req.params.uid;
  api(req, res).then(function(api) {
    // Here we are querying for a custom type ‘article’ by its unique ID
    return api.getByUID('article', uid);
  }).then(function(pageContent) {
    res.render('article', {
      pageContent: pageContent
    });
  });
});

Загрузка содержимого в шаблоны страниц

После того как мы получили необходимые данные из API, всё что остаётся сделать это вывести их в шаблоны. API будет возвращать JSON объект, который содержит весь наш контент.

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

В нашем примере, для создания HTML страниц будет использоваться шаблонизатор Pug (ранее Jade). Интегрировать содержимого в страницу очень просто и быстро. Все что нам нужно сделать, это заменить статический контент на то что мы получили из API.

Ниже показан код вывода содержимого в шаблоне, но в нём нет таких разделов как шапка или лэйаут. Если вы хотите увидеть код полностью, не стесняйтесь посмотреть весь проект на Github.

extends ./layout.pug

block body
  include ./partials/header.pug

  div.article.container
    include ./partials/back.pug
    
    h1
      //- Here we insert the StructuredText field ‘title’ from the custom type ‘article’
      != pageContent.getStructuredText('article.title').asText() || 'Untitled'
    img.article-image(src=pageContent.getImage('article.image').url, class='star')

    - var sliceZone = pageContent.getSliceZone('article.body') || {}
    for slice in sliceZone.slices
      //- Render the right markup for a given slice type.
      case slice.sliceType
        
        // Text Section 
        when 'text'
          div.article-section.text
            != slice.value.asHtml()

        // Quote Section
        when 'quote'
          div.article-section.quote
            span.block-quotation !{slice.value.asText()}

        // Image Section
        when 'image-with-caption'
          - var imageWithCaption = slice.value.toArray()[0]
          - var imageUrl = imageWithCaption.getImage('illustration') ? imageWithCaption.getImage('illustration').url : ''
          - var caption = imageWithCaption.get('caption')
          div.article-section.image
            img(src=imageUrl)
            if caption
              p
                span.image-label !{caption.asText()}
          
    include ./partials/back.pug

Мы только что вывели страницы для статей нашего сайта. Быстренько снова пройдёмся через этот же процесс и настроим домашнюю страницу.

2. Домашняя страница и лэйаут

Создание пользовательского типа содержимого для лэйаута

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

image03-1

Запрос к API и загрузка домашней страницы

Чтобы получить содержимое домашней страницы, на самом деле нам нужно выполнить два запроса к API. Первый для получения лэйаута, а второй для получения всех статей отображаемых на главной странице. При использовании NodeJS это может выглядеть так:

// Route for homepage
app.route('/').get(function(req, res) {
  api(req, res).then(function(api) {
    // Query the site-layout content
    api.getSingle("site-layout").then(function(layout) {
      // Then query all the articles and sort by date
      api.query(
        prismic.Predicates.at("document.type", "article"),
        { orderings: '[my.article.date desc]' }
      ).then(function(articles) {
        res.render('homepage', {
          layout: layout,
          articles: articles.results
        });
      }).catch(function(err) {
        handleError(err, req, res);
      });
    }).catch(function(err) {
      handleError(err, req, res);
    });
  });
});

Загрузка содержимого в шаблон домашней страницы

Для добавления данных из лэйаута, нужно просто обновить файл header.pug.

header
  a(href="./")
    p.logo
      != layout.getText('site-layout.logo')
  a.article-link(href=layout.getLink('site-layout.link').url() target="_blank")
    != layout.getText('site-layout.link-text')

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

div.featured-article-container
  div.featured-article(style="background-image: url(" + article.getImageView("article.image", "featured").url + ");")
    div.featured-content
      h2
        != article.getStructuredText('article.title').asText() || 'Untitled'

      //- display first valid slice text and limit it respecting the end of words.
      - var firstParagraph = article.getFirstParagraph()
      - var firstParagraphInPost = firstParagraph ? firstParagraph.text : ''
      - var textLimit = 100
      - var limitedText = firstParagraphInPost.substr(0, textLimit)
      p.description
        if firstParagraphInPost.length > textLimit
          = limitedText.substr(0, limitedText.lastIndexOf(' ')) + '...'
        else
          = firstParagraphInPost
      a.button.featured-button(href=ctx.linkResolver(article)) Read the article

И всё что нам остаётся сделать это интегрировать оставшиеся статьи.

div.article-tile
  div.article-tile-image(style="background-image: url(" + article.getImageView('article.image', 'tile').url + ");")
  img.article-tile-mobile-image(src=article.getImageView('article.image', 'tile-mobile').url)
  div.article-tile-content
    h2
      != article.getStructuredText('article.title').asText() || 'Untitled'
    p.meta-info 
      != article.getText('article.author')
      span.date  - 
        != ctx.dateConverter(article.getTimestamp('article.date'))
    //- display first valid slice text and limit it respecting the end of words.
    - var firstParagraph = article.getFirstParagraph()
    - var firstParagraphInPost = firstParagraph ? firstParagraph.text : ''
    - var textLimit = 300
    - var limitedText = firstParagraphInPost.substr(0, textLimit)
    p.description
      if firstParagraphInPost.length > textLimit
        = limitedText.substr(0, limitedText.lastIndexOf(' ')) + '...'
      else
        = firstParagraphInPost
    a.button(href=ctx.linkResolver(article)) Read the article

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

Другие области применения

Ещё одно преимущество API-based CMS заключается в том, что вы можете осуществлять запросы и загружать содержимое и на других платформах, таких как приложения для смартфонов. Для демонстрации я создал iOS приложение, которое запрашивает этот же API и выводит список статей на смартфоне.

При разработке этого приложения я использовал React Native и тот же подход, который описан выше. Я выбрал React Native потому что он позволяет создавать приложения с богатым UI, используя только JavaScript. На данный момент я сделал только iOS приложение, но React Native позволяет также легко запускать приложения и для Andorid.

Типы содержимого уже готовы, поэтому можно делать запросы к API. Вот запрос получения содержимого для одной статьи:

// The query for the article
async function article (uid) {
  try {
    const api = await PrismicHelper.getApi()
    const layoutDoc = await api.getSingle('site-layout')
    const articleDoc = await api.getByUID("article", uid)
    return {layoutDoc, articleDoc}
  } catch(error) {
    console.log(error);
    return {};
  }
}

После того как содержимое было получено, можно отобразить его в представлении (views) тем же методом, который был использован выше для сайта. Снова простой пример того как это может выглядеть.

<ScrollView>
  <StatusBar
    hidden
  />
  <View style={styles.container}>
    <Text style={styles.logo}>
      {layoutDoc.getText('site-layout.logo')}
    </Text>

    <TouchableHighlight onPress={ () => this._navigate() } underlayColor='rgba(0,0,0,0)'>
      <Text style={styles.back}>&larr; back to list</Text>
    </TouchableHighlight>

    <Text style={styles.title}>
      {article.getStructuredText('article.title').asText()}
    </Text>

    <Image source={{uri: article.getImage('article.image').url}} style={[styles.mainImage, styles.section]} resizeMode="cover"/>

    { !content ?
      <Text>Content is missing, try again later</Text>
    :
      <View style={styles.contentWrapper}>
        {content}

        <TouchableHighlight onPress={ () => this._navigate() } underlayColor='rgba(0,0,0,0)'>
          <Text style={styles.back}>&larr; back to list</Text>
        </TouchableHighlight>
      </View>
    }
  </View>
</ScrollView>

Преимущество создания мобильного приложения отдельно от сайта заключается в том, что вы можете лучше соединится с вашей аудиторией и облегчит им доступ к вашему контенту с помощью мобильных телефонов. Можно пойти дальше и посылать им Push-уведомления всякий раз когда на сайте появляется новая статья. Я нашёл простой способ интегрировать уведомления с помощью Urban Airship.

Надеюсь, что статья дала вам представление о том, чего можно ожидать от API-based CMS. Мне нравится свобода и уровень контроля, который предоставляет API-based CMS. Создание собственных типов контента - это то что нужно вам для начала. После этого вы сможете использовать технологии и шаблонизаторы, которые предпочитаете. И подключаться к API для лёгкого вывода вашего содержимого в шаблонах.

Комментарии

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