<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Kazane Shimizu</title>
    <description>The latest articles on DEV Community by Kazane Shimizu (@zane_jp).</description>
    <link>https://dev.to/zane_jp</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F313402%2F3b9636a8-ca57-4670-9b96-0de73f9c42a8.jpg</url>
      <title>DEV Community: Kazane Shimizu</title>
      <link>https://dev.to/zane_jp</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/zane_jp"/>
    <language>en</language>
    <item>
      <title>Can I create another WordPress that satisfies humanity?</title>
      <dc:creator>Kazane Shimizu</dc:creator>
      <pubDate>Mon, 27 Nov 2023 17:45:00 +0000</pubDate>
      <link>https://dev.to/collections/can-i-create-another-wordpress-that-satisfies-humanity-35le</link>
      <guid>https://dev.to/collections/can-i-create-another-wordpress-that-satisfies-humanity-35le</guid>
      <description>&lt;p&gt;With a strong presence in the industry, WordPress is employed by 40% of all websites. Even two decades since its inception, it continues to be the preferred platform for crafting blogs and websites. However, viewed from an engineer's perspective, the system poses numerous challenges.&lt;/p&gt;

&lt;p&gt;I'm currently offering an open-source Headless CMS named &lt;a href="https://collections.dev" rel="noopener noreferrer"&gt;Collections&lt;/a&gt;, a project I've been passionately developing over the past year. My motivation stems from a commitment to redefine the CMS landscape, addressing the issues I encountered with WordPress. Below is a summary of the development journey over the past year.&lt;/p&gt;

&lt;h1&gt;
  
  
  The Darkness of Black Magic
&lt;/h1&gt;

&lt;p&gt;WordPress is a meticulously designed system, offering the convenience of updating the software from the admin panel to ensure users always have access to the latest version. It boasts extensive customization options and can be further enhanced through external plugins.&lt;/p&gt;

&lt;p&gt;Yet, contamination from haphazardly added plugins and what some refer to as &lt;strong&gt;Black Magic&lt;/strong&gt; code has hindered updates and exposed the system to vulnerabilities.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqbaku2ycbr7ag1n7nmrp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqbaku2ycbr7ag1n7nmrp.png" alt="black-magic"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Indeed, the responsibility for this issue lies squarely with &lt;strong&gt;certain users&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Nevertheless, it's possible that this wasn't driven by malicious intent but rather a pursuit of a more advanced specification. In this context, extending beyond a user-specific problem, it suggests that humanity might no longer find satisfaction in the default WordPress.&lt;/p&gt;

&lt;h1&gt;
  
  
  Possibilities felt for headless
&lt;/h1&gt;

&lt;p&gt;Headless is an architecture that separates the front end from the back end.&lt;/p&gt;

&lt;p&gt;Traditional CMSs integrate both the front end and back end. By separating them, the front end can be tailored to diverse user segments, offering simplicity for light users and high customizability for advanced users. This system's separation naturally enhances maintainability.&lt;/p&gt;

&lt;p&gt;Although adopting a headless approach may initially appear to exclude light users, we are confident that, in the long run, it's a system capable of satisfying &lt;strong&gt;a broad range of users&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Headless is beginning in other industries
&lt;/h2&gt;

&lt;p&gt;The headless trend is already underway in other industries.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://tiptap.dev/editor" rel="noopener noreferrer"&gt;Tiptap&lt;/a&gt;, a tool that's becoming more prevalent in technical articles, serves as a specialized backend editor. Naturally, it doesn't operate as a standalone editor.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/Leecason" rel="noopener noreferrer"&gt;
        Leecason
      &lt;/a&gt; / &lt;a href="https://github.com/Leecason/element-tiptap" rel="noopener noreferrer"&gt;
        element-tiptap
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      🌸A modern WYSIWYG rich-text editor using tiptap and Element UI for Vue3 (1.0 for Vue2)
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;p&gt;&lt;a href="https://github.com/Leecason/element-tiptap" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2FLeecason%2Felement-tiptap%2Fdemos%2Fassets%2Flogo_for_github.png%3Fraw%3Dtrue" alt="ElTiptap logo"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;
  &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/a6e4634e76154d5ebc91a3a9e5b43093fdf092831579774bcb76bc894cfab276/68747470733a2f2f696d672e736869656c64732e696f2f6e706d2f762f656c656d656e742d746970746170"&gt;&lt;img alt="npm" src="https://camo.githubusercontent.com/a6e4634e76154d5ebc91a3a9e5b43093fdf092831579774bcb76bc894cfab276/68747470733a2f2f696d672e736869656c64732e696f2f6e706d2f762f656c656d656e742d746970746170"&gt;&lt;/a&gt;
  &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/897152240719682ed079fd992f574a6177e13f2710c816dbddc95023f7e26d25/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f72656c656173652d646174652f4c65656361736f6e2f656c656d656e742d746970746170"&gt;&lt;img alt="GitHub Release Date" src="https://camo.githubusercontent.com/897152240719682ed079fd992f574a6177e13f2710c816dbddc95023f7e26d25/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f72656c656173652d646174652f4c65656361736f6e2f656c656d656e742d746970746170"&gt;&lt;/a&gt;
  &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/b05cdca906cbca4af29d52d6b24a9f449e551dd67b53a61a5c78becd134762e7/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f7675652d253545332e302e302d767565"&gt;&lt;img alt="npm peer dependency version" src="https://camo.githubusercontent.com/b05cdca906cbca4af29d52d6b24a9f449e551dd67b53a61a5c78becd134762e7/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f7675652d253545332e302e302d767565"&gt;&lt;/a&gt;
  &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/251b82ec02847188c7f2f024d0a6752bb8e0422772baaace42e7a7dc3fd8c88a/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f2532302532302546302539462539332541362546302539462539412538302d73656d616e7469632d2d72656c656173652d6531303037392e737667"&gt;&lt;img alt="semantic-release" src="https://camo.githubusercontent.com/251b82ec02847188c7f2f024d0a6752bb8e0422772baaace42e7a7dc3fd8c88a/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f2532302532302546302539462539332541362546302539462539412538302d73656d616e7469632d2d72656c656173652d6531303037392e737667"&gt;&lt;/a&gt;
  &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/b09f94e582ddf99072b7f8e775d03f67b66a37f6e9f9b57235478c39bd69fb7e/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f4c65656361736f6e2f656c656d656e742d746970746170"&gt;&lt;img alt="GitHub" src="https://camo.githubusercontent.com/b09f94e582ddf99072b7f8e775d03f67b66a37f6e9f9b57235478c39bd69fb7e/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f4c65656361736f6e2f656c656d656e742d746970746170"&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Element Tiptap Editor&lt;/h3&gt;
&lt;/div&gt;

&lt;p&gt;A WYSIWYG rich-text editor using &lt;a href="https://github.com/ueberdosis/tiptap" rel="noopener noreferrer"&gt;tiptap2&lt;/a&gt; and &lt;a href="https://github.com/element-plus/element-plus" rel="noopener noreferrer"&gt;Element Plus&lt;/a&gt; for Vue3&lt;/p&gt;

