You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/api-endpoint-migration.md
+220-8Lines changed: 220 additions & 8 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -358,22 +358,29 @@ integrations: {
358
358
359
359
### Handling nullable types
360
360
361
-
When a field can be `null`, combine `nullable: true`with `$ref`:
361
+
Ajv does **not** allow `nullable: true` without `type`. Since `$ref` schemas don't have a `type` at the reference site, you cannot use `{ nullable: true, $ref: '...' }`. Instead, use `oneOf`with `{ type: 'null' }`:
> **Note**: `nullable: true` works fine with `type`, e.g., `{ type: 'string', nullable: true }`. The restriction only applies when combining `nullable` with `$ref` (no `type` present).
383
+
377
384
### Handling intersection types with `allOf`
378
385
379
386
When a response intersects a type with additional properties (e.g., `VideoConferenceInstructions & { providerName: string }`), use `allOf`:
@@ -440,6 +447,44 @@ This happens because `oneOf` requires **exactly one** match, but the value is bo
440
447
441
448
**Long-term fix**: Revise the core-typings to narrow `ts` to `string` (which is what MongoDB aggregation pipelines and `JSON.stringify` actually return), or adjust the AJV/typia schema generation to handle `Date | string` unions correctly (e.g., using `anyOf` instead of `oneOf`, or collapsing `Date` into `string`).
442
449
450
+
### Known Pitfall: Mapped utility types (`Nullable<>`, `Partial<>`, etc.)
451
+
452
+
Typia does **not** resolve custom mapped types when generating JSON schemas. For example, the following pattern:
Produces `avatarETag: { type: 'string' }`**without**`nullable: true`, even though the resolved type is `string | null`. This causes response validation to reject `null` values when `coerceTypes` is `false`.
464
+
465
+
**Fix**: Declare nullable fields explicitly instead of relying on mapped types:
After changing the type, rebuild core-typings (`yarn workspace @rocket.chat/core-typings run build`) to regenerate the schema.
478
+
479
+
**How to detect**: If a `$ref` schema rejects `null` values at runtime but the TypeScript type allows `null`, check if the type uses a mapped utility type. Inspect the generated schema:
4.**Augment `Endpoints`**: Use `declare module '@rocket.chat/rest-typings'` to merge the extracted types into the global `Endpoints` interface. This is what makes `useEndpoint('listInvites')` and similar SDK calls type-safe.
525
570
5.**Import `ExtractRoutesFromAPI`** from `'../ApiClass'` (relative to the endpoint file in `v1/`).
526
571
572
+
### Keep types in `rest-typings` (do NOT remove them)
573
+
574
+
The `declare module` augmentation via `ExtractRoutesFromAPI` only works within the `apps/meteor` compilation unit. External packages (`ddp-client`, `rest-client`, etc.) compile independently and **do not see** the augmented types — they only see the types exported from `@rocket.chat/rest-typings`.
575
+
576
+
**When migrating an endpoint, keep its type definition in `rest-typings` unchanged.** The augmentation adds response schema types on top of the existing definition. Removing the type from `rest-typings` will break external package consumers.
577
+
578
+
This duplication is temporary — see `docs/api-definitions-package-plan.md` for the planned consolidation.
579
+
580
+
### Use `as const` on options variables
581
+
582
+
When endpoint options are stored in a separate variable (required for sharing between action factories), add `as const` so that `authRequired: true` is inferred as the literal `true`, not `boolean`. This matters because `TypedThis` uses a conditional type:
The script scans for `API.v1.addRoute` and `API.default.addRoute` calls in `apps/meteor/app/api/`.
690
+
The script scans `apps/meteor/app/api/` for `API.v1.addRoute(...)` and `API.default.addRoute(...)` calls, extracting the route path, HTTP methods, file, and line number. Endpoints using this pattern lack compile-time type checking on request params and response shapes.
691
+
692
+
### `scripts/analyze-weak-types.mjs`
693
+
694
+
Analyzes `packages/rest-typings/` for "weak" types — generic types that provide little or no type safety in endpoint definitions.
695
+
696
+
```bash
697
+
# Full report grouped by endpoint
698
+
node scripts/analyze-weak-types.mjs
699
+
700
+
# JSON output for tooling/CI
701
+
node scripts/analyze-weak-types.mjs --json
702
+
703
+
# Only check AJV schema definitions (type: 'object' with no properties, etc.)
704
+
node scripts/analyze-weak-types.mjs --schema-only
705
+
706
+
# Only check TypeScript type definitions (any, Record<string, any>, etc.)
707
+
node scripts/analyze-weak-types.mjs --ts-only
708
+
```
709
+
710
+
Weak types detected:
711
+
712
+
| Pattern | Level | Risk |
713
+
|---|---|---|
714
+
|`any`, `unknown`| TypeScript | No type checking at all |
715
+
|`object` (bare) | TypeScript | Accepts any non-primitive |
716
+
|`Record<string, any>`, `Record<string, unknown>`| TypeScript | Untyped key-value bag |
After migrating endpoints, run through these improvements to maximize quality. These are not blocking but should be addressed before the migration is considered complete.
752
+
753
+
### 1. Extract shared schemas
754
+
755
+
Avoid duplicating the same schema inline across endpoints. Common patterns to extract:
756
+
757
+
```typescript
758
+
// Void success — reuse across all endpoints returning only { success: true }
**Important:** Declare shared `const` schemas **before** their first usage to avoid temporal dead zone (TDZ) errors. JavaScript `const` is not hoisted.
793
+
794
+
### 2. Strengthen relaxed object schemas
795
+
796
+
After initial migration, review schemas using `{ type: 'object' }` without `properties`. These pass any object shape through without validation.
797
+
798
+
**Acceptable cases** (document with a comment):
799
+
- Dynamic/schemaless objects (e.g., user preferences, custom fields)
800
+
- Complex types not yet in typia (e.g., `IExportOperation`)
801
+
802
+
**Should be fixed:**
803
+
- Types that have a `$ref` available (e.g., `IUser` → `{ $ref: '#/components/schemas/IUser' }`)
Run `node scripts/analyze-weak-types.mjs --schema-only` to find all instances.
807
+
808
+
### 3. Add `authRequired: false` explicitly
809
+
810
+
Endpoints that are intentionally public should declare `authRequired: false` explicitly rather than relying on the default. This makes the intent clear and self-documenting.
811
+
812
+
### 4. Add OpenAPI tags
813
+
814
+
Endpoints without `tags` in their options are excluded from the generated OpenAPI spec. Add appropriate tags:
815
+
816
+
```typescript
817
+
{
818
+
authRequired: true,
819
+
tags: ['Users'],
820
+
// ...
821
+
}
822
+
```
823
+
824
+
### 5. Create a changeset
825
+
826
+
If the migration changes error types (e.g., `'invalid-params'` → `'error-invalid-params'`), this is a breaking change for API consumers matching on `errorType`. Create a changeset:
827
+
828
+
```bash
829
+
yarn changeset
830
+
```
831
+
832
+
Use `minor` bump for the affected packages and document the error type change.
833
+
834
+
### 6. Add missing test coverage
835
+
836
+
For each migrated endpoint, verify:
837
+
-**Validator tests** exist for the body/query schema (valid, invalid, extra properties)
0 commit comments