Разработка Sergey Filimonov
1889

Опыт разработки статичного сайта с Airtable в качестве базы данных

lms.edmarket.ru — это расписание уроков для всех потоков курсов EdMarket. Это первый сайт в моей практике, который работает с Airtable в качестве базы данных — и это невероятно удобно в сравнении с тем, как эти публичные расписания выводились раньше через WordPress. Пост — об опыте разработки такого сайта.

В закладки
Как выглядит сайт lms.edmarket.ru на момент написания статьи

EdMarket обучает специалистов, экспертов и предпринимателей из разных сфер рынка создавать качественные онлайн-курсы, развивать образовательные проекты и запускать онлайн-школы

Текущий сайт edmarket.ru реализован мной и разработан на WordPress. Я мечтаю перевести его на статические рельсы, чтобы сайт был проще в разработке, быстрее и безопаснее. Но так как проект большой и каждую минуту на сайте происходят какие-то полезные для маркетинга и бизнеса действия, развернуть масштабную работу по переделке — задача непростая.

Поэтому я решил попробовать идти маленькими шагами и выносить в статику что-то частями. И начал я с расписаний.

Расписания — это сетка уроков для каждой группы учащихся, которая помогает им планировать своё обучение, в частности не пропускать уроки.

Изначально всё расписание было реализовано через Google Sheets, и это было крайне неудобно. Команда EdMarket попросила меня приспособить под это дело Airtable, так как многие области работы — планирование задачи команды и график рассылок — уже находятся там.

Структура данных внутри Airtable

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

Как работать с Airtable, я не так давно начал подробно рассказывать у себя на YouTube-канале.

Реализация с помощью WordPress и затем Gridsome

Задачу сделать возможным публикацию этих расписаний через iframe я изначально реализовал через WordPress. Для этого нужно было:

  1. Скопировать embed-код из Airtable.
  2. Создать страницу внутри WordPress.
  3. Вставить embed-код так, чтобы не поломать шаблон поста внутри WordPress.
  4. Опубликовать страницу.
  5. Проверить, всё ли в порядке.
  6. Очистить кэш.

Это несложные шаги, но я поставил перед собой задачу сделать эксперимент и упростить это всё с помощью статического фреймворка.

Все статичные сайты я создаю с помощью Gridsome — это фреймворк для разработки статических сайтов с помощью Vue и GraphQL, то есть почти полный аналог Gatsby, только появился несколько позже и построен не на React.

Так что реализовать задачу можно с помощью обоих фреймворков, это дело вкуса. На мой взгляд, Vue намного лучше подходит для разработки таких сайтов в силу более простых способов написания шаблонов и работы с данными внутри шаблона.

Для подключения Airtable к Gridsome я использовал плагин-коннектор @gridsome/source-airtable. Он просто добавляется к проекту, и параметры конфигурации заполняются нужными значениями.

В итоге весь код проекта содержательно выглядит так:

<template> <Layout> <div class="p-6"> <h1 class="font-bold mb-4">Расписания</h1> <ul class="flex flex-wrap"> <li class="mr-3 mb-4" v-for="item in categories"> <a class="inline-block border rounded py-1 px-3" :class="currentCategory == item ? 'border-purple-500 bg-purple-500 text-white' : 'border-gray-200 text-purple-500 hover:bg-gray-200 border-white'" href="#" @click="currentCategory = item">{{ item }}</a> </li> </ul> <div class="grid-cols grid-cols--6"> <g-link v-for="schedule in schedules" :key="schedule.node.id" :to="`/schedule/${schedule.node.slug}`" class="p-4 border border-gray-400 hover:shadow rounded"> <div class="text-xl text-center">{{ schedule.node.title }}</div> </g-link> </div> <div class="mt-8"> <div class="text-xl"> По всем вопросам пишите на <a href="mailto:ask@edmarket.ru" class="underline">ask@edmarket.ru</a> </div> </div> </div> </Layout> </template> <page-query> query Schedule { posts: allSchedule (sortBy: "slug", order: ASC) { edges { node { id title slug group url hash } } } } </page-query> <script> export default { data() { return { currentCategory: 'Все', categories: ['Все', 'OCM', 'IDO', 'IDOM', 'MOP', 'POC'] } }, metaInfo() { return { title: 'Расписания', meta: [ { vmid: 'description', name: 'description', content: '' } ] } }, computed: { schedules() { if (this.currentCategory === 'Все') { return this.$page.posts.edges } else { return this.$page.posts.edges.filter(edge => edge.node.slug.indexOf(this.currentCategory.toLowerCase()) >-1) } } } } </script>
<template> <Layout> <div class="p-6"> <h1 class="mb-4 font-bold">Расписание {{ $page.schedule.title }}</h1> <div class=""> <iframe class="airtable-embed" :src="`https://airtable.com/embed/${$page.schedule.hash}?backgroundColor=purple`" frameborder="0" onmousewheel="" width="100%" height="533" style="background: transparent; border: 1px solid #ccc;"></iframe> </div> </div> </Layout> </template> <page-query> query Schedule ($path: String!) { schedule: schedule (path: $path) { id title slug group url hash } } </page-query> <script> export default { metaInfo() { return { title: `Расписание ${this.$page.schedule.title}`, meta: [ { vmid: 'description', name: 'description', content: this.$page.schedule.title } ] } } } </script>

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

