4.5.4. Big Numbers in Data Models

In this chapter, you'll learn what big numbers are, how Medusa stores them, and how to handle their values when you retrieve or compute them.

What is a Big Number?#

A big number is a numeric value that Medusa stores with high precision to avoid the rounding errors of JavaScript's floating-point numbers. Medusa uses big numbers for monetary and other precision-sensitive values, such as prices, order totals, tax amounts, and payment amounts.

Tip: Prices in Medusa are stored as major currency units. For example, a price of $10.99 is stored as 10.99, not 1099. This is different from some other systems that store prices in minor currency units (for example, cents).

You define a big-number property in a data model with the bigNumber method:

Code
1import { model } from "@medusajs/framework/utils"2
3const CustomProduct = model.define("custom_product", {4  id: model.id().primaryKey(),5  price: model.bigNumber(),6})7
8export default CustomProduct
Tip: Use a big-number property when you need high precision for numbers that can have many decimal places. For less precision, use the float property instead.

How Medusa Stores Big Numbers#

For each big-number property, Medusa stores two columns in the database:

  • A numeric column with the same name as the property (for example, price). This holds the value as a number for convenience and querying.
  • A raw_ column prefixed with the property's name (for example, raw_price). This holds an object with the value as a string and its precision, which is the source of truth for the precise value.

For example, the raw_price column stores an object like this:

raw_price
1{2  "value": "10.99",3  "precision": 204}

The value is a string to preserve precision, since a string doesn't lose precision the way a JavaScript number can.


When Does Medusa Return a Big Number Value#

The shape of a big-number value depends on where you retrieve it and how the value is produced. Medusa reduces the value to a plain number only when it serializes the value to JSON for an HTTP response (that is, when the data is returned from an API route).

The following table summarizes the shape of a big-number value:

Where and How You Retrieve the Value

Shape

Server-side code (such as query.graph, a workflow, or a module service), retrieving a stored bigNumber property directly. For example, your custom price property, or a payment's amount.

A plain number. You can also request the property's raw_ field (such as raw_price) to retrieve the precise value.

Server-side code, retrieving a total that a Commerce Module computes and wraps in a BigNumber. For example, an order's or cart's total or subtotal.

A BigNumber instance.

A client consuming the response of an API route.

A plain number.

The difference in server-side code comes down to how the value is produced:

  • Medusa reads a stored bigNumber property back as its numeric value, so you get a plain number. The precise value is available in the property's raw_ field when you request it, as explained in the next section.
  • Commerce Modules compute some totals, such as an order's total, and wrap them in a BigNumber instance. For example, if you retrieve an order's total with Query, the total property is a BigNumber instance:
Code
1const { data: orders } = await query.graph({2  entity: "order",3  fields: ["total"],4})5
6// orders[0].total is a BigNumber instance
Tip: In server-side code, don't assume whether a big-number value is a plain number or a BigNumber instance. Handle it as explained in the next sections, which work for both shapes.

Retrieve the Precise Value of a Stored Property#

By default, query.graph returns only the fields you request, and a stored bigNumber property returns a plain number. To also retrieve the precise value, add the raw_ field to the fields array:

Code
1const { data: products } = await query.graph({2  entity: "custom_product",3  fields: ["price", "raw_price"],4})

Each product now has a raw_price property with the precise value:

Result
1[2  {3    "price": 10.99,4    "raw_price": {5      "value": "10.99",6      "precision": 207    }8  }9]

How to Handle Retrieved Big Numbers#

Display a Big Number#

When a client consumes an API route's response, the big-number value is a plain number that you can format and display directly.

