Dyrected
Model ContentFields

Money

A monetary field stored as exact integer minor units, so amounts never drift.

Use the money field when a number is an amount of money and it has to add up exactly: balances, prices, ledger entries, refunds. It stores the amount as a whole number of minor units (kobo for naira, cents for dollars), so 0.1 + 0.2 style rounding errors can't creep into your books.

Here is the simplest version, a balance in naira:

import { defineCollection, defineMoneyField } from '@dyrected/core'

export const Wallets = defineCollection({
  slug: 'wallets',
  fields: [defineMoneyField({ name: 'balance', label: 'Balance', currency: 'NGN' })],
})

An editor types 52,500.00 in the admin. Dyrected stores 5250000, and your API responses, hooks and frontend code all see 5250000. You'll know it's working when the list view shows ₦52,500.00 while a request to the API returns the integer.

Why integers

JavaScript numbers are floating point, so 52.5 * 1000 comes out as 52500.00000000001. Add up enough of those and a ledger stops reconciling. Integers add exactly, and Dyrected's atomic increment and decrement operators (see Transactions) keep them exact even when many requests write at once.

What gets checked on write

When a document is created or updated through the API, every money field must be a safe integer. An integer string such as "5250000" is converted to a number. Anything else, like 52.5 or "abc", is rejected with a 400 that names the field.

This check covers top-level money fields on collections (including ones inside a row). It doesn't cover money fields nested inside object, array or blocks fields, or on globals, so validate those in a hook if you use them there.

Choosing the currency

Set a fixed currency when every value uses the same one. When each document has its own, point currencyField at a sibling field that holds the ISO 4217 code:

import { defineCollection, defineMoneyField, defineSelectField } from '@dyrected/core'

export const Payments = defineCollection({
  slug: 'payments',
  fields: [
    defineSelectField({ name: 'currency', label: 'Currency', options: ['NGN', 'USD'] }),
    defineMoneyField({ name: 'amount', label: 'Amount', currencyField: 'currency' }),
  ],
})

The admin reads the currency from the document, so a USD payment shows as dollars and an NGN payment as naira. Currencies with a different number of decimal places need decimals, which defaults to 2. Use 0 for yen, for example.

Showing raw minor units

If you'd rather see and edit the raw integer in the admin, set admin.displayMinorAsMajor: false. This only changes what the admin shows. Storage and the API are untouched.

Storage, sorting and filtering

In SQL databases a promoted money column is a BIGINT (an INTEGER in SQLite). Add promoted: true if you filter or sort on it often, as you would for a number field. Atomic increment and decrement work whether or not the field is promoted.

The list view's number filter compares the stored minor units, so a filter value of 1000 means 10.00.

On this page

Dyrected| Cloud

Get your backend ready in minutes

Use a managed database, storage, APIs, and admin dashboard without setting up the infrastructure yourself.

Set Up My Backend