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
id
to the entity. Merchi will automatically generate anid
for 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.authenticatedFetch
Handles API calls by adding authentication tokens and other common parameters.
toFormData
Converts the job’s internal state into a format suitable for form submission.
fromJson
Updates 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.