The OrderCloud SDK for Javascript is a modern client library for building solutions targeting the Ordercloud eCommerce API. The SDK aims to greatly improve developer productivity and reduce errors.
- β¨ Features
- Requirements
- βοΈ Installation
- β Adding it to your project
- π Authentication
- π Filtering
- π¬ Impersonation
- Configuration
- Handling Errors π
- Request and response hooks
- Cancelling Requests
- Async/Await
- Typescript Support
- π License
- π€ Contributing
- π Getting Help
- Works both on the browser and node.js
- ESM module available for bundlers that support it. This enables tree shaking - use only what you import.
- Built-in Typescript support, no additional types package necessary
- Full feature parity with API
- Auto-generated API reference
Coming from an older version? Check out the migration guide so you can upgrade to the latest and greatest.
The SDK sends HTTP with the platform fetch implementation. It runs in browsers and Node versions that provide global fetch. No HTTP library is installed with the SDK.
with npm:
npm install ordercloud-javascript-sdk --saveor
with yarn:
yarn add ordercloud-javascript-sdkThis is the preferred method of importing the sdk for browser projects as it allows modern bundlers like webpack to tree shake the parts of the SDK that you aren't using, making your project more lean.
import { Products } from 'ordercloud-javascript-sdk';import * as OrderCloudSDK from 'ordercloud-javascript-sdk';const OrderCloudSDK = require('ordercloud-javascript-sdk');We'll need to get a token before we can make any API calls. The SDK offers five different ways of getting a token as part of the Auth Resource.
We'll use the login method for this example.
import { Auth, Tokens } from 'ordercloud-javascript-sdk';
const username = 'YOUR_USERNAME'; //username of the user logging in
const password = 'YOUR_PASSWORD'; //password of the user logging in
const clientID = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'; //clientID of the application the user is logging in to ([sign up for free](https://portal.ordercloud.io/register)
const scope = ['FullAccess']; //string array of [roles](https://ordercloud.io/knowledge-base/security-profiles) the application has access to
Auth.Login(username, password, clientID, scope)
.then(response => {
//store token, now any subsequent calls will automatically set this token in the headers for you
const token = response.access_token;
Tokens.SetAccessToken(token)
})
.catch(err => console.log(err));
// Custom roles are optional and belong on the request options object.
// ElevatedLogin, ClientCredentials, and Anonymous use the same shape.
// Anonymous also accepts anonuserid on that object.
Auth.Login(username, password, clientID, scope, {
customRoles: ['InventoryAdmin'],
})All of the filtering options you love from the API are available through the SDK as well. Simply build an object that matches the model of the item you're filtering on where the value is the value you'd like to filter on.
Let's run through a couple scenarios and what the call will look like with the SDK:
My products where xp.Featured is true
Me.ListProducts({ filters: { 'xp.Featured': true } })
.then(productList => console.log(productList));My orders submitted after April 20th, 2018
Me.ListOrders({ filters: { DateSubmitted: '>2020-04-20' } })
.then(orderList => console.log(orderList))Users with the last name starting with Smith:
Users.List('my-mock-buyerid', { filters: { LastName: 'Smith*' } })
.then(userList => console.log(userList));Users with the last name starting with Smith or users with the last name ending with Jones
Users.List('my-mock-buyerid', { filters: { LastName: 'Smith*|*Jones' } })
.then(userList => console.log(userList));Products where xp.Color is not red and not blue
Products.List({ filters: { 'xp.Color': ['!red', '!blue'] } })
.then(productList => console.log(productList));And of course you can mix and match filters to your heart's content.
Impersonation allows a seller user to make an API call on behalf of another user. The SDK enables this in two ways, each tackling different use cases.
The first method we'll talk about is best suited when you need to toggle between just two users during a session. You'll simply get an impersonation token, set it and then use the As() method available on every service to flag the SDK that you want to use the the stored token for that call.
import { Tokens, Me } from 'ordercloud-javascript-sdk';
// set regular token
const myToken = 'YOUR_TOKEN';
Tokens.SetAccessToken(myToken);
// set impersonation token
const myImpersonationToken = 'YOUR_IMPERSONATED_TOKEN'
Tokens.SetImpersonationToken(myImpersonationToken);
// Get products for regular user
Me.ListProducts()
.then(productList => console.log(productList))
// Get products for the impersonated user
Me.As().ListProducts()
.then(impersonatedProductList => console.log(impersonatedProductList))As you can see this method makes it very easy to toggle between impersonated calls and non-impersonated calls. But what if you have more than two tokens to toggle between? To address that scenario we recommend using the optional requestOptions.accessToken parameter. requestOptions is available as the last parameter on all sdk methods.
import { Me } from 'ordercloud-javascript-sdk';
var token1 = 'USER1_TOKEN';
var token2 = 'USER2_TOKEN';
var token3 = 'USER3_TOKEN';
// Get products for user 1
Me.ListProducts(null, { accessToken: token1 })
.then(user1ProductList => console.log(user1ProductList))
// Get products for user 2
Me.ListProducts(null, { accessToken: token2 })
.then(user2ProductList => console.log(user2ProductList))
// Get products for user 3
Me.ListProducts(null, { accessToken: token3 })
.then(user3ProductList => console.log(user3ProductList))The Configuration service can be used to set sdk level options.
Simply set the options you need to override and the SDK will merge it with the default options object.
import { Configuration } from 'ordercloud-javascript-sdk';
Configuration.Set({
// This URL can be found on your dashboard or in the upper right hand context menu of your OrderCloud application in Sitecore Portal.
baseApiUrl: 'https://sandboxapi.ordercloud.io',
timeoutInMilliseconds: 20 * 1000
})Similarly, you can see what the current options are by using the getter.
import { Configuration } from 'ordercloud-javascript-sdk';
const configuration = Configuration.Get();
console.log(configuration); // the current sdk configurationThe SDK uses a custom error (OrderCloudError) to provide rich and useful information in the case of an error.
Products.Get('my-product')
.catch(error => {
if(error.isOrderCloudError) {
// the request was made and the API responded with a status code
// that falls outside of the range of 2xx, the error will be of type OrderCloudError
// https://ordercloud-api.github.io/ordercloud-javascript-sdk/classes/orderclouderror
console.log(error.message);
console.log(JSON.stringify(error.errors, null, 4));
} else if (error.isCancellation) {
// the caller aborted the request
console.log(error.message);
} else if (error.isTimeout) {
// the request exceeded timeoutInMilliseconds
console.log(error.message);
} else {
// Something happened in setting up the request that triggered an Error
console.log('Error', error.message);
}
})onRequest and onResponse replace axios interceptors. Set them once with Configuration.Set. onRequest runs before the call is sent and returns the request to send. onResponse runs after the body is read and before the SDK returns data or throws OrderCloudError.
The request has url, method, headers, body, and signal. The response has data, status, statusText, headers, and request.
import { Configuration } from 'ordercloud-javascript-sdk';
Configuration.Set({
onRequest(request) {
request.headers['X-Request-Id'] = 'abc'
return request
},
onResponse(response) {
console.log(response.status, response.request.url)
return response
},
})requestOptions.signal accepts an AbortSignal. Use it to drop a request when the result is no longer needed, for instance when a react component unmounts.
import { Products, isCancellation } from 'ordercloud-javascript-sdk';
const controller = new AbortController();
Products.List({search: 'Tennis balls'}, {signal: controller.signal})
.catch(ex => {
if (isCancellation(ex)) {
console.log(ex) // 'This request was cancelled!'
} else {
console.log(ex) // Normal ordercloud exception
}
})
controller.abort()Pass a fetch function to the Configuration service to mock, cache, or reroute HTTP. The default is the platform global fetch.
import { Configuration } from 'ordercloud-javascript-sdk';
Configuration.Set({
baseApiUrl: 'https://sandboxapi.ordercloud.io',
timeoutInMilliseconds: 20 * 1000,
fetch: myFetch
})Async/Await is a special syntax to work with promises in a more comfortable fashion. Because the SDK is built with promises the syntax works right out of the box - simply add the async keyword to your outer function method.
// with normal promises
(() => {
Auth.Login('myusername', 'mypassword', 'myclientID', ['FullAccess'])
.then(authResponse => {
Tokens.SetAccessToken(authResponse.access_token)
Me.ListOrders().then(orderList => {
const firstOrder = orderList.Items[0]
console.log(firstOrder.Total)
})
})
.catch(err => {
console.log(err)
})
})()// with async/await
(async () => {
try {
const authResponse = await Auth.Login('myusername', 'mypassword', 'myclientID', ['FullAccess'])
Tokens.SetAccessToken(authResponse.access_token)
const myOrders = await Me.ListOrders()
const firstOrder = myOrders.Items[0]
} catch (err) {
console.error(err)
}
})()NOTE: async/await is part of ECMAScript 2017 and is not supported in Internet Explorer and older browsers without first transpiling to ES5 so use with caution.
While Typescript is not required to use this project (we compile it down to ES5 javascript for you), it does mean there are some added benefits for our Typescript users. You will need a minimum of typescript version 3.5 for all features to work correctly.
By default, properties of ordercloud models are required if their Create or Save operation requires them. For example the LineItem model has the properties ProductID and Quantity required. This is important to know if you need to define an object by type before using it.
import { LineItems, LineItem } from 'ordercloud-javascript-sdk';
const lineItem: LineItem = {
ProductID: 'my-awesome-product', // if this field is missing you get a type error!
Quantity: 2 // if this field is missing you get a type error!
}
LineItems.Create('Outgoing', 'my-order-id', lineItem)This works as expected and ensures a create or save always has the correct required parameters. However, if for example you need to perform a Patch operation (partial update), then you want all of the fields to be optional. To accomplish this you should use Typescript's built-in utility type Partial<T>
import { LineItems, LineItem } from 'ordercloud-javascript-sdk';
const lineItem: Partial<LineItem> = {
// no type errors even though Quantity and ProductID are missing
ShippingAddressID: 'my-shipping-address-id'
}
LineItems.Patch('Outgoing', 'my-order-id', 'my-lineitem-id', lineItem)Extended properties, or xp, is a platform feature that allows you to extend the OrderCloud data model. This is modeled in the SDK using (by default) a Typescript any type:
const category: Category = {};
category.xp.Featured = true;Even though Featured does not exist on the native model, the above code will compile and work just fine with the API. But you don't get any compile-time type-checking.
Alternatively, the SDK provides generic versions of all models that allow you to provide a custom xp type:
interface MyCategoryXp {
Featured?: boolean;
}
const category: Category<MyCategoryXp> = {};
category.xp.Featured = true; // strongly typed!These custom models can then be used when calling any method in the SDK
Categories.List<Category<MyCategoryXp>>('mock-catalog-id')
.then(categoryList => {
const firstCategory = categoryList.Items[0];
const isFeatured = firstCategory.xp.Featured; // strongly typed!
})A common alternative to the above example is to first define a custom interface that extends Category<MyCategoryXp>
interface MyCategoryXp {
Featured?: boolean;
}
interface MyCategory extends Category<MyCategoryXp> {
}
Categories.List<MyCategory>('mock-catalog-id')
.then(categoryList => {
const firstCategory = categoryList.Items[0];
const isFeatured = firstCategory.xp.Featured; // strongly typed!
})A model's own xp is still the first type parameter, so Category<MyCategoryXp> and Order<MyOrderXp> keep working. Nested models are passed as the extended type, not as a separate xp parameter for each nested field:
interface MyOrderXp {
ErpId?: string;
}
interface MyOrderUser extends OrderUser<{ Department?: string }> {}
interface MyAddress extends Address<{ Dock?: string }> {}
interface MyOrder extends Order<MyOrderXp, MyOrderUser, MyAddress> {}
interface MyLineItemXp {
GiftWrap?: boolean;
}
interface MyLineItem extends LineItem<MyLineItemXp, LineItemProduct, LineItemVariant, MyAddress, MyAddress> {}
type Worksheet = OrderWorksheet<MyOrder, MyLineItem>Order<MyOrderXp, MyFromUserXp> does not compile: the second parameter is the user model, not that user's xp. OrderWorksheet<SomeXp> does not compile: the first parameter is TOrder extends Order.
The sdk ships with various helpers and utilities that you may find useful. We also recommend using Typescript's built-in utilities when possible.
| Utility | Description |
|---|---|
ListPage<T> |
Takes in a type for the item in the list. For example ListPage<Order> will be the type for an order list page. |
ListPageWithFacets<T> |
Similar to ListPage but for premium search models. For example ListPageWithFacets<Product> will be the type for a product list page. |
Searchable<T> |
Takes in a SearchableEndpoint and returns the type for a valid searchOn field on list calls. For example Searchable<'Orders.List'>. |
Sortable<T> |
Takes in a SortableEndpoint and returns the type for a valid sortBy field on list calls. For example Sortable<'Orders.List'>. |
Filters<T> |
Takes in an ordercloud model and returns the type for a valid filter field on list calls. For example Filters<Product>. This also works for any custom models that extend an OrderCloud model, for example Filters<MyProduct>. |
PartialDeep<T> |
Similar to Typescript's Partial<T> except works on nested properties as well. |
RequiredDeep<T> |
Similar to Typescript's Required<T> except works on nested properties as well. |
DecodedToken |
A type representing a decoded OrderCloud token |
MessageSenderPayloads |
Takes in a type argument for the message type and resolves to the type for the message sender payload |
OrderCloud's Javascript SDK is an open-sourced software licensed under the MIT license.
Check out our Contributing guide.
If you're new to OrderCloud, exploring the documentation is recommended, especially the Welcome to OrderCloud tutorial.
For programming questions, please ask on Stack Overflow.
To report a bug or request a feature specific to the SDK, please open an issue.