&lt;p&gt;that's easy to use, friendly to developers, fully extensible and clean in design.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;🧊 Legacy&lt;/h2&gt;
&lt;/div&gt;

&lt;p&gt;&lt;a href="https://github.com/Leecason/element-tiptap/tree/master" rel="noopener noreferrer"&gt;Element Tiptap 1.0&lt;/a&gt;&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;📔 Languages&lt;/h2&gt;
&lt;/div&gt;

&lt;p&gt;English | &lt;a href="https://github.com/Leecason/element-tiptap./README_ZH.md" rel="noopener noreferrer"&gt;简体中文&lt;/a&gt;&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;🎄 Demo&lt;/h2&gt;

&lt;/div&gt;

&lt;p&gt;👉&lt;a href="https://leecason.github.io/element-tiptap" rel="nofollow noopener noreferrer"&gt;https://leecason.github.io/element-tiptap&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;👾&lt;a href="https://codesandbox.io/s/element-tiptap-bwlnj" rel="nofollow noopener noreferrer"&gt;Code Sandbox&lt;/a&gt;&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;✨ Features&lt;/h2&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;🎨Use &lt;a href="https://github.com/element-plus/element-plus" rel="noopener noreferrer"&gt;element-plus&lt;/a&gt; components&lt;/li&gt;
&lt;li&gt;💅Many out of box &lt;a href="https://github.com/Leecason/element-tiptap#extensions" rel="noopener noreferrer"&gt;extensions&lt;/a&gt; (welcome to submit an issue for feature request👏)&lt;/li&gt;
&lt;li&gt;🔖Markdown support&lt;/li&gt;
&lt;li&gt;📘TypeScript support&lt;/li&gt;
&lt;li&gt;🌐I18n support(&lt;code&gt;en&lt;/code&gt;, &lt;code&gt;zh&lt;/code&gt;, &lt;code&gt;pl&lt;/code&gt;, &lt;code&gt;ru&lt;/code&gt;, &lt;code&gt;de&lt;/code&gt;, &lt;code&gt;ko&lt;/code&gt;, &lt;code&gt;es&lt;/code&gt;, &lt;code&gt;zh_tw&lt;/code&gt;, &lt;code&gt;fr&lt;/code&gt;, &lt;code&gt;pt_br&lt;/code&gt;, &lt;code&gt;nl&lt;/code&gt;, &lt;code&gt;he&lt;/code&gt;). welcome to contribute more languages&lt;/li&gt;
&lt;li&gt;🎈Events you might use: &lt;code&gt;create&lt;/code&gt;, &lt;code&gt;transaction&lt;/code&gt;, &lt;code&gt;focus&lt;/code&gt;, &lt;code&gt;blur&lt;/code&gt;, &lt;code&gt;destroy&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;🍀Fully extensible, you can customize editor extension and its menu button view&lt;/li&gt;
&lt;li&gt;💻Also can control the behavior of the editor directly, customize the editor for yourself.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;📦 Installation&lt;/h2&gt;

&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;NPM&lt;/h3&gt;

&lt;/div&gt;

&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;yarn add element-tiptap&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Or&lt;/p&gt;

&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;npm install --save element-tiptap&lt;/pre&gt;

&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h4 class="heading-element"&gt;Install plugin&lt;/h4&gt;

&lt;/div&gt;

&lt;div class="highlight highlight-source-js notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-k"&gt;import&lt;/span&gt;&lt;/pre&gt;…
&lt;/div&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/Leecason/element-tiptap" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;



&lt;p&gt;By integrating it with the front end or developing a custom editor, Tiptap becomes adaptable to various user groups.&lt;/p&gt;

&lt;p&gt;Users find the flexibility to switch the UI based on their proficiency level appealing. For Tiptap, separating the front end, with its numerous issues and requests, enables a concentrated focus on back-end feature development.&lt;/p&gt;

&lt;p&gt;In the end, integrated software tends to &lt;strong&gt;diverge from the preferences of power users&lt;/strong&gt;. Therefore, adopting a headless approach becomes an inevitable trend.&lt;/p&gt;

&lt;h1&gt;
  
  
  Collections was born!
&lt;/h1&gt;

&lt;p&gt;Taking these considerations into account, I personally created an open-source headless CMS called Collections!&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
      &lt;div class="c-embed__cover"&gt;
        &lt;a href="https://collections.dev/" class="c-link s:max-w-50 align-middle" rel="noopener noreferrer"&gt;
          &lt;img alt="" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.collections.dev%2Fog-image.png" height="auto" class="m-0"&gt;
        &lt;/a&gt;
      &lt;/div&gt;
    &lt;div class="c-embed__body"&gt;
      &lt;h2 class="fs-xl lh-tight"&gt;
        &lt;a href="https://collections.dev/" rel="noopener noreferrer" class="c-link"&gt;
          Index – Collections
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;p class="truncate-at-3"&gt;
          A headless CMS that transforms your WordPress into an API. No need to copy and paste old posts anymore.
        &lt;/p&gt;
      &lt;div class="color-secondary fs-s flex items-center"&gt;
          &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.collections.dev%2Ffavicon.svg"&gt;
        collections.dev
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Here is a brief introduction to the features.&lt;/p&gt;

&lt;h2&gt;
  
  
  Define the data model
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fol50nwcqq85mc8ozz51h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fol50nwcqq85mc8ozz51h.png" alt="data-model"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Fields can be added, ranging from text and markdown to images, etc., and the data model is established. Upon completion of the registration, the database schema is dynamically generated.&lt;/p&gt;

&lt;h2&gt;
  
  
  Register your content
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe0yafh8itx737ous3k2w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe0yafh8itx737ous3k2w.png" alt="created-content"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, register content according to the data model.&lt;/p&gt;

&lt;h2&gt;
  
  
  Call as API
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnnaudbibwfa43xgs2442.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnnaudbibwfa43xgs2442.png" alt="api"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The endpoint is now set up!🥳&lt;br&gt;
You can retrieve the registered content by making an API call from the front end or a smartphone app.&lt;/p&gt;
&lt;h2&gt;
  
  
  Support for migration from WordPress
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwunu1h7kti0f627zj601.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwunu1h7kti0f627zj601.png" alt="wordpress"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Easily import your exported article data from WordPress by simply dragging and dropping. Say goodbye to the tedious task of copying and pasting old articles to get started!&lt;/p&gt;
&lt;h1&gt;
  
  
  Three Commitments
&lt;/h1&gt;
&lt;h2&gt;
  
  
  Must be open source
&lt;/h2&gt;

&lt;p&gt;Effective collaboration with the front end is crucial for a headless CMS. Consequently, having open code becomes essential as a shared language for comprehending the interface and exchanging feature requests.&lt;/p&gt;

&lt;p&gt;In contrast to Tiptap, transitioning to a headless CMS incurs a relatively high cost. This is due to the fact that, unlike editors which are easily replaceable tools, CMSs entail the complexity of data migration.&lt;/p&gt;

