git clone https://github.com/merchisdk/merchi_sdk_ts.git
cd merchi_sdk_ts
yarn
git clone https://github.com/merchisdk/merchi_sdk_ts.git
cd merchi_sdk_ts
npm install
To access any API endpoints, authentication is required.
You can generate your API key in your developer dashboard. This key will serve as your unique identifier when making API requests.
Add the generated API key to all requests as a GET parameter. Ensure it is included in the query string for every API call.
Merchi object automatically retrieves the session token of the currently logged-in user from the Merchi cookie.sessionToken argument to the Merchi constructor.For example:
const merchi = new Merchi("your-session-token");
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 class serves as the central access point for all functionality provided by the Merchi TypeScript SDK. To begin, create an instance of this class:
import { Merchi } from "merchi_sdk_ts";
// Create an instance of the Merchi SDK
const merchi = new Merchi();
If you have placed the TypeScript SDK in a non-standard directory, you may need to adjust the import path accordingly. For instance, if the SDK resides in merchisdk/typescript/, ensure your import path aligns with your project’s directory structure.
For modern React and Next.js projects, manual compilation with a tool like tsc is not required. These frameworks come with built-in support for TypeScript and handle compilation automatically. Below is a streamlined explanation of how TypeScript works in Next.js and React projects:
Automatic TypeScript Setup
When you add .ts or .tsx files to a Next.js project, the framework automatically enables TypeScript support. If TypeScript isn’t installed in your project, you’ll be prompted to install the necessary dependencies.
To set up manually:
npm install --save-dev typescript @types/react @types/node
Create a TypeScript File
Replace or create a file named index.tsx in the pages/ folder. For example:
const HomePage = () => {
return <h1>Hello, TypeScript in Next.js! <h1/>;
};
export default HomePage;
Run the Development Server Start the Next.js app:
npm run dev
Next.js will compile your TypeScript files into JavaScript during development and production builds (npm run build).
Compiled Output
Next.js automatically bundles your code for the browser. There’s no need to include compiled JavaScript files manually with <script> tags.
If you’re working on a standalone React project without Next.js, you can still use TypeScript with the same simplicity.
Install TypeScript in Your React App If you’re creating a new React app, you can initialize it with TypeScript:
npx create-react-app my-app --template typescript
Write and Compile TypeScript Code
Add your TypeScript files (.ts or .tsx) as needed. React’s tooling (such as Webpack) will handle the compilation process automatically.
If you’re working outside of these frameworks or need to compile TypeScript manually for specific tasks, you can still use the TypeScript compiler directly:
Compile TypeScript Code
Assuming you have a file named index.ts:
npx tsc index.ts
This will generate an index.js file in the same directory.
Include Compiled Output in HTML You can use the compiled JavaScript output in an HTML file as follows:
<script type="application/javascript" src="index.js"></script>
Merchi organizes entities into distinct types, which can be considered similar to REST resources. For each entity type, there are typically many specific instances available. After initializing the merchi object, you can retrieve an array of entities using the list method.
For example, to fetch a list of categories in the system (which are used to group products), you can use the following code:
Example: Listing categories
import { Merchi } from "merchi_sdk_ts";
const merchi = new Merchi();
const categories: any[] = [];
// Fetch categories using the 'list' method on the 'Category' entity
await merchi.Category.list()
.then((result: { items: any[] }) => {
for (const category of result.items) {
categories.push(category.toJson());
}
console.log("Categories:", categories);
})
.catch((error: any) => {
console.error(
`Error: ${error.errorCode} ${error.statusCode} ${error.errorMessage}`
);
});
Example: Listing Invoices
import { Merchi } from "merchi_sdk_ts";
const merchi = new Merchi("your-session-token"); // Authentication Required
const parameters = { inDomain: 18 };
try {
const result = await merchi.Invoice.list(parameters);
if (!result.items || result.items.length === 0) {
console.log("Log - No invoices found");
return;
}
for (const invoice of result.items) {
const invoiceJSON = invoice.toJson();
console.log(`Log - Invoice ID: ${invoiceJSON.id}`);
}
} catch (error) {
console.error("Fetching Error:", error);
}
console.log("Log - Finished fetching invoices");
In Merchi, almost every entity is uniquely identified by its id. Once you have the id of an entity, you can fetch that specific entity using the get static method.
Example: Fetching a job
import { Merchi } from "merchi_sdk_ts";
const merchi = new Merchi("your-session-token"); // Authentication Required
const jobId = 42;
const embed = {
domain: {},
};
await merchi.Job.get(jobId, { embed: embed })
.then((job: any) => {
console.log("Job: " + JSON.stringify(job));
})
.catch((error: any) => {
console.error("Error:", error);
});
Example: Fetching a category
import { Merchi } from "merchi_sdk_ts";
const merchi = new Merchi();
// Define the ID of the category you want to fetch
const categoryId = 42;
// Use the 'get' method to fetch the category by its ID
await merchi.Category.get(categoryId)
.then((category: any) => {
// Log or process the category's name
console.log("Category Name:", category.toJson().name);
})
.catch((error: any) => {
// Handle errors that occur during the fetch operation
console.error("Error fetching category:", error);
});
New entities can be added to the system using the create method.
To create a new entity, follow these steps:
Merchi object.create method to save the entity to the Merchi system.Note: Creating and editing objects locally in JavaScript/TypeScript has no effect on the server. The entity will only be stored in Merchi after you call
create, which triggers a network request to the server.Important: You do not need to manually assign an
idto the entity. Merchi will automatically generate anidfor the new object when it’s created.
Example: Creating single category
import { Merchi } from "merchi_sdk_ts";
const merchi = new Merchi("your-session-token");
const newCategory = new merchi.Category();
newCategory.name = "New Category Name";
// Call the 'create' method to store the new category in the system
newCategory
.create()
.then(() => {
// Once the category is created, its ID is automatically generated
console.log("The new category's ID is: " + newCategory.id);
})
.catch((error: any) => {
console.error(
`Error: ${error.errorCode} ${error.statusCode} ${error.errorMessage}`
);
});
Existing entities can be edited using the save method.
To edit an entity:
id attribute is set (this is required for editing).save method to update the entity in the system.You can use the objects returned by the list or get methods to edit them, as these objects will already have the id populated. However, if you already know the id of the entity you wish to edit, you can directly specify the id without needing to fetch the entity from the server.
Example: Editing a category
import { Merchi } from "merchi_sdk_ts";
const merchi = new Merchi("your-session-token");
const categoryToEdit = new merchi.Category();
categoryToEdit.id = 42;
categoryToEdit.name = "New Name"; // make a correction to the name
categoryToEdit
.save()
.then(() => {
console.log("ok, the category name was edited.");
})
.catch((error: any) => {
console.error(
`Error: ${error.errorCode} ${error.statusCode} ${error.errorMessage}`
);
});
Entities can be deleted via the delete method.
As with editing, if you know the id of the object, you do not have to fetch it before deleting.
Example: Deleting a category
import { Merchi } from "merchi_sdk_ts";
const merchi = new Merchi("your-session-token");
const categoryToDelete = new merchi.Category();
categoryToDelete.id = 42;
categoryToDelete
.delete()
.then(() => {
console.log("ok, the category with id 42 was deleted.");
})
.catch((error: any) => {
console.error(
`Error: ${error.errorCode} ${error.statusCode} ${error.errorMessage}`
);
});
Entities in Merchi can have relationships with other entities. For instance, categories are always linked to a Domain, which is Merchi’s term for a store.
When creating an entity, you can associate a Domain object with it to specify which domain it should belong to. This relationship ensures that the new entity is correctly linked within the Merchi system.
Example: Creating a category with a domain
import { Merchi } from "merchi_sdk_ts";
const merchi = new Merchi("your-session-token");
const newCategory = new merchi.Category();
const existingDomain = new merchi.Domain();
existingDomain.id = 42; // note: this id already exists
newCategory.domain = existingDomain;
newCategory.name = "vegetables";
newCategory
.create()
.then(() => {
console.log("ok, new vegetables category created on domain 42");
})
.catch((error: any) => {
console.error(
`Error: ${error.errorCode} ${error.statusCode} ${error.errorMessage}`
);
});
By default, the get and list methods in Merchi only fetch scalar properties (e.g., strings, numbers, dates, etc.) of an entity. Nested entities (like relationships to other objects) are not automatically included. This behavior prevents excessive and unnecessary data transfer, especially for deeply nested or cyclic references.
However, if you need specific nested entities, you can use the embed parameter to request them. You can define which nested entities to include and to what depth.
null vs undefined
undefined: Indicates that the nested entity was not included in the fetch or has not been updated locally.null: Indicates that the nested entity does not exist on the server.On a newly fetched category category.domain will be undefined, even if the category has a domain on the server. undefined means that the domain has not been included, or updated locally. If the category did not have a domain at all, category.domain would instead be null.
This distinction allows you to determine whether the data was not fetched or the relationship does not exist.
Example: Fetching a Category with Embedded Domain
The following example uses the embed parameter to include the domain entity when fetching a Category. The embed parameter works with both get and list methods.
import { Merchi } from "merchi_sdk_ts";
const merchi = new Merchi();
const categoryId = 42;
const embed = {
domain: {},
};
merchi.Category.get(categoryId, { embed: embed })
.then((category: any) => {
const categoryDomainEmail = category.domain.emailDomain;
console.log(
`The email of the domain of category ${categoryId} is: ` + categoryDomain
);
})
.catch((error: any) => {
console.error(
`Error: ${error.errorCode} ${error.statusCode} ${error.errorMessage}`
);
});
The save method in Merchi not only saves changes to the entity itself but also automatically propagates any local changes made to attached nested entities.
This allows you to update related entities simultaneously, avoiding the need for multiple save calls.
Example: Updating a Store (Domain) through a Category
In the following example, we update the name of a Domain (store) that is linked to a Category object:
import { Merchi } from "merchi_sdk_ts";
const merchi = new Merchi("your-session-token");
const category = new merchi.Category();
category.id = 12; // assume we already have the entity id's
const domain = new merchi.Domain();
domain.id = 42;
category.domain = domain;
domain.domain = "new-store.example.com"; // newly chosen name
category
.save()
.then(() => {
console.log("ok, the category and domain are both updated.");
})
.catch((error: any) => {
console.error(
`Error: ${error.errorCode} ${error.statusCode} ${error.errorMessage}`
);
});
In Merchi’s TypeScript SDK, entity types and their constructors are intentionally separated to work around a TypeScript limitation. This separation ensures proper type checking and object instantiation.
When interacting with entities, you should always use the constructors attached to the Merchi object for creating or listing entities. The entity types, however, can be imported separately for static type declarations.
Merchi object and are responsible for creating and managing entities.Merchi object and reference the types for type annotations.Example: Importing Entity Type and Using the Constructor
Here’s an example demonstrating the correct way to handle entity types and constructors:
import { Merchi } from "merchi_sdk_ts";
import { Category } from "merchi_sdk_ts/src/entities/category";
const merchi = new Merchi();
const category: Category = new merchi.Category();
category.name = "groceries";
console.log(`Category name: ${category.name}`);
In systems with tens or even hundreds of thousands of entities, it is inefficient and impractical to fetch all results at once. Instead, the list method in the Merchi SDK returns results in manageable “pages.” This paginated approach ensures efficient data retrieval and avoids overwhelming the server or client application.
The list method offers two key options for controlling pagination:
limit
Example: A limit of 10 will return up to 10 entities per page.
offset
limit, you can access subsequent pages.Example:
limit to 10 and offset to 20 will fetch the third page of results (entities 21–30).When calling list, the response includes a metadata object (result.metadata) that provides information about the query results:
count: The number of entities returned for the current query.available: The total number of entities available in the system, as if the limit were infinite and offset were zero.This metadata is essential for building robust pagination mechanisms and validating the completeness of fetched data.
Example: Basic Pagination Usage
The following example retrieves the second page of Category entities, with 10 results per page:
pagination
import { Merchi } from "merchi_sdk_ts";
const merchi = new Merchi();
merchi.Category.list({ offset: 10, limit: 10 })
.then((result: any) => {
const categories = result.items;
const info = result.metadata;
console.log("Categories returned: " + info.count);
console.assert(categories.length === info.count, "oh no!");
console.log("Categories available: " + info.available);
})
.catch((error: any) => {
console.error(
`Error: ${error.errorCode} ${error.statusCode} ${error.errorMessage}`
);
});
In addition to pagination, the list method allows you to search or filter entities using the q parameter. This parameter enables keyword-based searches across multiple fields, such as names, descriptions, or even related categories.
q Parameter Worksq: Represents the search query. It filters entities to return only those that match the provided keyword(s) in one or more fields.Example: Searching for Products
The following example demonstrates how to search for products with a specific keyword, such as “egg”:
import { Merchi } from "merchi_sdk_ts";
const merchi = new Merchi();
const query = "product name";
merchi.Product.list({ q: query, embed: embedProduct, limit: 25 })
.then((result: any) => {
console.log("Search results for: " + query);
for (const product of result.items) {
console.log(product.name);
}
})
.catch((error: any) => {
console.error(
`Error: ${error.errorCode} ${error.statusCode} ${error.errorMessage}`
);
});
In addition to basic keyword search using the q parameter, the Merchi SDK provides powerful filtering capabilities that allow for fine-grained control over query results. Filters enable you to restrict results based on specific criteria, making it easier to locate entities that meet your exact needs.
inDomain: Limits results to entities associated with a specific domain.
Example: Retrieve only the products belonging to “example.com.”tags: Restricts results to entities tagged with specific keywords.
Example: Retrieve products tagged as "big" and "blue".For a full list of available filters for a given entity, refer to the official Filtering Reference).
Example: Filtering by Tags
This example demonstrates how to retrieve products that have specific tags applied:
import { Merchi } from "merchi_sdk_ts";
const merchi = new Merchi();
merchi.Product.list({ tags: ["big", "blue"] })
.then((result: any) => {
console.log("Search results: ");
for (const product of result.items) {
console.log(product.name);
}
})
.catch((error: any) => {
console.error(
`Error: ${error.errorCode} ${error.statusCode} ${error.errorMessage}`
);
});
Error handling is a critical part of any application, ensuring that issues are caught and dealt with appropriately. Since the Merchi SDK utilizes JavaScript Promises, errors are typically handled using catch() on a Promise. If an error occurs, the catch() method is triggered, providing an ApiError object that contains useful details about the error.
ApiError)The ApiError object is returned when an error occurs during an API call. This object contains several useful properties to help you understand and troubleshoot the issue:
errorCode: A unique code representing the error type.statusCode: The HTTP status code of the response (e.g., 404, 500).errorMessage: A descriptive message detailing the error.Basic Error Handling Example
Here’s how to catch and handle errors when calling the Merchi API:
import { Merchi } from 'merchi_sdk_ts';
import { ApiError } from 'merchisdk/typescript/src/request';
const merchi = new Merchi();
merchi.Category.get(42)
.then(() => {
console.log("ok, got a category");
})
.catch((error: ApiError) => {
console.error(`Error: ${error.errorCode} ${error.statusCode} ${error.errorMessage}`)
});
});
There are three key functions (getQuote, deduct, and bulkDeduct) within the Job class. These methods allow you to interact with Merchi’s API to fetch quotes, deduct inventory, and manage job-related operations.
getQuote MethodThis method fetches a specialized order estimate (quote) from the API based on the current job’s details. It updates the job’s cost and other relevant fields using the fetched data.
POST request to the /specialised-order-estimate/ endpoint.skip_rights, product_id) for debugging or customization.deduct MethodThis method deducts inventory items associated with the job. It sends a request to the /jobs/{jobId}/deduct/ endpoint, specifying the inventories to be deducted.
POST request to deduct specified inventories.MatchingInventory objects.embed parameter to fetch related entities for detailed responses.bulkDeduct MethodThis method automates the process of deducting all matching inventories associated with the job. It simplifies the deduction operation by using the deduct method internally.
matchingInventories is defined before proceeding.deduct method with all matchingInventories.matchingInventories is undefined.authenticatedFetchHandles API calls by adding authentication tokens and other common parameters.
toFormDataConverts the job’s internal state into a format suitable for form submission.
fromJsonUpdates the job’s fields using data fetched from the API.
Here’s a practical usage flow to demonstrate how these methods can work together:
Fetch a Quote:
const job = new merchi.Job();
job.fromJson({'quantity': 10, 'cost': 0, product: {'id': 1}});
job.getQuote().then(updatedJob => console.log('Updated cost:', updatedJob.cost));
Deduct Specific Inventories:
const inventory1 = new merchi.Inventory();
inventory1.id = 1;
const matchingInventory = new merchi.MatchingInventory();
matchingInventory.inventory = inventory1;
job.deduct([matchingInventory]).then(updatedJob => console.log('Deduction complete.'));
Bulk Deduct All Inventories:
job.matchingInventories = [matchingInventory];
job.bulkDeduct().then(() => console.log('Bulk deduction complete.'));
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.