Осталось решить одну задачу — заставить статический сайт обновляться после обновления базы в Airtable.

Самым логичным способом было бы заставить Airtable отправлять событие о том, что таблица Site обновлена, в GitHub, а тот в свою очередь запустил бы создание новой рабочей версии сайта (билд, короче).

Для этого можно использовать Zapier, который умеет слушать изменения в Airtable, и... Netlify, который имеет интеграцию с Zapier. Но благодаря «точным» и «высокопрофессиональным» действиям РКН в отношении Telegram сервис Netlify из России чаще всего не работает.

Выход — использовать Now (он же Zeit.co). Но у него нет интеграции с Zapier. Поэтому пришлось идти обходным путём: настроить интеграцию какой-нибудь headless CMS с сайтом и сделать там «рычажок», потянув за который можно было бы обновить сайт вручную. Я выбрал Forestry.io.

Надо просто поменять значение update на что-то отличное от текущего. Это закоммитит изменение файла update.yaml на GitHub, а уже GitHub запустит пересборку сайта на CDN. Не так элегантно, как хотелось бы, но работает отлично

Что в итоге получилось

EdMarket "LMS" — это, конечно, не LMS в чистом виде, а всего лишь прототип одной из фич, которая могла бы быть в настоящей LMS, но в итоге получился очень простой и быстрый сайт, решающий текущую задачу команды. И для его управления достаточно не выходить за пределы Airtable. Ну, не считая одного действия в Forestry.

Для сравнения путь отдельного сайта с WordPress — это: устанавливать XAMPP, поднимать сервер локально разработки, создавать базу данных, настраивать интеграцию по Airtable, настраивать Cron (так как у Airtable есть лимит на запросы и делать обращение к Airtable для каждого показа пользователю не получится), создавать скрипт, который будет стягивать данные из Airtable и повторять структуру данных в точно такой же таблице на сервере, настраивать систему постов, PHP-шаблоны, запускать это всё на сервере, устанавливать кеширование, следить за безопасностью WordPress, настраивать обновление через GitHub...

В общем, я выберу вариант со статикой, который можно сделать за пару часов :)

У вас уже был опыт разработки статических сайтов?
Да
Нет
Показать результаты
Переголосовать
Проголосовать

На тему «статики» и Airtable часто пишу в своём блоге, Telegram-канале и YouTube.

Материал опубликован пользователем. Нажмите кнопку «Написать», чтобы поделиться мнением или рассказать о своём проекте.

Написать
{ "author_name": "Sergey Filimonov", "author_type": "self", "tags": [], "comments": 24, "likes": 10, "favorites": 28, "is_advertisement": false, "subsite_label": "dev", "id": 76805, "is_wide": true, "is_ugc": true, "date": "Thu, 25 Jul 2019 13:52:53 +0300" }
{"average":25900,"one":95,"ten":75}
Сколько денег вы откладываете в месяц?
Ответьте и узнаете, сколько копят другие.
0 ₽
70 000+ ₽
0 ₽
{ "id": 76805, "author_id": 35289, "diff_limit": 1000, "urls": {"diff":"\/comments\/76805\/get","add":"\/comments\/76805\/add","edit":"\/comments\/edit","remove":"\/admin\/comments\/remove","pin":"\/admin\/comments\/pin","get4edit":"\/comments\/get4edit","complain":"\/comments\/complain","load_more":"\/comments\/loading\/76805"}, "attach_limit": 2, "max_comment_text_length": 5000, "subsite_id": 235819, "last_count_and_date": null }
24 комментария

