inmake DOCS
Docs / Server / Quick start

Quick start

What the package does and the smallest render server.

drop-core-server is the server-side companion to the SDK: it receives the platform's data hooks into a swappable cache, serves reads through a repository with local/remote modes, and renders published store pages from Handlebars templates. The pages in this section are extracted from the package README.

Server-side companion to drop-core-sdk. It turns the data hooks the backend already emits into a warm local cache, and serves reads from that cache first — falling back to core (public.content) only when needed.

Three cooperating pieces:

  1. Hook receiver — accepts the backend's hook POSTs (verifying the access-seed signature when configured; the deprecated access_seed spelling is still accepted — nginx drops underscore headers by default) and writes the payload into a cache. WebSite.receiveEventHook is the MULTI-EVENT ingress: one endpoint receives every subscribed kind (data / files / templates / elements / …) and dispatches on the x-drop-event header. An x-drop-updated ordering guard (a {key}#v sidecar in the data cache) drops out-of-order deliveries, so unordered best-effort pushes can never re-warm stale data. Full feed reference: the repo-root docs/hooks-and-caching.md.
  2. Cache — a swappable Cache<T> keyed by ${locale}/${collection}/${id} (MemoryCache by default; RenderCache for SSR HTML; DiskCache — a Node-only file-per-entry backend for RAM-poor fleets, imported directly from lib/cache/disk_cache).
  3. Repository — a unified read API with local / remote / preferLocal / preferRemote modes over the cache and core.

It re-exports everything from drop-core-sdk (and thus drop-core-lib), so one import gives you entity types, the SDK, and all server helpers.

Two entity types are owned here rather than by drop-core-lib, because the core API never types them — they are contracts between this renderer and the admin UI:

  • WebTemplateEntity (web/web_template.ts) — the web-builder's HTML/CSS artifact at store/{sid}/schemas/{schemaId}/templates/{version}, read by WebSite and listed/edited by the admin Code Studio.
  • TranslationsEntity (web/translations.ts) — one stored translations group document, the input the {{t}} helper's flattener turns into {group}.{leaf} keys; the admin's translations editor writes it.

Both come out of the same drop-core-server import as everything else.

Install & build#

npm install      # pulls drop-core-sdk (file:../sdk-js)
npm run build    # tsc → lib/

Build order in the monorepo: core-lib → sdk-js → sdk-server-js. express is an optional peer dependency (v4 or v5) — only needed if you use expressHookRouter.

Unit tests are node:test files beside the sources in src/web/ (compose_module.test.ts, …): npm run build && npm test runs node --test "lib/web/*.test.js" over the compiled output.

Quick start#

import {
  DropCore, getDropCoreInstance,
  MemoryCache, HookReceiver, expressHookRouter, DataRepository,
} from 'drop-core-server';
import express from 'express';

// DropCore.start() registers the session; build the instance separately —
// it carries the .public.content facade the repository reads through.
DropCore.start({ store: '*', access_token: TOKEN, target: 'instore' });
const instance = getDropCoreInstance();

const cache = new MemoryCache({ ttlMs: 5 * 60_000 });
const repo = new DataRepository({ cache, instance, mode: 'preferLocal', locale: 'cs_CZ' });
const receiver = new HookReceiver({ cache, privateKey: HOOK_KEY }); // omit privateKey to accept unverified

const app = express();
app.use('/data', expressHookRouter(receiver));   // backend pushes here → cache warms
app.get('/p/:id', async (req, res) => res.json(await repo.get('products', req.params.id)));

Configure one store data-hook per locale+collection pointing at POST /data/:locale/:collection (the receiver recovers the id from body.id / body.ref), or POST /data/:locale/:collection/:id if your hook URL includes the id. x-drop-locale / x-drop-collection / x-drop-id headers override the route params when present — and the router also mounts a header-driven POST / (the multi-event form: context from headers alone; non-data kinds no-op-accept there). Consumers wanting the FULL event feed (files/templates/elements evictions and app-level orders/invoices/forms) route the store's one multi hook at WebSite.receiveEventHook instead — see "Multi-event hook ingress" below.