npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

medusa-blog-management

v0.0.12

Published

A starter for Medusa plugins.

Readme

medusa-blog-management

Medusa v2 plugin that adds a blog module with admin CRUD, storefront read APIs, and optional storefront helper utilities.

Plugin Overview

medusa-blog-management adds blog content management capabilities to a Medusa backend.

Implemented features in code:

  • Custom blog module with a blog_post model and migration.
  • Admin APIs for full blog post CRUD.
  • Store APIs for published blog listing/detail.
  • Store detail route increments read_count.
  • Admin extension routes/pages for listing, creating, editing, and viewing posts.
  • Tiptap-based rich-text editor component for admin authoring.
  • Helper utilities for storefront integration (getBlogList, getBlogPost, metadata generator).

It solves the need to run editorial/blog content in the same Medusa system as commerce data.

Medusa Version

  • Built for Medusa v2 (@medusajs/framework / @medusajs/medusa 2.12.4).

Installation & Setup

1) Install plugin

npm install medusa-blog-management
yarn add medusa-blog-management

2) Register module + plugin in medusa-config.ts

import { defineConfig } from "@medusajs/framework/utils"

export default defineConfig({
  modules: {
    blog: {
      resolve: "medusa-blog-management",
    },
  },
  plugins: [
    {
      resolve: "medusa-blog-management",
    },
  ],
})

⚠️ Note: The code exports the module from src/index.ts and includes admin route extensions; in practice, projects typically keep module and plugin registration aligned as shown above.

3) Run migrations

npx medusa db:migrate

The plugin includes migration Migration20260112153000 to create blog_post.

Configuration (config.ts / plugin options)

No plugin options/config schema is implemented in source code. The plugin is registered by resolve only.

| Option | Type | Required | Default | Description | |---|---|---|---|---| | None implemented | - | - | - | No runtime plugin options are defined in code. |

Example:

{
  resolve: "medusa-blog-management"
}

Environment Variables

No process.env.* usage exists in implemented source files under src.

| Variable | Purpose | Required | Example | |---|---|---|---| | None in implementation | - | - | - |

REST APIs / Routes

Admin blog routes

1) GET /admin/blog-posts

Returns paginated list of blog posts.

  • Auth: Admin route context
  • Query params:

| Param | Type | Required | Notes | |---|---|---|---| | skip | number | No | Default 0, must be >= 0. | | take | number | No | Default 20, range 1..100. |

  • Response: { posts, count, limit, offset }

2) POST /admin/blog-posts

Creates a blog post (validated by Zod CreateBlogPostSchema).

  • Auth: Admin route context
  • Body schema:

| Field | Type | Required | Notes | |---|---|---|---| | title | string | Yes | 1..500 chars. | | handle | string | No | Auto-generated from title if omitted. | | status | "draft" \| "published" | No | Default "draft". | | thumbnail | string \| null | No | URL validation when provided. | | short_description | string \| null | No | Max 1000 chars. | | read_time | number | No | Integer, >= 0, default 0. | | tags | string[] \| null | No | Stored in JSON field. | | body | string \| null | No | Rich text HTML. | | created_by | string \| null | No | Max 255 chars. | | metadata | Record<string, unknown> \| null | No | Arbitrary metadata. |

  • Response: { post }

3) GET /admin/blog-posts/:id

Returns single blog post by id.

  • Auth: Admin route context
  • Response: { post }

4) POST /admin/blog-posts/:id

Updates blog post by id (validated by UpdateBlogPostSchema).

  • Auth: Admin route context
  • Body: all create fields optional (with same validation constraints).
  • Response: { post }

5) DELETE /admin/blog-posts/:id

Deletes blog post by id.

  • Auth: Admin route context
  • Response: { id, object: "blog_post", deleted: true }

Store blog routes

6) GET /store/blog-posts

Returns paginated published posts only.

  • Auth: Public store route
  • Query params: skip (default 0), take (default 10, 1..100)
  • Response: { posts, count, limit, offset }

7) GET /store/blog-posts/:id

Returns a published post by id.

  • Auth: Public store route
  • Behavior: asynchronously increments read_count via incrementReadCount.
  • Response: { post }

Health routes

8) GET /admin/plugin

Simple plugin health endpoint, returns 200.

9) GET /store/plugin

Simple plugin health endpoint, returns 200.

cURL examples

curl -X GET "http://localhost:9000/admin/blog-posts?skip=0&take=20" \
  -H "Authorization: Bearer <ADMIN_TOKEN>"
