Disclaimer

In most cases, we recommend using our TypeScript SDK. It leverages modern, convenient programming techniques and, being TypeScript, provides the added benefit of static type-checking for improved reliability and development efficiency.

However, a JavaScript version is also available and actively maintained. Its primary advantage is that it works directly in browsers without requiring a compiler toolchain, making it a lightweight and straightforward option for certain use cases.


Installation

To use the JavaScript SDK with npm, you can install it with the following command:

npm i merchi_sdk_js

Alternatively, the latest minified version is available at:

https://merchi.co/static/js/dist/merchi.js

You can download it for your project or hotlink it directly.

For the unminified source code and additional resources, visit the GitHub repository:

https://github.com/merchisdk/merchi_sdk_js

To include the SDK via a <script> tag:

<script type="text/javascript" src="https://merchi.co/static/js/dist/merchi.

Important: Authentication

To access any API endpoints, authentication is required.

Generating an API Key

You can generate your API key in your developer dashboard. This key will serve as your unique identifier when making API requests.

Including the API Key

Add the generated API key to all requests as a GET parameter. Ensure it is included in the query string for every API call.

Key Requirement:

Important: Nothing will function unless you include a valid API key in your request. Make sure to keep your key secure and avoid sharing it publicly.


The Merchi object

All functionality in the SDK is encapsulated in a Merchi object, which must be manually initialized. This design ensures proper namespacing. The examples in this document assume that you have already set up the merchi object as demonstrated below:

Example: Initializing the Merchi Object

import { merchi as sdk_merchi } from "merchi_sdk_js";

// Initialize with backend and websocket URIs
const merchi = sdk_merchi("https://api.merchi.co/", "https://websockets.merchi.co/");

This setup prepares the merchi object for use in subsequent API calls.


Fluent API Style for JavaScript SDK Entities

In the JavaScript SDK, entities utilize a fluent API style, distinguishing them from the TypeScript SDK.

Example: Getting and Setting Attributes

// getting and setting
const domain = new merchi.Domain();
console.log("Domain name is: ", domain.domain());

const name = "new-value.example.com";
const newDomain = domain.domain(name);
console.log("Domain name has now been set to: ", domain.domain());

// example of chaining multiple sets and a single get into one line
const logoUrl = domain
    .domain("even-newer-value.example.com")
    .smsName("sms.example.com")
    .enableSmsNotifications(true)
    .enableEmailNotifications(false)
    .logoUrl();

  console.log("Sms notifications was set to: ", domain.enableSmsNotifications());
  console.log("Email notifications was set to: ", domain.enableEmailNotifications());
  console.log("New domain name is: ", domain.domain());

Listing Entities

You can retrieve a list of entities by calling the get method on the entity name. The entity name should be in camelCase format (e.g., products, categories).

Here’s an example demonstrating how to list products and invoices:

Example: Listing Products

// Success callback
function onSuccess(products) {
  console.log("Successfully fetched products!");
  console.log("Number of products:", products.length);

  if (products.length > 0) {
    console.log("ID of the first product:", products[0].id());
  } else {
    console.log("No products found.");
  }
}

// Error callback
function onError(error) {
  console.error("Error fetching products:", error);
}

// Optional parameters for the request
const parameters = {};

// Fetch the list of products
merchi.products.get(onSuccess, onError, parameters);

Example: Listing Invoices

// Success callback
function onSuccess(invoices) {
  console.log("Successfully fetched invoices!");
  console.log("Number of invoices:", invoices.length);

  if (invoices.length > 0) {
    console.log("ID of the first invoices:", invoices[0].id());
  } else {
    console.log("No invoices found.");
  }
}

// Error callback
function onError(error) {
  console.error("Error fetching products:", error);
}

// Optional parameters for the request
const parameters = {};

// Fetch the list of invoices
merchi.invoices.get(onSuccess, onError, parameters);

Fetching a Single Entity

Fetching a single entity in the JavaScript SDK follows a different approach compared to the TypeScript SDK.

Here’s how it works:

  1. Create an entity object: Initialize the entity you want to fetch (e.g., Product, Category).
  2. Set the ID: Assign the ID of the specific entity you want to retrieve.
  3. Call get: Use the get method with success and error callbacks. The entity object will be automatically populated with data from the server upon success.

Example: Fetching a Job

