Создание Endpoint в формате JSON для реализации поиска по сайту в Astro

31 декабря 2022 г.

Для реализации поиска по сайту, созданному на стеке Jamstack, можно воспользоваться популярной библиотекой List.js. У библиотеки неплохая документация на официальном сайте. Тем не менее, в зависимости от выбранной парадигмы, библиотеке может потребоваться доступ к структурированным данными постов сайта. В этой инструкции я покажу пример создания такого объекта при разработке сайта на Astro.

Для этого воспользуемся, встроенной в Astro, возможностью создания Endpoints любого формата. В папке pages создаём файл posts.json.js со следующим содержимым:

src/pages/posts.json.js
import { getCollection } from 'astro:content';
const posts = await getCollection('posts');
 
const json = posts.map((post) => ({
  date: post.data.date,
  title: post.data.title,
  description: post.data.description,
  body: post.body,
  url: `/posts/${post.slug}/`
}));
 
export function GET() {
  return new Response(JSON.stringify(json));
}

Теперь, в процессе сборки сайта, дополнительно будет генерироваться Endpoint в формате JSON с данными из всех постов сайта, размещённых в формате Markdown.

При создании Endpoints в Astro расширение js отбрасывается, поэтому результирующий файл получит наименование posts.json и, на примере этого сайта, будет доступен по адресу https://geshov.ru/posts.json.

Полученный JSON с данными постов сайта можно использовать для реализации поиска по сайту при помощи библиотеки List.js:

terminal
npm i list.js
src/pages/search.astro
<div id="posts">
  <input class="search" placeholder="Поиск" />
  <ul class="list"></ul>
</div>
 
<script>
  import List from 'list.js';
  fetch('/posts.json')
    .then((response) => response.json())
    .then((json) => {
      const options = {
        valueNames: ['date', 'title', 'description', 'body'],
        searchColumns: ['title', 'description', 'body'],
        item: (item) => `<li>
          <a href="${item.url}" class="title">${item.title}</a>
          <div class="date">${item.date}</div>
          <div class="description">${item.description}</div>
        </li>`,
        page: 10
      };
      const list = new List('posts', options, json);
    });
</script>