Skip to Content

Services

All business logic lives in src/lib/server/services/. Each service is a singleton class.

Services import db from the database seam (src/lib/server/db/index.ts) rather than opening a connection themselves. The seam resolves to a local better-sqlite3 database on the Node.js target and to the D1 binding on Cloudflare, so the same service code runs on both targets. See Database.

Product Service

Manages products and their variants.

import { productService } from "$lib/server/services/products"; // Get by ID or slug — returns resolved flat fields const product = await productService.getById(123); const product = await productService.getBySlug("blue-shirt"); product.name; // "Blue Shirt" (default language, directly from products table) // List with pagination const { items, total } = await productService.list({ limit: 20 }); // Search const results = await productService.list({ search: "shirt" }); // CRUD await productService.create({ slug: "new-product" /* ... */ }); await productService.update(123, { /* ... */ }); await productService.delete(123);

Variant operations (stock, pricing, SKU management) are also handled through productService.

Order Service

Manages the full order lifecycle, including cart operations.

import { orderService } from "$lib/server/services/orders"; // The cart is a cookie — an order row is created only when checkout starts, // snapshotting the cookie lines into order lines with 15-min stock reservations. const { order, checkoutToken } = await orderService.startCheckout(cartLines, { customerId, checkoutToken }); // State transitions (all go through transitionState) await orderService.transitionState(orderId, "payment_pending"); await orderService.transitionState(orderId, "paid"); await orderService.transitionState(orderId, "shipped"); await orderService.transitionState(orderId, "delivered"); await orderService.transitionState(orderId, "cancelled"); // Checkout await orderService.setShippingAddress(orderId, address); await orderService.setCustomerEmail(orderId, email); await orderService.updateTotals(orderId); // Query const order = await orderService.getByCode("ORD-EJXEDYIX");

Customer Service

import { customerService } from "$lib/server/services/customers"; // Create from auth user const customer = await customerService.getOrCreateFromAuth(authUser); // Query const customer = await customerService.getByEmail("user@example.com");

Translation Service

Manages translations for all 7 translatable entities. Uses Drizzle’s onConflictDoUpdate for upserts.

import { translationService } from "$lib/server/services/translations"; // Get translations for an entity const rows = await translationService.getProductTranslations(productId); // Upsert a translation (insert or update) await translationService.upsertProductTranslation(productId, "fi", { name: "Sininen paita", slug: "sininen-paita", description: "<p>Hieno paita</p>" }); // Bulk methods for list pages const allCategoryTranslations = await translationService.getAllCategoryTranslations(); const facetValueTranslations = await translationService.getAllFacetValueTranslations(facetId);

See Localization for the full API and admin UI integration.

Other Services

See src/lib/server/services/ for all available services including:

  • categoryService — Category tree, breadcrumbs, and product assignment
  • facetService — Facets and facet values (filterable attributes)
  • collectionService — Product collections (rule-based)
  • taxService — Tax rate lookups and calculations
  • paymentService — Payment provider orchestration
  • shippingService — Shipping provider orchestration
  • promotionService — Discount and promotion rules
  • reservationService — Stock reservations
  • customerGroupService — Customer group management
  • contentPageService — CMS content pages
  • translationService — Multi-language translations for all entities
  • assetService — Image/file management
  • relatedProductService — Related-product links
  • digitalDeliveryService — Delivery of digital products after payment
  • reviewService — Product reviews

Product search and reindexing live in product-search.ts, exposed as functions (listProducts, quickSearchProducts, reindexProduct, reindexAll, …) rather than a class.

The cart is a cookie, not a service singleton, and the wishlist is likewise just a cookie of product ids — there is no wishlistService. See Cart and Wishlists.

Promotion math

All promotion discount math is centralized in OrderService.computePromotionDiscount — coupon codes, automatic promotions, and totals recalculation all route through it, so the applied discount is computed the same way everywhere. See Promotions.

Emitting events

Services and routes record out-of-band work by calling emitEvent(type, payload) (a durable outbox insert) rather than doing it inline — e.g. the checkout completion emits order.paid. A scheduled drain processes these with retry. See Integrations.

MCP server

Hoikka ships a local MCP server (pnpm mcp, defined in scripts/mcp.ts and registered in .mcp.json) that exposes schema, catalog, and order tools to AI assistants over stdio. Point an MCP-capable client at it to let an agent inspect the schema and query products and orders directly.

Last updated on