&lt;p&gt;Moreover, the absence of normalized data in WordPress poses a migration challenge. To address this directly, Collections has crafted a migration tool as a plugin, which is also open source and available to the community.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/collectionscms/plugin-wp-importer" rel="noopener noreferrer"&gt;https://github.com/collectionscms/plugin-wp-importer&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Currently, we only have article data, but with the collaboration of developers worldwide, we aim to develop a tool that enables the transfer of all types of data!&lt;/p&gt;
&lt;h2&gt;
  
  
  Developer First
&lt;/h2&gt;

&lt;p&gt;Collections is designed with a strong emphasis on developers. It can be deployed in any environment, offers ease of use, and comes with comprehensive documentation. A supportive developer community is also accessible on &lt;a href="https://discord.gg/a6FYDkV3Vk" rel="noopener noreferrer"&gt;Discord&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In addition, the UI provides&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;😻 Markdown with GFM&lt;/li&gt;
&lt;li&gt;🌒 Dark mode&lt;/li&gt;
&lt;li&gt;📱 Responsive&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We've got a complete set of &lt;strong&gt;essential tools for engineers to do their writing&lt;/strong&gt;! 😎&lt;/p&gt;
&lt;h2&gt;
  
  
  Can be introduced with a one-liner
&lt;/h2&gt;

&lt;p&gt;No matter how fantastic a product is, if it's challenging to implement, people won't use it. But, of course, there's no need to worry!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx create-collections-app my-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Installing the software is a breeze with just a few commands! &lt;/p&gt;

&lt;p&gt;All that's left is to respond to the installer's questions to finalize the setup process.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Select your database client
? Which one? SQLite is the simplest. (Use arrow keys)
❯ SQLite
  MySQL / MariaDB
  PostgreSQL
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h1&gt;
  
  
  Collections Architecture
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3wujdcgbfwhqcyjwbg13.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3wujdcgbfwhqcyjwbg13.png" alt="architecture"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Backend UI
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh048j6bbl5e2anx00lqx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh048j6bbl5e2anx00lqx.png" alt="react+mui+ant"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The backend UI is a combination of &lt;a href="https://ja.react.dev/" rel="noopener noreferrer"&gt;React&lt;/a&gt; + &lt;a href="https://mui.com/" rel="noopener noreferrer"&gt;MUI&lt;/a&gt; + &lt;a href="https://ant.design/" rel="noopener noreferrer"&gt;Ant Design&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For a headless CMS, all you need is an administration screen, so there's no requirement for an overly elaborate UI. That's why we opted for MUI due to its refined components. We chose Ant Design solely for its icons, as we personally appreciate its aesthetic.&lt;/p&gt;
&lt;h2&gt;
  
  
  API
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhldxovtku5ev62m95hfn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhldxovtku5ev62m95hfn.png" alt="nodejs+express"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The API is a powerful blend of &lt;a href="https://nodejs.org/en" rel="noopener noreferrer"&gt;Node.js&lt;/a&gt; + &lt;a href="https://expressjs.com/" rel="noopener noreferrer"&gt;Express&lt;/a&gt;. When evaluating backend combinations, a crucial aspect is that it &lt;strong&gt;can be deployed in any environment&lt;/strong&gt;. Hence, vendor-locked options like Next.js were excluded from the candidate list.&lt;/p&gt;
&lt;h2&gt;
  
  
  Database
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7a0nsy8t2p3hmef7rrwg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7a0nsy8t2p3hmef7rrwg.png" alt="database"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Opting for RDB and supporting multiple vendors, we chose RDB for its versatility in deployment environments, ease of migration from WordPress, and the preference of many engineers who find RDB more user-friendly than NoSQL.&lt;/p&gt;

&lt;p&gt;Given the dynamic nature of the schema, we employ &lt;a href="https://knexjs.org/" rel="noopener noreferrer"&gt;Knex&lt;/a&gt;, a query builder, for database access.&lt;/p&gt;
&lt;h2&gt;
  
  
  Image
&lt;/h2&gt;

&lt;p&gt;Currently, S3 is supported. Looking ahead, we plan to expand support to include Google and Azure storage, along with CDN delivery. There's quite a bit of work ahead... 😅&lt;/p&gt;
&lt;h2&gt;
  
  
  Hosting
&lt;/h2&gt;

&lt;p&gt;As evident from the architecture, it comprises a fundamental structure. Consequently, it is adaptable to various environments, whether within or outside the network!&lt;/p&gt;

&lt;p&gt;For your convenience, we've provided instructions for deploying on Lightsail, which is essential for cost-effective usage. Feel free to give it a try!&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
      &lt;div class="c-embed__cover"&gt;
        &lt;a href="https://collections.dev/docs/deployments/lightsail" class="c-link s:max-w-50 align-middle" rel="noopener noreferrer"&gt;
          &lt;img alt="" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.collections.dev%2Fog-image.png" height="auto" class="m-0"&gt;
        &lt;/a&gt;
      &lt;/div&gt;
    &lt;div class="c-embed__body"&gt;
      &lt;h2 class="fs-xl lh-tight"&gt;
        &lt;a href="https://collections.dev/docs/deployments/lightsail" rel="noopener noreferrer" class="c-link"&gt;
          Amazon Lightsail – Collections
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;p class="truncate-at-3"&gt;
          A headless CMS that transforms your WordPress into an API. No need to copy and paste old posts anymore.
        &lt;/p&gt;
      &lt;div class="color-secondary fs-s flex items-center"&gt;
          &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.collections.dev%2Ffavicon.svg"&gt;
        collections.dev
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;



&lt;h1&gt;
  
  
  Future Development
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Launch of cloud version
&lt;/h2&gt;

&lt;p&gt;In 2024, we're gearing up to introduce a cloud version. While it will come at a modest cost, it's perfect for those seeking swift implementation without the hassle of software updates. Stay tuned for its release, and we hope you're as excited about it as we are!&lt;/p&gt;

&lt;h2&gt;
  
  
  Build out migration tools from WordPress
&lt;/h2&gt;

&lt;p&gt;Migrating 40% of our websites is a formidable undertaking, but we're collaborating with developers worldwide to tackle the challenge! We're counting on the support of developers from across the globe to help us overcome this hurdle!&lt;/p&gt;

&lt;h2&gt;
  
  
  Build an ecosystem with front-end
&lt;/h2&gt;

&lt;p&gt;Observing examples like Tiptap and CMS, I anticipate a continued trend of increasing separation between the front and back end, leading to a more loosely coupled relationship between software components.&lt;/p&gt;

&lt;p&gt;If you're a developer interested in creating applications in conjunction with Collections in the future, we'd love to collaborate with you! Let's work together to build a robust ecosystem 💪&lt;/p&gt;

&lt;h1&gt;
  
  
  Feedback and Request
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Want to try Collections?
&lt;/h2&gt;

&lt;p&gt;Collections is open source, available for free use by anyone. If you need help with implementation, feel free to join us on Discord 👇&lt;/p&gt;

