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-plugin-quickink-logistics

v0.0.2

Published

A starter for Medusa plugins.

Downloads

19

Readme

medusa-plugin-quickink-logistics

Medusa v2 plugin for managing Quickink logistics serviceability by pincode, including admin CRUD, CSV bulk upsert, and admin UI tooling.

Plugin Overview

medusa-plugin-quickink-logistics adds a dedicated module for pincode-level serviceability data with:

  • pincode CRUD (admin APIs)
  • soft-delete aware lookups and listing scopes (active, deleted, all)
  • CSV import endpoint with validation + batched upsert
  • admin extension route for pincode operations

The plugin solves operational management of delivery-serviceability matrices (COD/prepaid/pickup/reverse pickup) at pincode granularity.

Medusa Version

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

Installation & Setup

1) Install

npm install medusa-plugin-quickink-logistics

or

yarn add medusa-plugin-quickink-logistics

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

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

export default defineConfig({
  plugins: [
    {
      resolve: "medusa-plugin-quickink-logistics",
    },
  ],
  modules: [
    {
      resolve: "medusa-plugin-quickink-logistics/modules/quickink-logistics",
    },
  ],
})

3) Run migrations

npx medusa db:migrate

This is required for:

  • quickink_pincode table
  • ID default generation migration
  • partial unique active pincode index used by ON CONFLICT CSV upsert logic

Configuration (config.ts / plugin options)

This plugin uses a runtime config object in src/config.ts for CSV import limits.

CSV Import Config Options

| Option | Type | Required | Default | Description | |---|---|---|---|---| | maxFileSizeBytes | number | No | 5 * 1024 * 1024 | Maximum upload file size in bytes for CSV import middleware and route checks. | | maxRows | number | No | 100000 | Maximum CSV row count allowed during parsing. | | upsertBatchSize | number | No | 2000 | Number of rows per batched INSERT ... ON CONFLICT upsert operation. |

Complete Example Config Block

// src/config.ts
export type CsvImportConfig = {
  maxFileSizeBytes: number
  maxRows: number
  upsertBatchSize: number
}

export const csvImportConfig: CsvImportConfig = {
  maxFileSizeBytes: Number(process.env.QUICKINK_CSV_MAX_FILE_SIZE_BYTES || 5 * 1024 * 1024),
  maxRows: Number(process.env.QUICKINK_CSV_MAX_ROWS || 100000),
  upsertBatchSize: Number(process.env.QUICKINK_CSV_UPSERT_BATCH_SIZE || 2000),
}

Environment Variables

The plugin directly reads these environment variables in src/config.ts:

| Variable | Required | Default | Purpose | Example | |---|---|---|---|---| | QUICKINK_CSV_MAX_FILE_SIZE_BYTES | No | 5242880 | Max allowed CSV file size in bytes. | 10485760 | | QUICKINK_CSV_MAX_ROWS | No | 100000 | Maximum parsed rows per CSV import request. | 50000 | | QUICKINK_CSV_UPSERT_BATCH_SIZE | No | 2000 | Upsert chunk size for DB batch import. | 1000 |

REST APIs / Routes

Auth summary

  • admin routes: Admin JWT/session required
  • store/plugin: Public health route

Admin Pincode APIs

Base: /admin/quickink-logistics/pincodes

GET /admin/quickink-logistics/pincodes

Lists pincodes with pagination, search, filters, sorting, and soft-delete scope.

| Query Param | Type | Required | Default | Description | |---|---|---|---|---| | pincode | string | No | - | Contains search (ILIKE). | | limit | number | No | 20 | Page size (<=100). | | offset | number | No | 0 | Offset pagination. | | order | "created_at" \| "updated_at" \| "pincode" | No | "created_at" | Sort field. | | order_direction | "ASC" \| "DESC" | No | "DESC" | Sort direction. | | deleteonly | boolean | No | false | Legacy toggle; if true and list_scope omitted, scope becomes deleted. | | list_scope | "active" \| "deleted" \| "all" | No | derived | Soft-delete scope. | | cod_delivery | boolean | No | - | Exact boolean filter. | | prepaid_delivery | boolean | No | - | Exact boolean filter. | | pickup | boolean | No | - | Exact boolean filter. | | reverse_pickup | boolean | No | - | Exact boolean filter. |

Response:

{
  "pincodes": [],
  "count": 0,
  "offset": 0,
  "limit": 20
}

POST /admin/quickink-logistics/pincodes

Creates a pincode row.