In server-side code, a big-number value is either a plain number (for a stored property like price) or a BigNumber instance (for a computed total like an order's total). To get a plain number from either shape, check its type. If it's a BigNumber instance, use its numeric property. Otherwise, it's already a plain number, so use it as-is:

Code
1import { BigNumber } from "@medusajs/framework/utils"2
3const price =4  customProduct.price instanceof BigNumber5    ? customProduct.price.numeric6    : customProduct.price

You can also use the value in string interpolation, since a BigNumber instance supports numeric coercion:

Code
const message = `The price is ${customProduct.price}`

To display a price with its currency, pass the plain number and the currency code to JavaScript's Intl.NumberFormat. Medusa stores prices in major currency units, so you can format the value directly:

Code
1const formatted = new Intl.NumberFormat("en-US", {2  style: "currency",3  currency: "usd",4}).format(Number(customProduct.price))5
6// formatted is "$10.99"
Warning: Don't use a big-number's plain-number value as an input to further calculations, as it can lose precision. Instead, use the MathBN utility explained next.

Perform Arithmetic with MathBN#

To perform arithmetic on big numbers without losing precision, use the MathBN utility from @medusajs/framework/utils. Don't use JavaScript's arithmetic and comparison operators, such as + or >, as they reintroduce floating-point errors.

MathBN has the following methods for arithmetic operations:

  • MathBN.add(...nums): Add two or more numbers.
  • MathBN.sub(...nums): Subtract numbers from the first number.
  • MathBN.mult(n1, n2): Multiply two numbers.
  • MathBN.div(n1, n2): Divide the first number by the second.
  • MathBN.sum(...nums): Add all numbers, starting from 0.
  • MathBN.abs(n): Get the absolute value of a number.
  • MathBN.min(...nums) and MathBN.max(...nums): Get the smallest or largest number.

Each method accepts big-number values in any shape, including a BigNumber instance, a plain number, a numeric string, or a raw_ object. So, you can pass values retrieved from Query directly to MathBN.

MathBN performs exact decimal arithmetic, so it doesn't introduce the floating-point errors that JavaScript operators do.

For full precision, request and pass the raw_ value. Query types the raw_ field as Record<string, unknown>, so cast it to BigNumberRawValue from @medusajs/framework/types. You can also pass the plain number property, which is exact for typical values.

For example, to add two prices:

Code
1import { MathBN } from "@medusajs/framework/utils"2import { BigNumberRawValue } from "@medusajs/framework/types"3
4const { data: products } = await query.graph({5  entity: "custom_product",6  fields: ["price", "raw_price"],7})8
9const total = MathBN.add(10  products[0].raw_price as BigNumberRawValue,11  products[1].raw_price as BigNumberRawValue12)13
14// use the result as a plain number15const totalNumber = total.toNumber()

A MathBN method returns a big-number instance from the underlying bignumber.js library. To use the result as a plain number, call its toNumber method or pass it to the Number function, as shown above.

Sum an Array of Big Numbers#

To sum an array of big-number values, use JavaScript's reduce method with MathBN.sum. Start the accumulator with MathBN.convert(0), which creates a big-number 0:

Code
1import { MathBN } from "@medusajs/framework/utils"2import { BigNumberRawValue } from "@medusajs/framework/types"3
4const { data: products } = await query.graph({5  entity: "custom_product",6  fields: ["price", "raw_price"],7})8
9const total = products.reduce(10  (sum, product) =>11    MathBN.sum(12      sum,13      product.raw_price as BigNumberRawValue14    ),15  MathBN.convert(0)16)
Tip: MathBN.convert(value) normalizes any big-number shape to an instance you can use in calculations. MathBN.convert(0) is the common way to create a big-number 0.

Compare Big Numbers#

To compare big-number values, use the following MathBN methods, which each return a boolean:

  • MathBN.gt(n1, n2): Whether the first number is greater than the second.
  • MathBN.gte(n1, n2): Whether the first is greater than or equal to the second.
  • MathBN.lt(n1, n2): Whether the first number is less than the second.
  • MathBN.lte(n1, n2): Whether the first is less than or equal to the second.
  • MathBN.eq(n1, n2): Whether the two numbers are equal.

For example:

Code
1import { BigNumberRawValue } from "@medusajs/framework/types"2
3const pricedProducts = products.filter(4  (product) =>5    MathBN.gt(6      product.raw_price as BigNumberRawValue,7      08    )9)

How to Store Big Numbers in a Service#

When you store a big-number field with a module service's generated methods, such as createCustomProducts or updateCustomProducts, pass a plain number. Medusa converts it to a big number and maintains the raw_ column automatically.

For example:

Code
1const customProduct =2  await customProductModuleService.createCustomProducts({3    price: 10.99,4  })

The created record will have a price of 10.99 and a raw_price of:

raw_price
1{2  "value": "10.99",3  "precision": 204}
Was this chapter helpful?
Ask Bloom
For assistance in your development, use Claude Code Plugins or Medusa MCP server in Cursor, VSCode, etc...FAQ
What is Medusa?
How can I create a module?
How can I create a data model?
How do I create a workflow?
How can I extend a data model in the Product Module?
Recipes
How do I build a marketplace with Medusa?
How do I build digital products with Medusa?
How do I build subscription-based purchases with Medusa?
What other recipes are available in the Medusa documentation?
Chat is cleared on refresh
Line break