Популярные

По порядку

Написать комментарий...
0

Моя буханка едет и собирается за пару часов :) И если прочесть статью, то окажется, что это вовсе самый оптимальный вариант.

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

Я выберу вариант со статикой :)

Ответить
1

Вы как то сильно усложняете пусть с вордпрессом. Зачем «поднимать сервер» и т.д.? Любая домохозяйка установит вордпресс на хостинге за пару минут.

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

По вашему решению с отдельной CMS только для обновления данных. Может быть написать скрипт, который сам будет коммитить update.yaml на GitHub и дать на него ссылку в конце самой таблице airtable? Редактор внесет изменения в таблицу и нажмет кнопку «обновиться расписание».

Ответить
0

Вы как то сильно усложняете пусть с вордпрессом. Зачем «поднимать сервер» и т.д.? Любая домохозяйка установит вордпресс на хостинге за пару минут.

Можно так, то разрабатывать сайт на боевом сервере через FTP — так себе удовольствие, согласитесь :) Нормальный процесс — сделал локально, проверил, зедеплоил (можно без стейджа). Если делать так, то процесс с WP будет ровно такой, как я описал выше.

Может быть написать скрипт, который сам будет коммитить update.yaml на GitHub и дать на него ссылку в конце самой таблице airtable? Редактор внесет изменения в таблицу и нажмет кнопку «обновиться расписание».

А где предлагаете разместить кнопку «Обновить»?

Ответить
0

Не знаю, я не делаю сайты локально. Мне удобней, чтобы сайт сразу был в сети.

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

В конце таблицы сделать ячейку со ссылкой и покрасить ее в красный цвет. Будет почти натуральная кнопка) В экселе можно, в airtable свои сложности?

Ответить
0

Не знаю, я не делаю сайты локально. Мне удобней, чтобы сайт сразу был в сети.

То есть, если надо поправить систему оплаты, вы всё делаете на боевом сервере и, если на сайте большой трафик, и регулярно люди хотят оплатить, то стабильно запросто рискуете потерять часть оплат в ходе отладки новой фичи?

Но «знаменитая двух минутная установка» вроде как фишка вордпресса.

Это было давно. Сейчас аудитория, которой нужна такая установка, пользуется Тильдой и другими конструкторами — и это несомненный прогресс в сравнении с WordPress.

В конце таблицы сделать ячейку со ссылкой и покрасить ее в красный цвет. Будет почти натуральная кнопка) В экселе можно, в airtable свои сложности?

Airtable — не табличный сервис, это визуализированная база данных. Ну, допустим, можно создать отдельную таблицу ради одной записи, где будет ссылка. Но куда будет вести ссылка? Отвечу сразу на свой вопрос, потому что других вариантов нет — нужно запускать микросервис с интеграцией к ГитХабу ради этой кнопки, который будет коммитить изменения. Это точно перебор :)

И повторюсь, что решение с Forestry не идеальное, а вынужденное из-за РКН.

Ответить
0

Ох, Сергей. Что-то я потерял нить разговора. Причем тут Тильда и боевой сервер с трафиком и оплатами? Установить вордпресс — минутное дело. Вот и все, что я хотел сказать.

Ссылку может вести на скрипт на том же forestry или куда угодно. Для того, чтобы редактор пользовался airtable в одном окне и не заходил в другие панели управления, чтобы обновить данные.

Ответить
0

Ох, Сергей. Что-то я потерял нить разговора. Причем тут Тильда и боевой сервер с трафиком и оплатами? Установить вордпресс — минутное дело. Вот и все, что я хотел сказать.

Разрабатывать что-либо сразу на боевом сервере без локальной версии — плохая практика. Если вас устраивает, ок и для вас WordPress это действительно минутное дело. Меня такой подход не устраивает.