| Body Field | Type | Required | Description | |---|---|---|---| | pincode | string | Yes | Pincode value (trimmed). | | cod_delivery | boolean | Yes | COD support flag. | | prepaid_delivery | boolean | Yes | Prepaid support flag. | | pickup | boolean | Yes | Pickup support flag. | | reverse_pickup | boolean | Yes | Reverse pickup support flag. |

Response:

{ "pincode": { "id": "qp_...", "pincode": "560001" } }

Duplicate active pincode returns DUPLICATE_ERROR.

GET /admin/quickink-logistics/pincodes/:id

Returns one pincode by record ID.

PUT /admin/quickink-logistics/pincodes/:id

Partial update by record ID.

| Body Field | Type | Required | |---|---|---| | pincode | string | No | | cod_delivery | boolean | No | | prepaid_delivery | boolean | No | | pickup | boolean | No | | reverse_pickup | boolean | No |

At least one field is required.

DELETE /admin/quickink-logistics/pincodes/:id

Soft-deletes a pincode by record ID.

Response:

{ "id": "qp_...", "object": "quickink_pincode", "deleted": true }

PUT /admin/quickink-logistics/pincodes/pincode/:pincode

Updates an active row by pincode value.

DELETE /admin/quickink-logistics/pincodes/pincode/:pincode

Soft-deletes an active row by pincode value.

CSV Import API

POST /admin/quickink-logistics/pincodes/import

Multipart upload endpoint (file field) for CSV import.

| Input | Type | Required | Description | |---|---|---|---| | file | CSV file | Yes | CSV upload field name must be file. | | dry_run | boolean | No | If true, validates/parses only; no DB write. |

Dry run response:

{
  "dry_run": true,
  "parsed_rows": 200,
  "sample": []
}

Import response:

{
  "dry_run": false,
  "total_rows": 200,
  "created": 120,
  "updated": 80
}

CSV parsing behavior:

  • flexible/case-insensitive header matching
  • accepted boolean forms: true/false, 1/0, yes/no, y/n
  • duplicate pincode rows in same file: last row wins

Expected canonical headers (normalized):

  • pincode
  • cod_delivery
  • prepaid_delivery
  • pickup
  • reverse_pickup

Health routes

  • GET /admin/plugin -> 200
  • GET /store/plugin -> 200

Important endpoint examples

curl -X GET "http://localhost:9000/admin/quickink-logistics/pincodes?limit=50&offset=0&list_scope=active&order=updated_at&order_direction=DESC" \
  -H "Authorization: Bearer <admin_jwt>"
curl -X POST "http://localhost:9000/admin/quickink-logistics/pincodes" \
  -H "Authorization: Bearer <admin_jwt>" \
  -H "Content-Type: application/json" \
  -d '{
    "pincode": "560001",
    "cod_delivery": true,
    "prepaid_delivery": true,
    "pickup": true,
    "reverse_pickup": false
  }'
curl -X PUT "http://localhost:9000/admin/quickink-logistics/pincodes/pincode/560001" \
  -H "Authorization: Bearer <admin_jwt>" \
  -H "Content-Type: application/json" \
  -d '{"reverse_pickup": true}'
curl -X POST "http://localhost:9000/admin/quickink-logistics/pincodes/import" \
  -H "Authorization: Bearer <admin_jwt>" \
  -F "[email protected]"
await fetch("/admin/quickink-logistics/pincodes/import", {
  method: "POST",
  credentials: "include",
  body: new FormData(), // append("file", file)
})

Services

QuickinkLogisticsModuleService

Location: src/modules/quickink-logistics/service.ts

Manages pincode CRUD, soft-delete-aware listing, pincode-keyed updates/deletes, and CSV upsert batching.

| Method | Signature | Description | |---|---|---| | listPincodes | (selector?, config?) => Promise<[QuickinkPincodeDTO[], number]> | Lists pincodes with search/boolean filters and list scope. | | getPincodeById | (id: string) => Promise<QuickinkPincodeDTO> | Retrieves one pincode by ID. | | createPincode | (input) => Promise<QuickinkPincodeDTO> | Creates new pincode; rejects active duplicates. | | updatePincodeByPincode | (pincode: string, input) => Promise<QuickinkPincodeDTO> | Updates by active pincode value. | | softDeletePincodeByPincode | (pincode: string) => Promise<{ id: string }> | Soft-deletes by active pincode value. | | updatePincode | (id: string, input) => Promise<QuickinkPincodeDTO> | Partial update by ID. | | softDeletePincode | (id: string) => Promise<void> | Soft-delete by ID. | | upsertFromParsedRows | (rows: ParsedCsvPincodeRow[]) => Promise<{ created: number; updated: number }> | Batched SQL upsert using partial unique conflict target on active pincode index. |