&lt;p&gt;&lt;a href="https://discord.gg/a6FYDkV3Vk" rel="noopener noreferrer"&gt;https://discord.gg/a6FYDkV3Vk&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Finally
&lt;/h1&gt;

&lt;p&gt;A CMS is a product whose value is derived from its enduring appeal. The enduring popularity of WordPress, even after two decades, attests to this. I firmly believe that this is the very realm where engineers should leverage their strengths and dedicate their careers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Resources&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://github.com/collectionscms/collections" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; — Open source repository&lt;br&gt;
&lt;a href="https://demo.collections.dev/admin/" rel="noopener noreferrer"&gt;Demo&lt;/a&gt; — Demo for those who want to get a taste of Collections functionality&lt;br&gt;
&lt;a href="https://collections.dev" rel="noopener noreferrer"&gt;Document&lt;/a&gt; — General Information, API Information&lt;br&gt;
&lt;a href="https://discord.gg/a6FYDkV3Vk" rel="noopener noreferrer"&gt;Discord&lt;/a&gt; — Developer Community&lt;br&gt;
&lt;a href="https://twitter.com/collectionscms" rel="noopener noreferrer"&gt;X&lt;/a&gt; — Latest Product Information&lt;/p&gt;

&lt;p&gt;Your appreciation in the form of liking our articles or simply giving us a star on GitHub would be truly uplifting! &lt;/p&gt;

&lt;p&gt;Thank you for your support!! 🙌&lt;/p&gt;

</description>
      <category>react</category>
      <category>wordpress</category>
      <category>opensource</category>
      <category>jamstack</category>
    </item>
    <item>
      <title>Over 10 Years of Indie Hacking, I can stay active at 40+</title>
      <dc:creator>Kazane Shimizu</dc:creator>
      <pubDate>Mon, 06 Nov 2023 12:30:00 +0000</pubDate>
      <link>https://dev.to/collections/over-10-years-of-indie-hacking-i-can-stay-active-at-40-2aog</link>
      <guid>https://dev.to/collections/over-10-years-of-indie-hacking-i-can-stay-active-at-40-2aog</guid>
      <description>&lt;p&gt;Hello, I turned 42 this year and I'm an active freelance engineer.&lt;/p&gt;

&lt;p&gt;I continue to write code, thanks to the experience I've gained over more than a decade of indie hacking and my product contributions.&lt;/p&gt;

&lt;p&gt;I've created about 10 products so far, and some have even received funding. However, solo product development remains challenging. When I first started, I often abandoned projects halfway through. But now, with my own set of rules in place, I consistently see projects through to completion.&lt;/p&gt;

&lt;p&gt;In this edition, I've distilled these rules into 10 methods!&lt;/p&gt;

&lt;h2&gt;
  
  
  What are you developing?
&lt;/h2&gt;

&lt;p&gt;I'm currently working on a headless CMS called 'Collections'.&lt;/p&gt;

&lt;p&gt;Collections allows you to API your WordPress articles with a simple drag-and-drop feature. It's built entirely in TypeScript, runs on an RDBMS, and is open-source 💚.&lt;/p&gt;

&lt;p&gt;While there are various headless CMS options available, Collections stands out by offering a seamless transition from WordPress.&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
      &lt;div class="c-embed__cover"&gt;
        &lt;a href="https://collections.dev/" class="c-link s:max-w-50 align-middle" rel="noopener noreferrer"&gt;
          &lt;img alt="" src="https://res.cloudinary.com/practicaldev/image/fetch/s--MW0IPkYL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.collections.dev/og-image.png" height="420" class="m-0" width="800"&gt;
        &lt;/a&gt;
      &lt;/div&gt;
    &lt;div class="c-embed__body"&gt;
      &lt;h2 class="fs-xl lh-tight"&gt;
        &lt;a href="https://collections.dev/" rel="noopener noreferrer" class="c-link"&gt;
          Index – Collections
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;p class="truncate-at-3"&gt;
          A headless CMS that transforms your WordPress into an API. No need to copy and paste old posts anymore.
        &lt;/p&gt;
      &lt;div class="color-secondary fs-s flex items-center"&gt;
          &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://res.cloudinary.com/practicaldev/image/fetch/s--A9l1t8zH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.collections.dev/favicon.svg" width="800" height="800"&gt;
        collections.dev
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  Benefits of Indie Hacking
&lt;/h2&gt;

&lt;p&gt;Numerous articles have extensively covered this topic, so I won't go into detail here. Just like strength training, consistency yields significant benefits over an extended period, gradually accumulating.&lt;/p&gt;

&lt;p&gt;On the flip side, the downsides are...well, there really aren't many! If I were to mention one, it might be the time commitment. However, you could also view it as an investment in experience. As someone who has worked in a brokerage firm, it's challenging to find a more cost-effective investment than this 😆.&lt;/p&gt;

&lt;h2&gt;
  
  
  10 Methods
&lt;/h2&gt;

&lt;p&gt;Let's get straight to it. Here are the top 10 essential factors for reaching the finish line&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Recognize developers' strengths.&lt;/li&gt;
&lt;li&gt;Clearly define the problems to be addressed.&lt;/li&gt;
&lt;li&gt;Avoid setting daily quotas.&lt;/li&gt;
&lt;li&gt;Establish development deadlines.&lt;/li&gt;
&lt;li&gt;Research competitors and emulate their methods.&lt;/li&gt;
&lt;li&gt;Find a reliable partner.&lt;/li&gt;
&lt;li&gt;Be cautious with new tech.&lt;/li&gt;
&lt;li&gt;Focus only on critical issues.&lt;/li&gt;
&lt;li&gt;Don't expect a spectacular launch.&lt;/li&gt;
&lt;li&gt;Make it public.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  📌 1. Recognize developers' strengths
&lt;/h2&gt;

&lt;p&gt;Even in an era where non-engineers can create products without coding, engineers have the advantage of &lt;strong&gt;sustaining product development long-term&lt;/strong&gt; without incurring production costs.&lt;/p&gt;

&lt;p&gt;Most services that people desire have already been developed by predecessors, making it nearly impossible to create a groundbreaking product from scratch.&lt;/p&gt;

&lt;p&gt;Even well-funded companies always operate within budget constraints, leading to ongoing debates on whether to "pull the plug" or "invest more".&lt;/p&gt;

&lt;p&gt;On the other hand, individuals can easily keep creating and improving their work, which is a significant advantage.&lt;/p&gt;

&lt;p&gt;In my opinion, it's wiser to focus on developing products that can provide long-term value rather than blindly following industry trends.&lt;/p&gt;

&lt;h2&gt;
  
  
  📌 2. Clearly define the problems to be addressed
&lt;/h2&gt;

&lt;p&gt;Once this is determined, the product becomes easier to create. For the most part&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For yourself&lt;/li&gt;
&lt;li&gt;For someone close to you&lt;/li&gt;
&lt;li&gt;For someone you don't know who has a challenge&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With the third option, the potential user base is larger, making it easier to scale the product. However, it also becomes more challenging to identify common issues. During your early journey as an indie hacker, it's advisable to create a product for yourself or someone in your close circle.&lt;/p&gt;