Ссылку может вести на скрипт на том же forestry или куда угодно. Для того, чтобы редактор пользовался airtable в одном окне и не заходил в другие панели управления, чтобы обновить данные.

Я думаю, что вы всё-таки не очень понимаете матчасть того, что обсуждается в статье. Если непонятно — спрашивайте, а не утверждайте.

Ответить
0

Согласен, не очень) Это было скорее предложение/вопрос.

Ответить
0

Если коротко, то нет проблемы где-то разместить ссылку внутри Airtable. Главное — заставить эту ссылку что-то делать. Отчасти это ещё обсуждено ниже с Олегом Якуниным.

Ответить
4

Какой-то сомнительный подход,в итоге чтобы упростить, все наоборот усложнилось, и дальнейшая разработка, и поддержка. Теперь вместо одного wordpress используется зоопарк сервисов для интеграции, костыльная логика и при этом чтобы обновить все равно нужно заходить в отдельную админку и менять значение поля вручную! 🤦 Страшно даже представить какой монстр у вас получится, если вы все же доведете задуманное до конца и действительно сделаете весь сайт статичным :)

Почему нельзя было сделать синхронизацию данных с AirTable напрямую с базой Wordpress по апи - загадка. Перенести фронтент и тему сайта на Vue тоже не является проблемой.

Ответить
1

Почему нельзя было сделать синхронизацию данных с AirTable напрямую с базой Wordpress по апи - загадка. Перенести фронтент и тему сайта на Vue тоже не является проблемой.

У вас есть примеры и того и другого?

Ответить
1

Интересный подход!

Ответить
0

Добрый день. Не могли бы вы перенести код со скриншотов в блок «Код» в редакторе?

Ответить
2

Сделал :)

Ответить
1

Большое спасибо!

Ответить
0

Не знаю насколько это правда, но говорят что у AirTable очень крутое серверное API.

Ответить
1

Весьма. Но маленький лимит на запросы. Если использовать это API для обычного, динамического сайта, то лимит быстро исчерпается. Но Airtable хорошо подходит для статического сайта, так как запросы идут к базе только в момент сборки.

* корректно — Airtable, строчная t ;)

Ответить
0

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

Полагаю, увеличатся трудозатраты, зато без сторонних сервисов и необходимости обновлять вручную. Или Airtable  принципиален?

Возможно, я где-то неправ. Очень нравится ваш сайт и ваша деятельность, смотрю дизайн обновили )

Ответить
0

Работаю на рельсах, к джемстеку только перехожу постепенно, хочу освоить Vue

Собственно, можно построить и бэкенд на рельсах, а на вуе уже сделать фронт и по апи брать инфу. Наверное, можно ещё и вэбсокеты прикрутить для обновлений интерфейса без ф5.

Это уже не JAMstack будет. Это обычный full-stack. Поясню дальше.

Полагаю, увеличатся трудозатраты, зато без сторонних сервисов и необходимости обновлять вручную

100% — это намного сложнее. Если бы задача стояла сделать приложение обычным способом, я бы выбрал JS-стек: Express + Vue + что-то для сбора и слоя хранения данных из Airtable. Это явно не пара часов на всё про всё. Если, конечно, нет личных наработок. В случае с Gridsome — всё готово буквально из коробки.

Или Airtable принципиален?

Да. Цитата из статьи:

Изначально всё расписание было реализовано через Google Sheets, и это было крайне неудобно. Команда EdMarket попросила меня приспособить под это дело Airtable, так как многие области работы — планирование задачи команды и график рассылок — уже находятся там.

Очень нравится ваш сайт и ваша деятельность, смотрю дизайн обновили )

Круто! :) Да, недавно полностью обновил)

Ответить
0

По поводу фулл-стэк, у нас же будет отдельный бэк, с которым общаемся по апи, rest или graphql, не суть, а на фронте уже стоит гридсам, то есть 2 отдельных приложения, по сути. Как раз буква A в JAM и подразумевает это, разве нет?

В плане решений из коробки - рельсы классный инструмент для бэка.

Ответить
0

Как раз буква A в JAM и подразумевает это, разве нет?

Нет. A = API, то есть возможность получать данные в первую очередь через другие API, а не строить свои. Свои, конечно, можно строить, но это напрямую к JAMstack отношения не имеет — это находится за пределами JAMstack.

