@shopimind/sdk-shopimind
v1.0.3
Published
Official JavaScript/TypeScript SDK for the Shopimind API. Mirrors the PHP SDK (shopimind/sdk-shopimind) feature-for-feature.
Readme
@shopimind/sdk-shopimind
SDK officiel JavaScript / TypeScript pour l'API Shopimind.
- Node.js ≥ 14
- CommonJS (
require) et ESM / TypeScript (import) - Types
.d.tsinclus - Dépendance runtime unique :
axios - Retry automatique, chunking optionnel, format de retour uniforme (envelope)
Sommaire
- Installation
- Quick start
- Format de réponse — envelope uniforme
- Lire / récupérer des données (GET)
- Écrire des données (create / update / delete / bulk)
- Retry automatique
- Chunking optionnel
- Helpers partagés
- Configuration du client
- Enregistrer une shop
- Valider un webhook entrant
- Ressources disponibles
- TypeScript
- Périmètre couvert
Installation
Via registre
# npm
npm install @shopimind/sdk-shopimind
# yarn
yarn add @shopimind/sdk-shopimind
# pnpm
pnpm add @shopimind/sdk-shopimindPour un registre privé (GitHub Packages, Verdaccio, Nexus), router le scope @shopimind dans le .npmrc du projet consommateur :
@shopimind:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}Ne jamais committer un token : référencez une variable d'environnement (
${GITHUB_TOKEN}) ou placez le token dans le~/.npmrcutilisateur.
Via Git (sans registre)
Épinglez toujours un tag pour garantir une version reproductible :
yarn add git+https://github.com/shopimind/shopimind-api-js-sdk.git#v1.0.2
# ou
npm install git+https://github.com/shopimind/shopimind-api-js-sdk.git#v1.0.2Via chemin local (développement)
yarn add file:../shopimind-api-js-sdk
# ou, pour itérer en live :
# cd shopimind-api-js-sdk && yarn link
# cd ../mon-projet && yarn link @shopimind/sdk-shopimindQuick start
const { SpmClient, SpmCustomers } = require('@shopimind/sdk-shopimind');
const client = SpmClient.getClient('v1', process.env.SHOPIMIND_API_KEY);
// Écrire (synchronisation)
const res = await SpmCustomers.bulkSave(client, customers, { chunk: true });
if (!res.ok) {
console.error('Sync failed', res.error);
return;
}
console.log(`Synced ${res.data.sent_count}, rejected ${res.data.rejected_count}`);
// Lire
const page = await SpmCustomers.list(client, { limit: 50 });
console.log(page.data);En TypeScript / ESM :
import { SpmClient, SpmCustomers } from '@shopimind/sdk-shopimind';Format de réponse — envelope uniforme
Toutes les méthodes retournent le même shape, succès ou échec. Le SDK ne throw jamais pour une erreur HTTP.
{
ok: true | false,
statusCode: 200 | 4xx | 5xx | 0,
data: {/* body de l'API */} | null,
error: null | {
message: string,
code: string, // 'HTTP_503', 'ETIMEDOUT', etc.
retryable: boolean,
attempts: number, // combien de tentatives effectuées
details?: any, // pour les aggregated errors (chunk failures)
},
}const res = await SpmCustomers.bulkSave(client, items);
if (res.ok) {
console.log(res.data.sent_count, res.data.rejected_count);
} else if (res.error.retryable) {
console.warn('Transient failure, API is unavailable');
} else {
console.error('Permanent failure', res.error.message);
}Pour les routes de lecture, le payload de l'API est dans
res.data(souventres.data.datapour un élément unique, selon l'endpoint).
Lire / récupérer des données (GET)
Les ressources de données exposent des méthodes de lecture statiques qui renvoient l'envelope standard.
const { SpmCustomers, SpmOrders, SpmCustomersAddresses } = require('@shopimind/sdk-shopimind');
// Liste paginée — la query est sérialisée en query-string
const page = await SpmCustomers.list(client, { limit: 50, offset: 0 });
// Un élément par identifiant (+ projection de champs optionnelle)
const customer = await SpmCustomers.get(client, 'cust_1', ['email', 'first_name']);
const order = await SpmOrders.get(client, 'ord_123');
const byRef = await SpmOrders.getByReference(client, 'CMD-2024-0001');
// Sous-ressources d'un parent
const addresses = await SpmCustomersAddresses.list(client, 'cust_1', { limit: 20 });
const groups = await SpmCustomers.listGroups(client, 'cust_1');Méthodes de lecture par ressource :
| Ressource | Lecture |
|---|---|
| SpmCustomers | get(client, id, fields?) · list(client, query?) · listGroups(client, customerId, query?) |
| SpmCustomersAddresses | list(client, customerId, query?) |
| SpmCustomersGroups, SpmProducts, SpmProductsCategories, SpmProductsManufacturers, SpmOrdersCarriers, SpmOrdersStatuses, SpmVouchers, SpmCarts | list(client, query?) |
| SpmOrders | get(client, orderId, fields?) · getByReference(client, reference, fields?) · list(client, query?) |
SpmProductsImagesetSpmProductsVariationsn'ont pas de route GET côté API (écriture uniquement).
Écrire des données (create / update / delete / bulk)
Deux styles selon le besoin.
Statique — opérations bulk (recommandé pour la synchro)
await SpmCustomers.bulkSave(client, items, { chunk: true });
await SpmCustomers.bulkUpdate(client, items, { chunk: true });
await SpmCustomers.delete(client, 'cust_1');
await SpmCustomers.bulkDelete(client, ['cust_1', 'cust_2']);Instance — un item à la fois
const { SpmCustomers } = require('@shopimind/sdk-shopimind');
const customer = new SpmCustomers(client);
customer.customer_id = 'cust_1';
customer.email = '[email protected]';
customer.first_name = 'Foo';
customer.last_name = 'Bar';
customer.is_opt_in = true;
customer.is_newsletter_subscribed = false;
customer.lang = 'fr';
customer.is_active = true;
customer.created_at = '2026-01-01T00:00:00Z';
customer.updated_at = '2026-01-01T00:00:00Z';
const res = await customer.save(); // ou .update()Ressources imbriquées (addresses, images, variations)
Le constructeur prend le parent ; les méthodes statiques prennent le parent en 2ᵉ argument :
const { SpmCustomersAddresses } = require('@shopimind/sdk-shopimind');
await SpmCustomersAddresses.bulkSave(client, 'cust_1', addresses, { chunk: true });
await SpmCustomersAddresses.bulkDelete(client, 'cust_1', [42, 43]);Endpoint flat bulkSaveAll
Payload plat avec le parent embarqué dans chaque item (batch multi-parents) :
await SpmCustomersAddresses.bulkSaveAll(client, [
{ customer_id: 'cust_1', address_id: 1, /* ... */ },
{ customer_id: 'cust_2', address_id: 2, /* ... */ },
], { chunk: true });Associer un data_source
Toutes les ressources de synchronisation acceptent optionnellement un identifiant ou libellé de source de données :
customer.id_data_source = 3;
customer.source_label = 'Magasin Paris';
await customer.save();Retry automatique
Par défaut, le SDK retry 3 fois sur :
- HTTP
408,429,500,502,503,504 - Erreurs réseau :
ECONNRESET,ETIMEDOUT,ECONNREFUSED,ENETUNREACH,ENOTFOUND,EAI_AGAIN
Avec un backoff exponentiel (1s → 2s → 4s avec jitter ±500ms, cap à 10s).
// Personnaliser
const client = SpmClient.getClient('v1', apiKey, {
retry: {
maxRetries: 5,
backoffBaseMs: 2000,
backoffCapMs: 30000,
retryableStatus: [429, 502, 503, 504],
},
});
// Désactiver
const client = SpmClient.getClient('v1', apiKey, { retry: false });Chunking optionnel
Passez un gros tableau au SDK : il le découpe en batches alignés sur la limite de l'API et agrège les résultats.
const hugeArray = Array(500).fill().map((_, i) => ({ /* customer data */ }));
const res = await SpmCustomers.bulkSave(client, hugeArray, { chunk: true });
console.log(res.data.sent_count); // total agrégé
console.log(res.data.chunks); // détail par chunk (10 × 50)Par défaut, chunkSize vaut la valeur alignée sur l'API NestJS :
| Ressource | CHUNK_SIZE |
|---|---|
| SpmCustomDataRecords | 20 |
| SpmCustomers, SpmCustomersAddresses, SpmProducts, SpmProductsImages, SpmProductsVariations, SpmOrders, SpmVouchers | 50 |
| SpmCustomersGroups, SpmProductsCategories, SpmProductsManufacturers, SpmOrdersCarriers, SpmOrdersStatuses | 100 |
await SpmProducts.bulkSave(client, items, { chunk: true, chunkSize: 25 }); // override
console.log(SpmCustomers.CHUNK_SIZE); // 50Helpers partagés
const { SpmHelpers } = require('@shopimind/sdk-shopimind');
SpmHelpers.chunk(array, 50);
// → [[...50], [...50], ...]
SpmHelpers.mergeResponses([envelope1, envelope2, ...]);
// → envelope agrégée avec data.sent_count, data.rejected_count, data.chunks[]
SpmHelpers.extractCounts(envelope);
// → { sent: N, rejected: M, failed: K }
SpmHelpers.isRetryable(error);
// → true si l'erreur ou l'envelope est retryable (429 / 5xx / network)
SpmHelpers.formatError(envelope);
// → { message, code, statusCode, retryable, attempts, details }
// safe à sérialiser pour des logs structurésConfiguration du client — toutes les options
const client = SpmClient.getClient('v1', 'your-api-key', {
baseUrl: 'https://core-staging.shopimind.com',
headers: { 'x-trace-id': 'abc-123' },
timeout: 60000,
labelSource: 'web',
retry: { maxRetries: 3 },
});| Option | Type | Défaut | Rôle |
|---|---|---|---|
| baseUrl | string | https://core.shopimind.com | URL de base de l'API |
| headers | Record<string, string> | {} | Headers additionnels |
| timeout | number | 30000 | Timeout de requête (ms) |
| labelSource | string \| null | 'web' | Injecté en label_source dans les POST/PUT de synchro. null désactive. |
| retry | SpmRetryOptions \| false | Voir Retry automatique | Politique de retry |
L'URL de base peut aussi être surchargée via la variable d'environnement SHOPIMIND_CORE_API_BASE. Priorité : options.baseUrl > SHOPIMIND_CORE_API_BASE > défaut.
Headers nécessaires pour shop/connection
L'endpoint POST /shop/connection attend trois headers spécifiques : client-id, client-version, current-build. Les passer via options.headers :
const client = SpmClient.getClient('v1', apiKey, {
headers: {
'client-id': clientId,
'client-version': '5.0.0',
'current-build': '1',
},
});Enregistrer une shop
const { SpmShopConnection } = require('@shopimind/sdk-shopimind');
const res = await SpmShopConnection.saveConfiguration(client, {
default_currency: 'EUR',
default_lang: 'fr',
langs: ['fr', 'en'],
timezone: 'Europe/Paris',
url_client: 'https://myshop.example.com',
ecommerce_version: '2.7.0',
module_version: '5.0.0',
});Valider un webhook Shopimind entrant
const { SpmRequestValidator } = require('@shopimind/sdk-shopimind');
const validation = SpmRequestValidator.validateRequest({
clientId: req.headers['shopimind-client-identifiant'],
hmacToken: req.headers['shopimind-token'],
body: req.body,
apiIdentification: config.apiIdentification,
apiPassword: config.apiPassword,
});
if (!validation.valid) {
return res.status(401).json({ error: validation.error });
}Ressources disponibles
Le SDK couvre les endpoints publiés dans la documentation Swagger de l'API, avec deux exceptions conservées volontairement bien que masquées de la doc : POST shop/connection (connexion boutique) et data-sources (sources de données).
| Export | Endpoint | Méthodes |
|---|---|---|
| SpmShopConnection | shop/connection | saveConfiguration |
| SpmCustomers | customers | CRUD + bulk + get / list / listGroups |
| SpmCustomersAddresses | customers/:id/addresses + customers/addresses/bulk | CRUD + bulk + bulkSaveAll + list |
| SpmCustomersGroups | customers-groups | CRUD + bulk + list |
| SpmProducts | products | CRUD + bulk + list |
| SpmProductsCategories | products-categories | CRUD + bulk + list |
| SpmProductsImages | products/:id/images + products/images/bulk | CRUD + bulk + bulkSaveAll |
| SpmProductsManufacturers | products-manufacturers | CRUD + bulk + list |
| SpmProductsVariations | products/:id/variations + products/variations/bulk | CRUD + bulk + bulkSaveAll |
| SpmOrders | orders | CRUD + bulk + get / getByReference / list |
| SpmOrdersCarriers | orders-carriers | CRUD + bulk + list |
| SpmOrdersStatuses | orders-statuses | CRUD + bulk + list |
| SpmVouchers | vouchers | CRUD + bulk + list |
| SpmDataSources | data-sources | list / create / update / delete |
| SpmIntegrationConfig | integration-config | get / set — config posée par l'intégrateur (par boutique), résolue au rendu en {integration.<key>} |
| SpmCustomDataDefinitions | custom-data-definitions | list / get / create / update (= extend) / extend / activate / deactivate / delete / listOverrides / updateOverrides |
| SpmCustomDataRecords | custom-data-records/:definitionId | bulkSave (chunk 20) / list / update / delete / bulkDelete (chunk 20) |
| SpmContacts | contacts, contact-consent-history, contact-messages-reject | get / list / listLists / listTags / listCustomDataDefinitions / listConsentHistory / listMessagesReject |
| SpmEvents | events | create / update / list / listHistories / get / delete / trigger |
| SpmCarts | carts | list (lecture seule — voir note) |
| SpmLists | lists | list / get / create / update / delete / listContacts |
| SpmCustomKpi | custom-stats/kpis | list / get / listSources / listFields / create / update / delete / preview |
SpmDataSources,SpmIntegrationConfig,SpmCustomDataDefinitions,SpmCustomDataRecords,SpmContacts,SpmEvents,SpmLists,SpmCustomKpi(ainsi que toutes les méthodes de lecture) exposent des méthodes statiques(client, …)— pas d'instanciation. Toutes retournent l'envelope standard.
Config intégrateur (SpmIntegrationConfig)
Un champ de config_schema déclaré owner: "integrator" est invisible côté marchand : c'est l'intégrateur qui pose sa valeur par boutique, avec sa clé API, et elle est résolue au rendu en {integration.<key>} (avec le default déclaré comme repli tant qu'elle n'est pas posée).
const { SpmIntegrationConfig } = require('@shopimind/sdk-shopimind');
// Poser des valeurs (clés owner:"integrator" uniquement)
await SpmIntegrationConfig.set(client, { tracking_base: 'https://t.partner.io/p', account_ref: 'acct_42' });
// Relire (valeurs posées + defaults déclarés)
const res = await SpmIntegrationConfig.get(client);
if (res.ok) console.log(res.data.data.values); // { tracking_base: '…', account_ref: '…' }
label_sourcen'est injecté que sur les endpoints de synchronisation de données (customers / products / orders / vouchers / addresses / images / variations…). Les routes de configuration et d'administration (shop/connection,data-sources,custom-data-*,events,lists,custom-stats,contacts,carts) ne le reçoivent pas — sinon leur validation stricte rejetterait le champ comme inconnu.
Paniers en lecture seule : l'API publique n'expose aucune route d'écriture pour les paniers (ils sont dérivés du tracking front et des
cart_iddes commandes). Poussez des commandes viaSpmOrders.
Consentement : l'historique de consentement (EMAIL / SMS / WHATSAPP / PUSH) est en lecture seule via
SpmContacts.listConsentHistory. L'API publique n'expose pas de route SET de consentement.
Hors périmètre clé API : les contrôleurs
widgetsetintegrationsutilisent un Bearer JWT (AuthGuard('jwt')), pas la cléspm-api-keyde ce SDK — ils ne sont donc pas couverts.
TypeScript
Les types sont dans index.d.ts et importables directement :
import {
SpmClient,
SpmCustomers,
SpmCustomerData,
SpmEnvelope,
SpmChunkedEnvelope,
SpmHelpers,
} from '@shopimind/sdk-shopimind';
const client = SpmClient.getClient('v1', process.env.SHOPIMIND_API_KEY!);
const items: SpmCustomerData[] = [/* ... */];
const res: SpmEnvelope | SpmChunkedEnvelope = await SpmCustomers.bulkSave(client, items, { chunk: true });
if (res.ok) {
const counts = SpmHelpers.extractCounts(res);
console.log(`${counts.sent} envoyés, ${counts.rejected} rejetés`);
}Interfaces data disponibles : SpmCustomerData, SpmCustomerAddressData, SpmBulkCustomerAddressData, SpmCustomerGroupData, SpmProductData, SpmProductCategoryData, SpmProductImageData, SpmBulkProductImageData, SpmProductManufacturerData, SpmProductVariationData, SpmBulkProductVariationData, SpmOrderData, SpmOrderProduct, SpmOrderCustomer, SpmOrderCarrierData, SpmOrderStatusData, SpmVoucherData, SpmShopConnectionData, SpmDataSourceData, SpmCreateDataSourceData, SpmEnvelope, SpmChunkedEnvelope, SpmBulkOptions, SpmRetryOptions.
Périmètre couvert
Le SDK reflète le contrat public de l'API (documentation Swagger), plus deux exceptions conservées volontairement : POST shop/connection (connexion boutique) et data-sources (sources de données).
Couvert
Synchronisation e-commerce (customers, products, orders, vouchers… et leurs sous-ressources), lecture (get / list / getByReference / listGroups), custom data (definitions & records), contacts & historiques (consent / messages-reject), events, lists, custom KPIs, carts (lecture seule), data sources.
Hors périmètre (volontaire)
Les endpoints d'administration masqués de la doc Swagger (@ApiExcludeController) ne sont pas exposés par le SDK : campaigns, templates, stats, domains, webhooks, job/import, export, users/sub-users, list-actions, newsletter-subscribers. Ils relèvent de l'interface d'administration (authentification Bearer JWT, pas la clé spm-api-key) ; pour les consommer, appelez l'API directement avec un Bearer JWT.
Ces endpoints d'administration n'ont jamais fait partie d'une version publiée du SDK (la dernière publiée,
1.0.1, ne les exposait pas) — leur absence n'est donc pas une rupture de compatibilité. Côté consentement : pas de route SET publique ; (dés)abonnement géré côté admin, historique lisible viaSpmContacts.listConsentHistory.
License
Propriétaire — Shopimind.