&lt;p&gt;You've likely heard the advice to "share your product as soon as you create it and gather feedback" at least once. While this is the right approach, receiving unintended feedback expressing that they "don't need" or "won't use" the product can be disheartening, potentially demotivating you and causing you to drop out of the race.&lt;/p&gt;

&lt;p&gt;Therefore, in the early stages, the main goal should be to &lt;strong&gt;find satisfaction in what you've created&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  📌 3. Avoid setting daily quotas
&lt;/h2&gt;

&lt;p&gt;One strategy I've experimented with, but found to be ineffective, involves setting personal quotas, like 'write code every day' or 'complete it this week.' To be honest, it's quite simple to come up with various excuses and rationalize why I didn't take certain actions. However, when these self-imposed goals aren't achieved, it is easy to lose motivation.&lt;/p&gt;

&lt;p&gt;So, it is much more important to keep the motivation for 'making it' high. The key to completion is to &lt;strong&gt;enjoy the process of creation itself&lt;/strong&gt; 😊.&lt;/p&gt;

&lt;h2&gt;
  
  
  📌 4. Establish development deadlines
&lt;/h2&gt;

&lt;p&gt;In contrast to not setting quotas as mentioned earlier, consider setting deadlines for development. The most significant adversaries for indie hacking are 'boredom' and 'giving up'.&lt;/p&gt;

&lt;p&gt;No matter how high your motivation is, one of these two outcomes is bound to occur. While it's a different story for services that require extensive effort to create, when you believe that you can create something great with time, it may not always be the case. As you start noticing flaws, the fear of continuously releasing, making fixes, or worst of all, starting over becomes overwhelming. As you tinker around, you might get bored and end up not releasing at all.&lt;/p&gt;

&lt;p&gt;So, let's release products before these pressures build up.&lt;/p&gt;

&lt;p&gt;The goal should be to release within &lt;strong&gt;six months&lt;/strong&gt;, if it takes &lt;strong&gt;more than a year, it should be considered too late&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  📌 5. Research competitors and emulate their methods
&lt;/h2&gt;

&lt;p&gt;I believe you can appreciate the importance of maintaining motivation in indie hacking. However, on the flip side, what are the moments that most often diminish motivation?&lt;/p&gt;

&lt;p&gt;One such moment is when you initially think your idea is unique and then discover a similar service already exists 😱.&lt;/p&gt;

&lt;p&gt;The shock of this discovery can make you feel like all the efforts you've put in so far have been instantly wiped away! Unfortunately, this often stems from insufficient preliminary research...&lt;/p&gt;

&lt;p&gt;When a new idea strikes, and you're eager to 'develop it right away,' it's a good idea to temper that excitement a bit and start by searching on GitHub for similar software. Just because you find something similar doesn't mean there's no value in creating something new. In fact, by addressing the shortcomings of existing competitors, you can create an attractive product.&lt;/p&gt;

&lt;p&gt;Additionally, thoroughly &lt;code&gt;emulate the common elements&lt;/code&gt;. By identifying 2 or 3 similar projects in terms of language and structure, you can streamline concerns during implementation and prerequisite knowledge.&lt;/p&gt;

&lt;p&gt;And &lt;strong&gt;thoroughly emulate&lt;/strong&gt; the common elements. By finding 2 or 3 similar things in terms of language and structure, you can shortcut the points of concern during implementation and the prerequisite knowledge.&lt;/p&gt;

&lt;h2&gt;
  
  
  📌 6. Find a reliable partner
&lt;/h2&gt;

&lt;p&gt;When you're developing something on your own, you might start feeling like, "Do I need this service?" or become fearful of releasing it. That's why it's important to occasionally bounce your ideas off someone else.&lt;/p&gt;

&lt;p&gt;During these times, it's great to consult with someone who has experienced success with a product, but even fellow indie hackers work well! On the contrary, it's better to avoid seeking advice from friends or acquaintances who might find it easy to say no. Users might know what's good, but they won't fully understand until they've experienced it. Consequently, they might just point out glaring issues or say, "I won't use it" without diving into the details.&lt;/p&gt;

&lt;p&gt;Even if you don't have someone close by to consult with, indie hackers often share common traits and interests. So, reaching out to someone for advice is unlikely to lead to hard feelings.&lt;/p&gt;

&lt;h2&gt;
  
  
  📌 7. Be cautious with new tech
&lt;/h2&gt;

&lt;p&gt;One common challenge faced by developers is this:&lt;/p&gt;

&lt;p&gt;The better you are in a specific field, the stronger the temptation to introduce new technologies or tools becomes. However, this can increase the learning curve and slow your progress, unless these additions are truly essential for your project. It's best to steer clear of new technologies unless they are absolutely vital for the service you're creating. With many tasks on your plate, particularly as you approach the release, taking on non-essential technologies can complicate the process.&lt;/p&gt;

&lt;h2&gt;
  
  
  📌 8. Focus only on critical issues
&lt;/h2&gt;

&lt;p&gt;This is the second common pitfall that developers often encounter.&lt;/p&gt;

&lt;p&gt;In the world of indie hacking, you don't incur traditional costs. However, due to the nature of this profession, the desire to perfect specifications and resolve bugs can lead to unnecessary time consumption, resulting in endless delays when trying to release a product.&lt;/p&gt;

&lt;p&gt;In conventional software development, we typically use tools like JIRA to manage issue statuses, with labels such as &lt;code&gt;highest&lt;/code&gt;, &lt;code&gt;high&lt;/code&gt;, &lt;code&gt;medium&lt;/code&gt;, &lt;code&gt;low&lt;/code&gt;, and &lt;code&gt;lowest&lt;/code&gt;. In contrast, indie hacking simplifies this to just two choices: &lt;code&gt;critical&lt;/code&gt; or &lt;code&gt;non-critical&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Unless it's a &lt;code&gt;critical feature&lt;/code&gt; or a &lt;code&gt;critical bug&lt;/code&gt;, everything else should be added to the backlog.&lt;/p&gt;

&lt;h2&gt;
  
  
  📌 9. Don't expect a spectacular launch
&lt;/h2&gt;

&lt;p&gt;It's tough to put in a year of work only to have no one pay attention. And the creeping burnout can be intense... 😱&lt;/p&gt;

&lt;p&gt;Think of the launch as not the goal but merely the first milestone on a long roadmap. What's crucial is to keep improving it after the launch!&lt;/p&gt;

&lt;p&gt;Some people might be concerned that if they release their ideas incrementally, they could be copied. However, groundbreaking ideas don't come by often. The strategies that result in user growth are the truly innovative ideas. So, don't worry and keep sharing your ideas openly and freely.&lt;/p&gt;

&lt;h2&gt;
  
  
  📌 10. Make it public
&lt;/h2&gt;