JAMstack — это plug-in-play для разработки статичных сайтов.

Ответить
0

Понял, спасибо за разъяснение.

Вот ещё такой вопросик - а если нужно создать сайт, куда контент-менеджер может вносить изменения, к примеру, создавать новые объекты некой сущности, типичный CRUD, в общем, вы будете использовать gridsome и хранить это в некой бд, типа firebase? Или же другой подход?

Ответить
1

За это уже отвечает буква M) Markdown. К сайту просто подключается любая headless CMS, которая коммитит изменения в репозиторий файлами .md + frontmatter. Можно подключить файл .yaml.

Можно вообще через API WordPress всё сделать.

Бекенд для этого писать не нужно ни в одном случае.

Ответить
0
{ "page_type": "article" }

Прямой эфир

[ { "id": 1, "label": "100%×150_Branding_desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox_method": "createAdaptive", "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "ezfl" } } }, { "id": 2, "label": "1200х400", "provider": "adfox", "adaptive": [ "phone" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "ezfn" } } }, { "id": 3, "label": "240х200 _ТГБ_desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "fizc" } } }, { "id": 4, "label": "240х200_mobile", "provider": "adfox", "adaptive": [ "phone" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "flbq" } } }, { "id": 5, "label": "300x500_desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "ezfk" } } }, { "id": 6, "label": "1180х250_Interpool_баннер над комментариями_Desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "pp": "h", "ps": "bugf", "p2": "ffyh" } } }, { "id": 7, "label": "Article Footer 100%_desktop_mobile", "provider": "adfox", "adaptive": [ "desktop", "tablet", "phone" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "fjxb" } } }, { "id": 8, "label": "Fullscreen Desktop", "provider": "adfox", "adaptive": [ "desktop", "tablet" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "fjoh" } } }, { "id": 9, "label": "Fullscreen Mobile", "provider": "adfox", "adaptive": [ "phone" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "fjog" } } }, { "id": 10, "disable": true, "label": "Native Partner Desktop", "provider": "adfox", "adaptive": [ "desktop", "tablet" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fmyb" } } }, { "id": 11, "disable": true, "label": "Native Partner Mobile", "provider": "adfox", "adaptive": [ "phone" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fmyc" } } }, { "id": 12, "label": "Кнопка в шапке", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "p1": "bscsh", "p2": "fdhx" } } }, { "id": 13, "label": "DM InPage Video PartnerCode", "provider": "adfox", "adaptive": [ "desktop", "tablet", "phone" ], "adfox_method": "createAdaptive", "adfox": { "ownerId": 228129, "params": { "pp": "h", "ps": "bugf", "p2": "flvn" } } }, { "id": 14, "label": "Yandex context video banner", "provider": "yandex", "yandex": { "block_id": "VI-223676-0", "render_to": "inpage_VI-223676-0-1104503429", "adfox_url": "//ads.adfox.ru/228129/getCode?pp=h&ps=bugf&p2=fpjw&puid1=&puid2=&puid3=&puid4=&puid8=&puid9=&puid10=&puid21=&puid22=&puid31=&puid32=&puid33=&fmt=1&dl={REFERER}&pr=" } }, { "id": 15, "label": "Плашка на главной", "provider": "adfox", "adaptive": [ "desktop", "tablet", "phone" ], "adfox": { "ownerId": 228129, "params": { "p1": "byudx", "p2": "ftjf" } } }, { "id": 16, "label": "Кнопка в шапке мобайл", "provider": "adfox", "adaptive": [ "tablet", "phone" ], "adfox": { "ownerId": 228129, "params": { "p1": "byzqf", "p2": "ftwx" } } }, { "id": 17, "label": "Stratum Desktop", "provider": "adfox", "adaptive": [ "desktop" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "fzvb" } } }, { "id": 18, "label": "Stratum Mobile", "provider": "adfox", "adaptive": [ "tablet", "phone" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "fzvc" } } }, { "id": 19, "label": "Тизер на главной", "provider": "adfox", "adaptive": [ "desktop", "tablet", "phone" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "p1": "cbltd", "p2": "gazs" } } } ]
Приложение-плацебо скачали
больше миллиона раз
Подписаться на push-уведомления
{ "page_type": "default" }