Your Business. Your Code. Your Rules.
Products, cart, checkout, auth, blog, media and analytics are methods on one typed client, and every one of them is an HTTP call you can make yourself. There is no templating language to learn, no theme to fight, and nothing in the way of the frontend you were going to write anyway.
import { DashClient } from "dash4devs"const dash = new DashClient({ apiKey: "pk_live_…" })// Products, cart, checkout, auth. All built in.const { products } = await dash.products.list()const cart = await dash.cart.get()const order = await dash.checkout.complete({ … })The Short Version
Six facts, and every one of them is checkable by installing the package. Nothing on this row is a number nobody counted.
- Package
- dash4devs
- Entry point
- new DashClient({ apiKey })
- Browser key
- pk_
- Server key
- sk_
- Types
- TypeScript declarations, in the package
- Without the SDK
- HTTPS and JSON, documented per endpoint
The Whole Platform Behind One Object
Most commerce platforms hand you a themeable storefront and hide the backend. This does the opposite. You get the backend, and it stays out of the way of your frontend. Create one DashClient with an API key and every part of the platform hangs off it.
There is no second package for the parts you did not think you needed, and no plugin marketplace standing between you and a feature.
- dash.products
- dash.categories
- dash.cart
- dash.checkout
- dash.auth
- dash.blog
- dash.pages
- dash.seo
- dash.email
- dash.shipping
- dash.media
- dash.insights
// Use it anywhere: server or browser.const products = await dash.products.list()const cart = await dash.cart.get()const order = await dash.checkout.complete({ … })const posts = await dash.blog.list()const reviews = await dash.products.getReviews("slug")// Same client, signed in as the shopper.const history = await dash.auth.getOrders()const book = await dash.auth.getAddresses()const rates = await dash.shipping.getRates({ … })// And the content the same backend is serving.const about = await dash.pages.getByPath("/about")const coas = await dash.coa.list()See The Difference
One complete checkout, written out the way you would have to write it, beside the call that runs the same seven steps.
Written by hand
// 1. Validate the cart and check stockconst items = await db.cartItems.findMany({ where: { cartId }, include: { product: true },})for (const item of items) { if (item.product.stock < item.quantity) throw new Error("Out of stock")}// 2. Work out every total yourselfconst subtotal = items.reduce(byLineTotal, 0)const tax = await taxProvider.quote({ subtotal, address })const shipping = await carrier.rate({ parcel, address })// 3. Assemble the payment requestconst merchant = new ApiContracts.MerchantAuthenticationType()merchant.setName(AUTHNET_LOGIN)merchant.setTransactionKey(AUTHNET_KEY)const card = new ApiContracts.CreditCardType()card.setCardNumber(number)card.setExpirationDate(expiry)card.setCardCode(cvv)// 4. …run it, and read the response and reason codes// 5. …hold the order when the fraud filter fires// 6. …write the order, then send the confirmation// 7. …decrement stock, and unwind all of it on a failureAnd it stops at step three. The response codes, the fraud hold, the order record, the confirmation email and the stock unwind are all still yours to write, and yours to keep working.
On this backend
// The same seven steps, run by the backend.const order = await dash.checkout.complete({ shipping, // address, and the rate the buyer chose payment_token, // from dash.payment.tokenize() analytics: dash.insights.attributionForOrder(),})// order.number, order.total, order.status.What That One Call Did
- Cart validated against live stock
- Tax and shipping quoted for the address
- Payment captured through your processor
- Fraud rules applied, and the order held if they fire
- Order record written
- Confirmation email sent
- Inventory decremented, and unwound if anything fails
Two Keys, One Client
It is the same client either way, scoped by which key you hand it. There is no second SDK to learn, and no way to ship the powerful one by importing the wrong package.
A key pair is also the tenant boundary, which is what makes running several storefronts off one backend a matter of swapping an environment variable.
How tenancy worksnew DashClient({ apiKey })
One import, one object, one set of types
pk_
Safe in the browser
Reads the catalog and drives the cart. Ship it in your client bundle.
sk_
Server only
Opens the admin side, for server-side scripts and seeding.
Attribution That Survives The Trip
The checkout call above takes an attribution payload from the insights module, so the order record carries the real UTM source and visitor id rather than whatever a third-party script guessed after the fact. It is first-party, captured into your own backend, and it is on the order rather than in somebody else’s dashboard.
That is the practical difference between a backend built for a marketing dashboard and one built for the person writing the checkout.
Where the data ends up
What You Get
Not a roadmap, and not an add-on marketplace you assemble yourself. This is what is in the package today.
A Plain JavaScript Client
Next.js, Remix, Astro, Vue or vanilla. It is an ES module, not a framework.
One Package, Both Sides
Server-side and client-side usage out of the same import.
A REST Endpoint Per Method
Documented, and callable directly if you would rather not take the SDK at all.
Scoped Keys
pk_ for public reads and the cart, sk_ for admin and seeding.
Per-Organization Isolation
Your data is never mixed with another tenant's, at the query level.
Types For Everything
Products, orders, carts, reviews and the rest, declared in the package.
Build On A Backend You Can Read.
Sign up, take your keys, and make your first dash.products.list() call against a real backend. Nothing on this page is behind a sales call.