// Create a job instance
const job = new merchi.Job();

// Set the ID of the job you want to fetch
job.id(42);

// Success callback
function onSuccess() {
  // `job` is now updated with data from the Merchi server
  console.log(`Job type: ${job.jobType()}`);
}

// Error callback
function onError(error) {
  console.error("Error fetching job:", error);
}

// Optional embed parameters for nested data
const embed = {};

// Fetch the job from the server
job.get(onSuccess, onError, embed);

Example: Fetching a Product

// Create a product instance
const product = new merchi.Product();

// Set the ID of the product you want to fetch
product.id(42);

// Success callback
function onSuccess() {
  // `product` is now updated with data from the Merchi server
  console.log(`Product name: ${product.name()}`);
}

// Error callback
function onError(error) {
  console.error("Error fetching product:", error);
}

// Optional embed parameters for nested data
const embedProduct = {};

// Fetch the product from the server
product.get(onSuccess, onError, embedProduct);

Key Points

This method gives you fine-grained control over fetching single entities while ensuring that the object is updated in place.Creating a New Entity

As with fetching, to create a new entity on the server, you must first create a new entity object locally in javascript. Instead of filling in the objects id, you should fill in the data that you want to provide to the new object, and then call create. Like the other method in the javascript SDK, create takes onSuccess and onError callbacks to let you know when the operation has completed.


Creating a New Entity

The Merchi JavaScript SDK allows you to interact with the server to create new entities. This guide walks you through the process of creating a session and a product on the server.

To create a new entity, follow these steps:

  1. Instantiate a new object for the entity you want to create (e.g., Product).
  2. Fill in the details you want for the new entity, instead of setting an id.
  3. Use the create method to send the entity data to the server.
  4. Provide onSuccess and onError callbacks to handle the operation’s result.

Example: Creating a Product

Step 1: Establish a Session

Before creating any entity, you need to establish a session with the server. Use your credentials securely (ideally on the server side, not in client-side code).

const embed = {}; // Configuration object
const userName = "userName@gmail.com"; // Replace with your username
const password = "userpassword";       // Replace with your password
const newSession = new merchi.Session();

function sessionCreateSuccess() {
  console.log("Session created successfully!");
}

function sessionCreateError(error) {
  console.log("Error creating session: ", error);
}

// Create a session
await newSession.create(sessionCreateSuccess, sessionCreateError, embed, userName, password);

Step 2: Create a New Product

After establishing a session, you can create a product by setting its properties and calling the create method.

const newProduct = new merchi.Product();
newProduct.name("Beans"); // Set the product name
newProduct.description("Beans are a delicious legume."); // Set the product description

function onSuccess() {
  console.log(`The product has been created with id ${newProduct.id()}`);
}

function onError(error) {
  console.log("Error creating product: ", error);
}

// Send the product data to the server
await newProduct.create(onSuccess, onError, embed, domainId);

Key Points

  1. Sensitive Data:
    • Never hardcode sensitive information like userName and password in client-side code. Use secure server-side mechanisms for handling credentials.
  2. Asynchronous Operations:
    • Use await to ensure that the operations complete before proceeding.
    • Alternatively, handle the returned promises explicitly using .then() and .catch().
  3. Error Handling:
    • Always provide meaningful error-handling logic in the onError callback to handle potential issues, such as network errors or server-side validation failures.

Editing an Entity

The process for editing values on the server follows a straightforward pattern. The method used for this operation is patch, named after the HTTP method it employs. Here’s how to edit a product:

  1. Create a JavaScript object that represents the product you want to edit.
  2. Set the product’s id (primary key) and any attributes you want to modify.
  3. Call the patch method to submit the changes.

As with other methods, patch accepts two callback functions: one to handle success and another to handle errors.

Example: Editing a Product

const existingProduct = new merchi.Product();
existingProduct.id(42); // we want to edit the product with id 42
existingProduct.name("New Product Name"); // we want to change its name to a ne

function onSuccess() {
  console.log("The product has been edited.");
}

function onError(error) {
	console.log("Error details: ", error);
}

existingProduct.patch(onSuccess, onError); // this call makes the network request

Deleting an Entity

To delete an entity from the server, follow these simple steps:

  1. Create a JavaScript object for the entity you want to delete.
  2. Set the entity’s id to identify the item you want to remove.
  3. Call the destroy method to delete the entity.

