Plugin Development API
Plugins in ShopsWired are built using JavaScript and run server-side within our embedded JS scripting engine. This allows you to safely execute code, modify data on the fly via hooks, and build entirely new data models using Custom Records.
1. The manifest.json
Every plugin requires a manifest.json file to define its metadata, scripts, settings, and custom database schemas.
Example: A Blog Plugin
{
"id": "blog",
"name": "Blog Plugin",
"version": "1.0.0",
"scripts": [
{ "path": "api.js", "type": "route", "method": "GET", "route_path": "/blog" },
{ "path": "hooks.js" }
],
"settings": [
{
"key": "inject_footer",
"type": "checkbox",
"label": "Inject Footer Menu Link",
"default": true
}
],
"custom_records": [
{
"id": "blog",
"name": "Blog Posts",
"icon": "📝",
"fields": [
{ "name": "title", "type": "string", "list": true },
{ "name": "slug", "type": "string", "list": true },
{ "name": "content", "type": "richtext" },
{ "name": "published_at", "type": "datetime", "group": "Publishing" }
]
}
]
}The custom_records array is incredibly powerful. It allows your plugin to dynamically generate complete CRUD interfaces within the admin dashboard without writing a single line of backend Go or frontend Vue code.
2. Event Hooks
Hooks allow you to intercept and modify data during core lifecycle events. To use hooks, export functions matching the hook name in your designated hook script (e.g., hooks.js).
Example: Auto-tagging Products
module.exports = {
"product.before_save": function (event) {
// Auto-generate SKU if missing
if (!event.data.sku) {
event.data.sku = "AUTO-" + Date.now();
}
// Ensure tags always include "demo"
if (event.data.tags && Array.isArray(event.data.tags)) {
if (event.data.tags.indexOf("demo") === -1) {
event.data.tags.push("demo");
}
}
}
};Example: Custom Tax Calculation
You can completely override core functionality, such as replacing the built-in tax calculator with an external API or custom logic.
module.exports = {
"tax.calculate": function (event) {
// event.data contains: cart, subtotal, shipping, address
if (event.data.address.state === "TX" && event.data.address.country === "US") {
var taxable = event.data.subtotal + event.data.shipping;
event.data.tax = Math.round(taxable * 0.0825);
event.data.name = "TX Sales Tax";
}
}
};3. The Global API Bridges
The global sw object and standard JS functions like fetch() provide access to the platform's core infrastructure.
Store Bridge (CRUD)
Access and manipulate core entities like products, customers, orders, and your records.
// Update a product
var product = sw.products.save({ id: 123, price: 2499 });
// Access custom records defined in your manifest
var blogPost = sw.records.blog.save({
title: "Hello World",
slug: "hello-world",
content: "..."
});
// List records (with filters and sorting)
var posts = sw.records.blog.list({ limit: 5 }).items;HTTP & Network (Fetch)
Use the standard fetch() API to interact with external services synchronously.
var response = fetch("https://httpbin.org/post", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ source: "plugin" })
});
console.log(response.status);Files & Cache Bridges
Upload files directly to the shop's CDN or leverage the in-memory cache.
// Upload an image from an external URL directly to the shop's storage
var file = sw.files.upload("images/sample.jpg", fetch("https://picsum.photos/200/200"));
console.log(file.path);
// Use the distributed cache (set for 60 seconds)
sw.cache.set("api_rate_limit", 42, 60);
var counter = sw.cache.get("api_rate_limit");