Related parser utilities in csv.ts:

  • parsePincodeCsv(csvText, maxRows)
  • parseCsvBoolean(value, fieldName)

Workflows & Steps (Medusa v2)

No workflows or custom steps are implemented.
src/workflows/index.ts is a placeholder export.

Subscribers / Event Hooks

No subscribers are implemented in runtime source code.

Admin UI / Widgets

Admin Route Extension: Qikink Serviceability

  • Location: src/admin/routes/qikink-serviceability/page.tsx
  • Placement: Admin extension route (label: Qikink serviceability, icon: Adjustments)
  • Renders:
    • paginated pincode table
    • pincode search
    • boolean filter selectors (COD/prepaid/pickup/reverse)
    • list scope + sort controls
    • create/edit modals
    • CSV import button/input
    • per-row edit/delete actions
  • Interactions:
    • list fetch with debounced search
    • create/update/delete API calls
    • CSV upload/import and toast feedback
    • pagination controls
  • Data consumed:
    • /admin/quickink-logistics/pincodes
    • /admin/quickink-logistics/pincodes/:id
    • /admin/quickink-logistics/pincodes/import

Models & Entities

quickink_pincode

Defined by src/modules/quickink-logistics/models/quickink-pincode.ts and migrations.

| Field | Type | Nullable | Notes | |---|---|---|---| | id | text | No | Primary key; DB default added in later migration. | | pincode | text | No | Searchable pincode field. | | cod_delivery | boolean | No | Default false. | | prepaid_delivery | boolean | No | Default false. | | pickup | boolean | No | Default false. | | reverse_pickup | boolean | No | Default false. | | created_at | timestamptz | No | Auto timestamp. | | updated_at | timestamptz | No | Auto timestamp. | | deleted_at | timestamptz | Yes | Soft-delete marker. |

Key index evolution (from migrations):

  • initial normal pincode index
  • later unique constraint/index attempts
  • final active-only partial unique index:
    • IDX_quickink_pincode_pincode_active_unique on ("pincode") WHERE "deleted_at" IS NULL

This final index is required for CSV upsert conflict target and soft-delete re-create behavior.

Use Cases & Examples

  1. Serviceability lookup management

    • Scenario: operations team maintains where logistics services are available.
    • Use: admin list/create/update/delete pincode APIs.
  2. Bulk onboarding of pincodes

    • Scenario: load thousands of serviceability rows from partner exports.
    • Use: CSV import endpoint with batched upsert.
  3. Incremental pincode updates

    • Scenario: quickly toggle reverse pickup for one pincode.
    • Use: PUT /admin/quickink-logistics/pincodes/pincode/:pincode.
  4. Soft-delete and restore-friendly lifecycle

    • Scenario: deprecate a pincode today and re-add later.
    • Use: soft-delete APIs + active-only unique index behavior.
  5. Ops dashboard workflow

    • Scenario: non-technical admins manage records visually.
    • Use: Admin extension page (Qikink serviceability) for filtering, editing, importing.

Troubleshooting

CSV file is required

  • Cause: multipart upload missing file field.
  • Fix: send FormData with file.

CSV file exceeds maximum size (...)

  • Cause: upload larger than configured max.
  • Fix: increase QUICKINK_CSV_MAX_FILE_SIZE_BYTES or reduce file size.

CSV row count exceeds limit (...)

  • Cause: parsed rows exceed configured max.
  • Fix: increase QUICKINK_CSV_MAX_ROWS or split CSV into smaller files.

CSV missing required columns: ...

  • Cause: CSV headers missing normalized required columns.
  • Fix: ensure required header set is present (case-insensitive, punctuation ignored).

Invalid boolean value for ...

  • Cause: CSV boolean value not in accepted formats.
  • Fix: use true/false, 1/0, yes/no, or y/n.

no unique or exclusion constraint matching the ON CONFLICT specification

  • Cause: DB missing partial unique active pincode index.
  • Fix: run npx medusa db:migrate and verify IDX_quickink_pincode_pincode_active_unique exists.

Pincode ... already exists

  • Cause: create attempted for existing active pincode.
  • Fix: update existing row or soft-delete first, then recreate.