The destroy method accepts two optional callback functions:

Example: Deleting a Product

const existingProduct = new merchi.Product();
existingProduct.id(42); // we want to delete the product with id 42

function onSuccess() {
  console.log("The product has been deleted.");
}

function onError(error) {
  console.log("Error details: ", error);
}

existingProduct.destroy(onSuccess, onError);

Creating Nested Entities

As with the typescript SDK, nested entities can be created at once. For example, you might create a product and its category in the same request.

Example: Creating a product and category at once

const newProduct = new merchi.Product();
newProduct.name("lime");
newProduct.description("limes are green");
const newCategory = new merchi.Category();
newCategory.name("fruits");
// note that an array is provided as products may be in
// multiple categories.
newProduct.categories([newCategory]);
function onSuccess() {
    console.log("the product and category both now exist");
}
const onError = console.error.bind(console);
newProduct.create(onSuccess, onError);  // this call makes the network request

Pagination

When working with large datasets (e.g., tens or hundreds of thousands of entities), it is not feasible to fetch all the results at once. Instead, results are returned in “pages,” allowing for efficient data retrieval and handling.

Key Concepts

  1. limit Option:

    • Controls how many results are returned per page.
    • Defaults to 25 if not specified.
    • May be restricted by backend-enforced limits or the total number of available entities.
  2. offset Option:

    • Specifies the number of results to skip over.
    • For example:
      • If limit is set to 10 and offset is 20, the third “page” of results will be returned.
    • Defaults to 0 if not supplied.
  3. Meta Information:

    • The result array returned to the success callback includes a

      meta object with the following details:

      • meta.available: Total number of results available in the dataset.
      • meta.count: Number of results in the current “page.”
      • meta.limit: Number of results requested per page (via the limit option).
      • meta.offset: Starting point for the current set of results (via the offset option).

Example: Fetching Paginated Results

function onSuccess(products) {
    console.log(`Got ${products.length} products`);
    console.log(`${products.meta.available} products are available`);
    console.assert(products.length == products.meta.count);
    console.assert(products.meta.limit == 2);
    console.assert(products.meta.offset == 3);
  }

  function onError(error) {
    console.log("Error fetching products: ", error);
  }

  const parameters = { offset: 3, limit: 2 };

  merchi.products.get(onSuccess, onError, parameters);

Searching

Many entities in the system support a q parameter in their list method. This allows you to filter results by search terms, enabling quick and efficient searches.

How It Works:

Example: Searching for Egg-Related Products

function onSuccess(products) {
  console.log(`Got ${products.length} products`);
  console.log("Product names:");
  for (const product of products) {
    console.log(product.name());
  }
}

function onError(error) {
  console.log("Error fetching products: ", error);
}

// Define search parameters with the query term
const parameters = { q: "egg" };

merchi.products.get(onSuccess, onError, parameters);

Embedding

In the Merchi SDK, entities can be nested deeply and may include cyclic references. By default, the fetching and listing methods only retrieve scalar properties (e.g., strings, numbers, dates). Nested entities are not included in the response unless explicitly requested.

To include nested entities, you can use the embed parameter to specify which nested entities to fetch and to what depth.


Key Concepts

  1. Default Behavior:
    • Only scalar properties are retrieved during a fetch or list operation.
    • Nested entities are excluded unless explicitly embedded.
  2. Using the embed Parameter:
    • The embed parameter allows you to fetch specific nested entities.
    • Depth and scope of nested entities can be controlled using the embed configuration.
  3. Understanding undefined vs. null:
    • undefined: Indicates the nested property has not been included in the fetched data or updated locally.
    • null: Indicates the nested property exists but has no value on the server.

Example: Fetching Nested Entities

function onSuccess(categories) {
    console.log(`Got ${categories.length} categories`);
    const category = categories[0];
    const categoryId = category.id();
    console.log(`Category name: ${category.name()}`);
    console.log(`Category id: ${categoryId}`);
    console.log(`Category domain: ${category.domain()}`);
  }

  function onError(error) {
    console.log("Error fetching category: ", error);
  }

  const embed = { domain: {} };
  merchi.categories.get(onSuccess, onError, embed);

Filtering

The Merchi SDK provides advanced filtering options to narrow down results based on specific criteria. These filters are more targeted than basic search functionality, enabling precise control over the data retrieved.