&lt;p&gt;You don't need to keep it running indefinitely. Creating just one or two articles and leaving it at that is sufficient. It can serve as a record, and if you feel like you've reached a conclusion within yourself, it can also bring a sense of accomplishment.&lt;/p&gt;

&lt;p&gt;The important thing is to &lt;strong&gt;put it out into the world in a usable form!&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Finally
&lt;/h2&gt;

&lt;p&gt;Indie Hacking is enjoyable! It's like the foundation of our thirst for knowledge.&lt;br&gt;
Let's bring a new experience to someone else!&lt;/p&gt;

&lt;p&gt;Happy Coding!!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>learning</category>
      <category>career</category>
      <category>newbie</category>
    </item>
    <item>
      <title>Running Jest for multiple DB with GitHub Actions</title>
      <dc:creator>Kazane Shimizu</dc:creator>
      <pubDate>Mon, 30 Oct 2023 12:45:00 +0000</pubDate>
      <link>https://dev.to/collections/running-jest-for-multiple-db-with-github-actions-5186</link>
      <guid>https://dev.to/collections/running-jest-for-multiple-db-with-github-actions-5186</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;If you're working on an application that supports multiple databases, even minor adjustments can lead to issues related to SQL dialects or query builder specifications like Knex.&lt;/p&gt;

&lt;p&gt;The headless CMS I'm currently developing also offers users the option to choose their preferred database, so it's essential to ensure not only the logic but also the ability to perform CRUD operations correctly in each database when developing features.&lt;/p&gt;

&lt;p&gt;The same holds true for testing, which requires some clever strategies, such as connecting to multiple databases to verify a single scenario. However, by automating these processes through continuous integration (CI), you can reduce the likelihood of unexpected bugs.&lt;/p&gt;

&lt;p&gt;In this article, we'll demonstrate how to use GitHub Actions to execute Jest tests on multiple databases while creating a disposable environment.&lt;/p&gt;

&lt;p&gt;All the code is available on GitHub for your reference!&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/collectionscms" rel="noopener noreferrer"&gt;
        collectionscms
      &lt;/a&gt; / &lt;a href="https://github.com/collectionscms/collections" rel="noopener noreferrer"&gt;
        collections
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Collections is a headless CMS for Answer Engine Optimization (AEO).
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;⭐ What is Collections&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;Collections is a headless CMS for Answer Engine Optimization (AEO).&lt;/p&gt;
&lt;p&gt;Our mission is to deliver both human and AI friendly content as people are moving from &lt;code&gt;Google it&lt;/code&gt; to &lt;code&gt;Ask AI&lt;/code&gt;.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h4 class="heading-element"&gt;
&lt;a href="https://app.collectionsdemo.live/admin/" rel="nofollow noopener noreferrer"&gt;&lt;strong&gt;Try Live Demo&lt;/strong&gt;&lt;/a&gt;    &lt;a href="https://collections-nextjs-blog.vercel.app/" rel="nofollow noopener noreferrer"&gt;&lt;strong&gt;View Blog Demo&lt;/strong&gt;&lt;/a&gt;
&lt;/h4&gt;
&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Features&lt;/h3&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;🪄 AI supports SEO&lt;/li&gt;
&lt;li&gt;🌐 Multilingual content&lt;/li&gt;
&lt;li&gt;🖊 Notion-like editor&lt;/li&gt;
&lt;li&gt;🕘️ Article versioning&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;📚 Usage &amp;amp; Documentation&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;Extended documentation is available on our website.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://collections.dev" rel="nofollow noopener noreferrer"&gt;English&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://collections.dev/ja" rel="nofollow noopener noreferrer"&gt;日本語&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;🚀 Installation&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;How to start using Collections on localhost.&lt;/p&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;// clone
git clone git@github.com:collectionscms/collections.git
&lt;span class="pl-c1"&gt;cd&lt;/span&gt; collections

// env
cp .env.sample .env
vi .env - make it your environment

// install direnv
brew install direnv
vi &lt;span class="pl-k"&gt;~&lt;/span&gt;/.zshrc - add &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;`&lt;/span&gt;eval &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;$(&lt;/span&gt;direnv hook zsh&lt;span class="pl-pds"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;&lt;span class="pl-pds"&gt;`&lt;/span&gt;&lt;/span&gt;
&lt;span class="pl-c1"&gt;source&lt;/span&gt; &lt;span class="pl-k"&gt;~&lt;/span&gt;/.zshrc
direnv allow &lt;span class="pl-c1"&gt;.&lt;/span&gt;

// init
yarn install
yarn db:refresh
yarn dev&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Add the following lines to the &lt;code&gt;/etc/hosts&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;127.0.0.1   app.test.com
127.0.0.1   en.test.com
127.0.0.1   ja.test.com&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Open &lt;a href="http://app.test.com:4000/admin" rel="nofollow noopener noreferrer"&gt;http://app.test.com:4000/admin&lt;/a&gt; to view your…&lt;/p&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/collectionscms/collections" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  Environment
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Contents&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Test tool&lt;/td&gt;
&lt;td&gt;Jest&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DB&lt;/td&gt;
&lt;td&gt;MySQL, PostgreSQL, SQLite.... .etc. As many as you want!&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CI&lt;/td&gt;
&lt;td&gt;GitHub Actions&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Goal
&lt;/h2&gt;

&lt;p&gt;Don't repeat "MySQL is OK, PostgreSQL is an error 😱"&lt;/p&gt;

&lt;h2&gt;
  
  
  Test Flow
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Launch Docker&lt;/li&gt;
&lt;li&gt;Migration /seed in Jest setup&lt;/li&gt;
&lt;li&gt;Test execution on multiple DBs!&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Jest
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Configuration
&lt;/h3&gt;

&lt;p&gt;Begin by initializing the database for testing purposes. You can specify the setup code in the Jest configuration file's globalSetup section.&lt;/p&gt;

