Thêm tính năng draft cho GatsbyJs

Gatsby hiện tại có lẽ là một trong những Static site generator phổ biến nhất. Khoảng thời gian cuối năm ngoái - đầu năm nay, mình cũng thực hiện chuyển đổi toàn bộ blog của mình từ Jekyll sang Gatsby. Định viết một bài cập nhật như mọi khi nhưng vì 🦠 covid-19 làm đảo lộn nhịp sống nên rồi cũng bỏ đấy.

Rồi mình nhận ra là mình có quá nhiều thứ đang viết dở dang, và đôi khi mình nhẫm lần giữa một đống bài viết nháp và bài viết đã hoàn thành. Vậy nên mình quyết định thêm tính năng draft cho blog. Ưu điểm của Gatsby là có rất nhiều plugin hỗ trợ, và chắc cũng sẽ có plugin cho việc này. Tuy nhiên nó cũng chỉ là một tính năng đơn giản nên tự tay thêm cũng sẽ nhanh thôi và dễ custom theo ý mình hơn.

Thêm trường draft vào frontmatter của các markdown node

Để đánh dấu các bài viết nháp, mình thêm một field draft vào frontmatter của từng markdown node bằng cách sử dụng createSchemaCustomization API (được thêm vào Gatsby từ bản 2.12).

gatsby-node.js

exports.createSchemaCustomization = ({ actions, schema }) => {
  const { createTypes, createFieldExtension } = actions
  // Tạo field extension defaultFalse có tác dụng áp giá trị mặc định là false cho field có kiểu Boolean
  createFieldExtension({
    name: "defaultFalse",
    extend() {
      return {
        resolve(source, args, context, info) {
          if (source[info.fieldName] == null) return false
          return source[info.fieldName]
        },
      }
    },
  })
  // Tạo field 'draft' cho frontmatter của các MarkdownRemark Nodes
  createTypes(`
    type MarkdownRemark implements Node {
      frontmatter: Frontmatter
    }
    type Frontmatter {
      draft: Boolean @defaultFalse
    }
  `)
}

Ok vậy là xong 👌. Từ nay những bài viết nào là draft thì chỉ cần thêm draft = true ở frontmatter, lúc nào publish thì chuyển thành false hoặc xóa đi.

Làm những gì bạn thích với các bài viết nháp

Sau khi đã có field draft ở frontmatter của các markdown node thì việc sử dụng nó như thế nào là hoàn toàn tùy ở ý bạn thôi. 😂

Chẳng hạn nếu mình muốn các trang bài viết nháp chỉ được tạo ở môi trường development:

gatsby-node.js

/* Trong createPages API */
// Code query graphql, blah blah ở đây
const allPosts = result.data.allMarkdownRemark.nodes
const posts = (process.env.NODE_ENV.trim() === "development") ? 
              allPosts : 
              allPosts.filter(post => post.frontmatter.draft === false)
posts.forEach((post, index) {
  // Code tạo page ở đây
}

Tiếp theo, trong component hiển thị danh sách bài viết, mình có thể phân biệt các bài viết nháp và bài viết đã hoàn thành bằng cách sử dụng filter khi query graphql:

query {
  publishedPosts: allMarkdownRemark(filter: {frontmatter: {draft: {eq: false}}}) {
    edges {
      node
    }
  }
  draftPosts: allMarkdownRemark(filter: {frontmatter: {draft: {eq: true}}}) {
    edges {
      node
    }
  }
}

hoặc cứ query tất cả trước rồi filter bằng javascript:

const result = await graphql(`
    {
      allMarkdownRemark {
        nodes
      }
    }
  `)
const allPosts = result.data.allMarkdownRemark.nodes
const publishedPosts = allPosts.filter(p => p.frontmatter.draft=false)
const draftPosts = allPosts.filter(p => p.frontmatter.draft=true)

Rồi viết code để hiển thị chúng theo ý mình muốn (ẩn các bài viết nháp đi, đánh dấu chúng trên danh sách bài viết, hiển thị danh sách các bài draft riêng biệt,...). 👋