Skip to content

Commit 22f1f64

Browse files
authored
docs/ts: add type docs for apis (#2112)
1 parent 28373a2 commit 22f1f64

File tree

3 files changed

+113
-0
lines changed

3 files changed

+113
-0
lines changed

docs/menu.cue

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -729,6 +729,11 @@
729729
text: "Cookies"
730730
path: "/ts/primitives/cookies"
731731
file: "ts/primitives/cookies"
732+
}, {
733+
kind: "basic"
734+
text: "Types"
735+
path: "/ts/primitives/types"
736+
file: "ts/primitives/types"
732737
}]
733738
}, {
734739
kind: "basic"

docs/ts/primitives/types.mdx

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
---
2+
seotitle: Types in Encore.ts API schemas
3+
seodesc: Learn how to work with types in Encore.ts schemas
4+
title: Types
5+
subtitle: Types in API schemas
6+
lang: ts
7+
---
8+
9+
When you define APIs in Encore.ts, the TypeScript types you use for request and response data are analyzed to generate your API schema. This schema is used for automatic validation, API documentation, and generating type-safe clients.
10+
11+
To ensure your API schema can be properly represented and serialized, Encore.ts reduces complex TypeScript types into basic types that can be represented in JSON. This means your API schemas should use simple, serializable types like strings, numbers, booleans, objects, and arrays.
12+
13+
## Decimal
14+
15+
JavaScript's native `number` type uses floating-point arithmetic, which can lead to precision errors when working with decimal values. For example, `0.1 + 0.2` equals `0.30000000000000004` instead of `0.3`. Additionally, JavaScript numbers are limited to values between `Number.MIN_SAFE_INTEGER` and `Number.MAX_SAFE_INTEGER` (approximately ±9 quadrillion).
16+
17+
To handle decimal values with arbitrary precision and arbitrarily large numbers, Encore.ts provides the `Decimal` type from `encore.dev/types`. This type is especially useful for financial calculations, prices, scientific computations, or any scenario where exact decimal precision and large number support are required.
18+
19+
### Using Decimal in APIs
20+
21+
The `Decimal` type can be used in your API request and response schemas just like any other type:
22+
23+
```typescript
24+
import { api } from "encore.dev/api";
25+
import { Decimal } from "encore.dev/types";
26+
27+
interface PaymentRequest {
28+
amount: Decimal;
29+
currency: string;
30+
}
31+
32+
interface PaymentResponse {
33+
total: Decimal;
34+
tax: Decimal;
35+
}
36+
37+
export const processPayment = api(
38+
{ expose: true, method: "POST", path: "/payments" },
39+
async (req: PaymentRequest): Promise<PaymentResponse> => {
40+
const taxRate = new Decimal("0.15"); // 15% tax
41+
const tax = req.amount.mul(taxRate);
42+
const total = req.amount.add(tax);
43+
44+
return { total, tax };
45+
}
46+
);
47+
```
48+
49+
### Creating Decimal values
50+
51+
You can create a `Decimal` from strings, numbers, or bigints:
52+
53+
```typescript
54+
import { Decimal } from "encore.dev/types";
55+
56+
const price1 = new Decimal("19.99");
57+
const price2 = new Decimal(29.99);
58+
const price3 = new Decimal(100n);
59+
```
60+
61+
For maximum precision, it's recommended to use string literals when creating `Decimal` values to avoid any floating-point conversion issues.
62+
63+
### Arithmetic operations
64+
65+
The `Decimal` type supports basic arithmetic operations:
66+
67+
```typescript
68+
const a = new Decimal("10.50");
69+
const b = new Decimal("2.25");
70+
71+
const sum = a.add(b); // 12.75
72+
const difference = a.sub(b); // 8.25
73+
const product = a.mul(b); // 23.625
74+
const quotient = a.div(b); // 4.666666...
75+
```
76+
77+
## Type compatibility and limitations
78+
79+
Encore.ts analyzes your TypeScript types to generate API schemas, but TypeScript's type system is incredibly complex and supports many advanced features. While we continuously add support for new type patterns, not all TypeScript type combinations are currently supported for API schemas.
80+
81+
### Working with ORM types
82+
83+
A common scenario where you might encounter type compatibility issues is when using types from ORMs (Object-Relational Mappers) or database libraries directly in your API schemas. These types often use complex features from the TypeScript type system.
84+
85+
In such cases, it's often better to create dedicated API types and convert between them and your ORM types.
86+
87+
### Benefits of separate API types
88+
89+
Creating dedicated API types instead of reusing ORM types can have several advantages:
90+
91+
- **Better API design**: Your API schema doesn't have to match your database schema 1:1. You can expose only the fields that make sense for your API consumers.
92+
- **Security**: Avoid accidentally exposing sensitive internal fields like password hashes or soft-delete timestamps.
93+
- **Stability**: Changes to your database schema don't automatically affect your API contract.
94+
- **Type compatibility**: Avoid issues with complex ORM-specific types that may not be supported in API schemas.
95+
96+
If you encounter a type that doesn't work in your API schema, creating a dedicated API type as shown above can be a good approach. In many cases, reusing your database types directly works fine, so use separate API types when it makes sense for your use case.

runtimes/js/encore.dev/types/mod.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,18 +26,30 @@ export class Decimal {
2626
: new runtime.Decimal(String(value));
2727
}
2828

29+
/**
30+
* Adds this decimal to another decimal value.
31+
*/
2932
add(d: Decimal | ToDecimal): Decimal {
3033
return Decimal.fromImpl(this.impl.add(this.toImpl(d)));
3134
}
3235

36+
/**
37+
* Subtracts another decimal value from this decimal.
38+
*/
3339
sub(d: Decimal | ToDecimal): Decimal {
3440
return Decimal.fromImpl(this.impl.sub(this.toImpl(d)));
3541
}
3642

43+
/**
44+
* Multiplies this decimal by another decimal value.
45+
*/
3746
mul(d: Decimal | ToDecimal): Decimal {
3847
return Decimal.fromImpl(this.impl.mul(this.toImpl(d)));
3948
}
4049

50+
/**
51+
* Divides this decimal by another decimal value.
52+
*/
4153
div(d: Decimal | ToDecimal): Decimal {
4254
return Decimal.fromImpl(this.impl.div(this.toImpl(d)));
4355
}

0 commit comments

Comments
 (0)