Common Filters:

  1. inDomain: Restricts results to a specific domain.
  2. tags: Filters results to include only entities with specific tags.
  3. Entity-Specific Filters: Each entity may support additional unique filters. For a complete list of available filters, refer to the Filtering Reference.

Example: Filtering Products by Tags

function onSuccess(products) {
    console.log("Ok, got some products!");
    console.log("Products amounts: ", products.length);
    console.log("Id of the first product: ", products[0].id());
  }

  function onError(error) {
    console.log("Error fetching products: ", error);
  }

  const parameters = { inDomain: 206 };
  merchi.products.get(onSuccess, onError, parameters);

Job Entity

The Job entity is a core object in this system, designed to manage job-related data and operations. It includes methods for creating, retrieving, updating, and interacting with job details.

Initialization

Constructor

import { merchi as sdk_merchi } from "merchi_sdk_js";

// Initialize with backend and websocket URIs
const merchi = sdk_merchi("https://api.merchi.co/", "https://websockets.merchi.co/");

const job = new merchi.Job();

Properties

See the full list of properties in the source code.


Create a Job

create(success, error, embed, asDomain, withRights)

Creates a new job with serialized data and specified settings.

Parameters:

Example:

const domainId = 42;

await job.create(
  (createdJob) => console.log('Job created:', createdJob),
  (err) => console.error('Error creating job:', err),
  {},
  domainId,
  true
);

Retrieve a Job

get(success, error, embed, includeArchived, withRights)

Retrieves job details by id.

Parameters:

Example:

job.id(12345);
await job.get(
  (retrievedJob) => console.log('Job details:', retrievedJob),
  (err) => console.error('Error fetching job:', err),
  {},
  false,
  true
);

Update a Job

patch(success, error, embed, asDomain, withRights)

Updates job details with modified data.

Parameters:

Example:

const domainId = 42;
job.notes('Updated job notes.');
await job.patch(
  (updatedJob) => console.log('Job updated:', updatedJob),
  (err) => console.error('Error updating job:', err),
  {},
  domainId,
  true
);

Delete a Job

destroy(success, error)

Deletes the job by id.

Example:

job.destroy(
  () => console.log('Job deleted successfully'),
  (err) => console.error('Error deleting job:', err)
);

Check Job Completion

isComplete()

Determines if the job is complete based on its production, drafting, payment, and shipping statuses.

Example:

const isComplete = job.isComplete();
console.log('Is the job complete?', isComplete);

Calculate Total Cost

productTotalCost()

Calculates the total cost of the job’s product based on quantity and cost per unit.

Example:

const totalCost = job.productTotalCost();
console.log('Total product cost:', totalCost);

Assignments and Variations

assignmentsUnarchived()

Returns all active (non-archived) assignments.

Example:

const activeAssignments = job.assignmentsUnarchived();
console.log('Active assignments:', activeAssignments);

hasVariations()

Checks if the job has variations.

Example:

const hasVariations = job.hasVariations();
console.log('Job has variations:', hasVariations);

variationsTotalCost()

Calculates the total cost of all variations.

Example:

const totalVariationCost = job.variationsTotalCost();
console.log('Total variations cost:', totalVariationCost);

Advanced Use Cases

Fetch and Update Assignment by ID

fetchAndUpdateAssignmentById(success, error, assignmentId, embed)

Fetches the latest assignment details by ID and updates it in the job.

Example:

job.fetchAndUpdateAssignmentById(
  () => console.log('Assignment updated successfully'),
  (err) => console.error('Error updating assignment:', err),
  123,
  {}
);

Exploring Other Merchi SDK Entities

So far, we’ve worked with products, categories, and job but the Merchi SDK offers many other entities such as cart, invoice, and payment. These entities help manage different parts of your store, like cart info, order details, and payment status.

For a complete list of available entities, check out the API Reference.


Completeness

The TypeScript SDK is the most actively used and maintained within our development ecosystem. As a result, it may receive updates and new features more frequently compared to the JavaScript SDKs.

That said, all SDKs are fully supported and designed to meet your development needs. If you encounter any missing features or functionality in the JavaScript SDKs, please do not hesitate to contact us. We are committed to ensuring a seamless experience and can typically address such gaps or add features promptly.