&lt;p&gt;For the finer details, please consult the repository, as we've excluded all but the most essential code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/** @type {import('ts-jest').JestConfigWithTsJest} */
export default {
  ...
  globalSetup: './test/setups/setup.ts',
  ...
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;a href="https://github.com/superfastcms/superfast/blob/main/jest.integrations.config.mjs" rel="noopener noreferrer"&gt;https://github.com/superfastcms/superfast/blob/main/jest.integrations.config.mjs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this stage, migrations are executed, and seed data is provided for each database to be tested. The key distinction is that these processes are repeated within an array known as testDatabases.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export default async (): Promise&amp;lt;void&amp;gt; =&amp;gt; {
  for (const testDatabase of testDatabases) {
    const database = knex(config.knexConfig[testDatabase]!);

    if (testDatabase === 'sqlite3') {
      writeFileSync('test.db', '');
    }

    await database.migrate.latest();
    await database.seed.run();
  }
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;a href="https://github.com/superfastcms/superfast/blob/main/test/setups/setup.ts" rel="noopener noreferrer"&gt;https://github.com/superfastcms/superfast/blob/main/test/setups/setup.ts&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next is the test code.&lt;/p&gt;
&lt;h2&gt;
  
  
  Test Codes
&lt;/h2&gt;

&lt;p&gt;While iterating with testDatabases as well, you can write the usual test cases 🙆&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;describe('Users', () =&amp;gt; {
  describe('Create', () =&amp;gt; {
    it.each(testDatabases)('%s - should create', async (database) =&amp;gt; {
      const connection = databases.get(database)!;

      const repository = new UsersRepository(tableName, { knex: connection });
      const result = await repository.create(user);

      expect(result).toBeTruthy();
    });
  });
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;An important point to remember is that afterAll discards all connections. Failing to do so can result in Jest continuing to run for several minutes without terminating the process, even after the test has passed.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const databases = new Map&amp;lt;string, Knex&amp;gt;();

beforeAll(async () =&amp;gt; {
  for (const database of testDatabases) {
    databases.set(database, knex(config.knexConfig[database]!));
  }
});

afterAll(async () =&amp;gt; {
  for (const [_, connection] of databases) {
    await connection.destroy();
  }
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;a href="https://github.com/superfastcms/superfast/blob/main/test/api/repositories/users.test.ts" rel="noopener noreferrer"&gt;https://github.com/superfastcms/superfast/blob/main/test/api/repositories/users.test.ts&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Test run
&lt;/h2&gt;

&lt;p&gt;With the setup now complete, let's get started!&lt;/p&gt;

&lt;p&gt;yarn test:int&lt;/p&gt;

&lt;p&gt;The test succeeds like this~~Yay!! 🙌&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn run v1.22.19
$ node --loader ts-node/esm --experimental-vm-modules node_modules/jest/bin/jest.js --config=jest.integrations.config.mjs
(node:2144) ExperimentalWarning: Custom ESM Loaders is an experimental feature and might change at any time
(Use `node --trace-warnings ...` to show where the warning was created)

Setup databases: [ 'sqlite3', 'mysql', 'maria', 'postgres' ]
🟢 Starting tests! 

(node:2144) ExperimentalWarning: VM Modules is an experimental feature and might change at any time
PASS test/api/repositories/projectSettings.test.ts (6.145 s)
PASS test/api/repositories/users.test.ts

-------------------------|---------|----------|---------|---------|------------------------------------------------------
File                     | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s                                    
-------------------------|---------|----------|---------|---------|------------------------------------------------------
All files                |   72.97 |    83.33 |      40 |   72.97 |                                                      
 src                     |     100 |      100 |     100 |     100 |                                                      
  env.ts                 |     100 |      100 |     100 |     100 |                                                      
 src/api/database        |   39.74 |      100 |       0 |   39.74 |                                                      
  connection.ts          |   39.74 |      100 |       0 |   39.74 | 32-78                                                
 src/api/repositories    |   54.77 |    82.35 |   41.66 |   54.77 |                                                      
  base.ts                |   87.91 |       70 |   54.54 |   87.91 | 31-35,77-78,85-86,89-90                              
  projectSettings.ts     |   72.22 |      100 |      50 |   72.22 | 13-17                                                
  users.ts               |   29.54 |      100 |   27.27 |   29.54 | 13-17,25-30,40-49,52-63,66-81,84-107,110-116,119-131 
 src/exceptions          |      68 |      100 |   33.33 |      68 |                                                      
  base.ts                |   66.66 |      100 |      50 |   66.66 | 12-17                                                
  invalidCredentials.ts  |   71.42 |      100 |       0 |   71.42 | 5-6                                                  
 src/exceptions/database |     100 |      100 |     100 |     100 |                                                      
  recordNotUnique.ts     |     100 |      100 |     100 |     100 |                                                                                                         
-------------------------|---------|----------|---------|---------|------------------------------------------------------

Test Suites: 2 passed, 2 total
Tests:       5 passed, 5 total
Snapshots:   0 total
Time:        8.422 s
Ran all test suites.

🏁 Tests complete!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;In this scenario, we're conducting tests across all databases (SQLite, MySQL, MariaDB, and PostgreSQL). However, running these tests each time can be time-consuming and resource-intensive in a CI environment. In such cases, it's advisable to pass specific tests as parameters, as demonstrated below, to streamline the process.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const allDatabases = ['sqlite3', 'mysql', 'maria', 'postgres'];
export const testDatabases = process.env.TEST_DB?.split(',').map((v) =&amp;gt; v.trim()) ?? allDatabases;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;In my headless CMS, I use only SQLite for all DBs except when I modify the logic.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TEST_DB=sqlite3 yarn test:int
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  GitHub Actions
&lt;/h2&gt;

&lt;p&gt;Having successfully executed the tests locally, you can now set them up as a GitHub Actions workflow. Automating the testing process helps minimize the chances of unexpected issues arising from minor adjustments or library updates made through Renovate.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;jobs:
  integration:
    name: ${{ matrix.database }}
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        database:
          - sqlite3
          - mysql
          - maria
          - postgres
    steps:
      - name: Checkout repository
        uses: actions/checkout@v3

      - name: Yarn install
        run: yarn install

      - name: Start Services
        if: matrix.database != 'sqlite3'
        run: docker compose -f test/docker-compose.yml up ${{ matrix.database }} -d --quiet-pull --wait

      - name: Run Tests
        run: TEST_DB=${{ matrix.database }} yarn test:int
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;a href="https://github.com/superfastcms/superfast/blob/main/.github/workflows/integration-full.yml" rel="noopener noreferrer"&gt;https://github.com/superfastcms/superfast/blob/main/.github/workflows/integration-full.yml&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In continuous integration (CI), the focus is on the database to be tested for each pull request. While there are various approaches, one method involves specifying paths to run any files that have been modified by a PR, especially if they relate to backend code or the test code itself.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pull_request:
  branches:
    - main
  paths:
    - src/api/**
    - test/api/**
    - package.json
    - .github/workflows/integration-full.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Alternatively, you can save time and CI resources by exclusively using SQLite, as it doesn't necessitate Docker startup!&lt;/p&gt;
&lt;h2&gt;
  
  
  Finally
&lt;/h2&gt;

&lt;p&gt;We are developing a headless CMS Collections that will turn WordPress articles into an API with drag and drop 🚀&lt;br&gt;
&lt;/p&gt;
&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
      &lt;div class="c-embed__cover"&gt;
        &lt;a href="https://collections.dev/" class="c-link s:max-w-50 align-middle" rel="noopener noreferrer"&gt;
          &lt;img alt="" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.collections.dev%2Fog-image.png" height="auto" class="m-0"&gt;
        &lt;/a&gt;
      &lt;/div&gt;
    &lt;div class="c-embed__body"&gt;
      &lt;h2 class="fs-xl lh-tight"&gt;
        &lt;a href="https://collections.dev/" rel="noopener noreferrer" class="c-link"&gt;
          Index – Collections
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;p class="truncate-at-3"&gt;
          A headless CMS that transforms your WordPress into an API. No need to copy and paste old posts anymore.
        &lt;/p&gt;
      &lt;div class="color-secondary fs-s flex items-center"&gt;
          &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.collections.dev%2Ffavicon.svg"&gt;
        collections.dev
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;



&lt;p&gt;Full TypeScript, works with RDB. And it is open source. Please try the &lt;a href="https://demo.collections.dev/admin/" rel="noopener noreferrer"&gt;Live Demo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Happy Coding!!&lt;/p&gt;

</description>
      <category>jest</category>
      <category>postgressql</category>
      <category>githubactions</category>
    </item>
    <item>
      <title>Next.js + Redoc to create a low-latency API references</title>
      <dc:creator>Kazane Shimizu</dc:creator>
      <pubDate>Sat, 28 Oct 2023 05:56:19 +0000</pubDate>
      <link>https://dev.to/collections/nextjs-redoc-to-create-a-low-latency-api-references-51fb</link>
      <guid>https://dev.to/collections/nextjs-redoc-to-create-a-low-latency-api-references-51fb</guid>
      <description>&lt;p&gt;I'm currently developing an open-source headless CMS called Collections. During this project, the need arose for a public API specification, and we decided to try out the combination of Next.js (Nextra) and Redoc, which turned out to be quite effective. We've documented the procedure for your reference.&lt;/p&gt;

&lt;p&gt;API specifications, like those using OpenAPI (Swagger), are typically created for each project, but sharing them with the public may not happen very often. Nonetheless, this is a domain where technology trends evolve relatively slowly, so it's worth keeping in mind!&lt;/p&gt;

&lt;h2&gt;
  
  
  Completed product
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--93TX2FI3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ejffocfwfx8mcmlt0ikr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--93TX2FI3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ejffocfwfx8mcmlt0ikr.png" alt="api" width="800" height="401"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The published page is &lt;a href="https://collections.dev/reference/api"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All code is available on GitHub for your reference.&lt;br&gt;
&lt;a href="https://github.com/collectionscms/collections/blob/main/docs/pages/reference/api.en.mdx"&gt;https://github.com/collectionscms/collections/blob/main/docs/pages/reference/api.en.mdx&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  What is Redoc?
&lt;/h2&gt;

&lt;p&gt;This tool takes YAML files defined in the OpenAPI specification and transforms them into static documents in HTML format. It's an open-source tool.&lt;/p&gt;

&lt;p&gt;In the case of Collections, we employ Nextra, a Next.js-based static site generator, to produce the complete document, which is subsequently integrated and rendered.&lt;/p&gt;
&lt;h2&gt;
  
  
  Install Redoc as a component of React
&lt;/h2&gt;

&lt;p&gt;Let's get started and follow the &lt;a href="https://redocly.com/docs/redoc/deployment/react/"&gt;official steps&lt;/a&gt;. First, install &lt;code&gt;@redocly/cli&lt;/code&gt; and its associated dependencies.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm i react react-dom mobx styled-components core-js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Prepare yaml file
&lt;/h2&gt;

&lt;p&gt;Next, create a yaml file based on the OpenAPI specification.&lt;br&gt;
The Collections API specification is attached for your reference.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/collectionscms/collections/blob/main/docs/data/api.yaml"&gt;https://github.com/collectionscms/collections/blob/main/docs/data/api.yaml&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you are using VSCode, you should install the &lt;code&gt;OpenAPI (Swagger) Editor&lt;/code&gt;, which will warn you of syntax errors so that you can proceed with your work quickly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=42Crunch.vscode-openapi"&gt;https://marketplace.visualstudio.com/items?itemName=42Crunch.vscode-openapi&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Add to component
&lt;/h2&gt;

&lt;p&gt;When the file is ready, fill the page with Redoc components and it will automatically generate and output HTML!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { RedocStandalone } from 'redoc';

&amp;lt;RedocStandalone specUrl="url/to/your/spec"/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, it is not possible to &lt;strong&gt;reference local files&lt;/strong&gt; in the &lt;code&gt;specUrl&lt;/code&gt;. This has been exchanged on GitHub issues, but is due to the same-origin policy and security reasons.&lt;br&gt;
&lt;a href="https://github.com/Redocly/redoc/issues/149#issuecomment-359285569"&gt;https://github.com/Redocly/redoc/issues/149#issuecomment-359285569&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One option is to save yaml to S3, but this is more work, and I don't want to add a CI job to upload the files.&lt;/p&gt;
&lt;h2&gt;
  
  
  Distribute via Next.js API and convert to SSG
&lt;/h2&gt;

&lt;p&gt;Therefore, we solved this problem by using the Next.js API to respond to yaml and passing it on.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export default function handler(_req: NextApiRequest, res: NextApiResponse&amp;lt;ResponseData&amp;gt;) {
  const fullPath = path.join(dataDirectory, 'api.yaml');
  const fileContents = fs.readFileSync(fullPath, 'utf8');
  res.status(200).json(YAML.parse(fileContents));
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Well, this would have been fine, but the rendering is quite slow due to each request, so we need to do something more. Redoc also has a property called &lt;code&gt;spec&lt;/code&gt; that passes an object instead of a URL.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const getStaticProps = ({ params }) =&amp;gt; {
  return fetch(`https://collections.dev/api/openapi`)
    .then((res) =&amp;gt; res.json())
    .then((repo) =&amp;gt; ({
      props: {
        ssg: repo,
      },
    }));
};

export const Redoc = () =&amp;gt; {
  const data = useData();
  return &amp;lt;RedocStandalone spec={data} /&amp;gt;;
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By pre-rendering and passing the object in SSG in this way, the response surface was successfully cleared!&lt;/p&gt;

&lt;h2&gt;
  
  
  Performance
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl 'https://collections.dev/reference/api/' -H 'Accept-Encoding: gzip, deflate, sdch' -H 'Accept-Language: en-US,en;q=0.8,ja;q=0.6' -H 'Upgrade-Insecure-Requests: 1' -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.86 Safari/537.36' -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8' -H 'Connection: keep-alive' --compressed -o /dev/null -w  "%{time_starttransfer}\n" -s

0.239587
0.249859
0.255718
0.249946
0.253883
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response time is usually within 300 ms.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zqFyaXA3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g936ugoco8yxjt8ktnia.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zqFyaXA3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g936ugoco8yxjt8ktnia.png" alt="api" width="800" height="494"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;First Contentful Paint&lt;/code&gt; is also 0.6sec. I think we can give it a passing grade since it is not a page that requires any further improvement or SEO.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finally
&lt;/h2&gt;

&lt;p&gt;I'm building an open source headless CMS &lt;a href="https://collections.dev/"&gt;Collections&lt;/a&gt; that turns WordPress into an API 🚀&lt;/p&gt;

&lt;p&gt;It has Markdown (with GFM), dark mode, and other features that make it comfortable to write with. We also have a &lt;a href="https://demo.collections.dev"&gt;Live Demo&lt;/a&gt;, so please feel free to try it 🙌🙌&lt;/p&gt;

&lt;p&gt;See you soon!&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>react</category>
      <category>redoc</category>
      <category>openapi</category>
    </item>
  </channel>
</rss>