curl -X POST "http://localhost:9000/admin/blog-posts" \
  -H "Authorization: Bearer <ADMIN_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "How to launch with Medusa",
    "status": "draft",
    "short_description": "Launch checklist for your store",
    "body": "<p>Content...</p>",
    "tags": ["launch", "guide"],
    "created_by": "Team"
  }'
curl -X GET "http://localhost:9000/store/blog-posts?skip=0&take=10"
curl -X GET "http://localhost:9000/store/blog-posts/<post_id>"

Services

BlogModuleService

Location: src/modules/blog/service.ts

Extends MedusaService({ Post }) and provides generated repository/service operations for Post.

Custom method implemented:

| Method | Signature | Description | |---|---|---| | incrementReadCount | (id: string) => Promise<void> | Retrieves post and increments read_count by 1. Used in store detail route. |

Common generated methods used by routes (from MedusaService model integration):

  • listAndCountPosts
  • createPosts
  • retrievePost
  • updatePosts
  • deletePosts

Workflows & Steps (Medusa v2)

No custom workflows or steps are implemented.

Subscribers / Event Hooks

No event subscribers are implemented.

Admin UI / Widgets

The plugin includes admin route extensions (not widget-zone injection).

Admin routes

| Route | Purpose | Interaction/Data | |---|---|---| | /blog | Blog list table | Fetches /admin/blog-posts; supports delete; links to create/edit/detail. | | /blog/create | Create post page | Uses BlogForm; submits to /admin/blog-posts. | | /blog/:id | Edit post page | Fetches /admin/blog-posts/:id; submits updates via POST /admin/blog-posts/:id. | | /blog/detail/:id | Read-only admin detail view | Fetches /admin/blog-posts/:id; displays metadata/tags/content. |

Main admin components

| Component | What it renders | |---|---| | BlogForm | Full authoring form (title, status, author, read time, thumbnail upload, tags, rich content). | | RichTextEditor | Tiptap editor with bold/italic/headings/lists/image upload/YouTube embed. |

Admin media upload interaction

Both BlogForm and RichTextEditor upload images via POST /admin/uploads, then persist returned file URL in content/thumbnail fields.

Models & Entities

blog_post model

Defined in src/modules/blog/models/post.ts, with migration in Migration20260112153000.

| Field | Type | Nullable | Notes | |---|---|---|---| | id | id | No | Primary key. | | title | text | No | Post title. | | handle | text | No | Unique slug/handle. | | status | text | No | Default "draft" (draft/published usage in code). | | thumbnail | text | Yes | Thumbnail URL. | | short_description | text | Yes | Summary text. | | read_time | number | No | Default 0. | | tags | json | Yes | Stored as array/object JSON. | | body | text | Yes | Rich text content. | | created_by | text | Yes | Author name. | | read_count | number | No | Default 0. | | metadata | json | Yes | Arbitrary metadata. | | created_at | timestamptz | No | Migration-managed timestamp. | | updated_at | timestamptz | No | Migration-managed timestamp. | | deleted_at | timestamptz | Yes | Soft-delete marker. |

Indexes in migration:

  • Unique index on handle where deleted_at IS NULL
  • Index on deleted_at where deleted_at IS NULL

Relationships: no explicit relations to core Medusa entities are defined.

Use Cases & Examples

  1. Editorial blog management inside Medusa Admin

    • Create/edit/publish posts from /blog admin routes without external CMS.
  2. Public storefront blog feed

    • Use GET /store/blog-posts to render a paginated list of published content.
  3. Article detail analytics

    • Use GET /store/blog-posts/:id; each read increments read_count.
  4. SEO/social metadata generation in frontend apps

    • Use exported helper getBlogMetadata(post) for OG/Twitter metadata.
  5. Headless storefront integration

    • Use getBlogList and getBlogPost helper functions with base URL + publishable key transport options.

Troubleshooting

EntityManager is not available in the container

  • Cause: BlogModuleService constructor requires injected manager.
  • Fix: ensure plugin/module registration is correct in medusa-config.ts, rebuild plugin, and restart server.

Migration/table errors (blog_post not found)

  • Cause: migration not applied.
  • Fix: run npx medusa db:migrate and verify migration Migration20260112153000 is included in build output.

Validation failed on create/update

  • Cause: request payload does not match Zod schema (validators.ts).
  • Fix: check response errors array and adjust field formats (URL, lengths, enums, numeric ranges).

Store detail endpoint returns 404 for existing post

  • Cause: store route returns only status === "published".
  • Fix: publish the post via admin update before expecting storefront visibility.

Image upload failures in admin form/editor

  • Cause: /admin/uploads unavailable or file upload module not configured in host app.
  • Fix: configure Medusa upload module/provider and verify admin upload permissions.