https://sonarly.com/issue/3801?type=bug
The POST /metadata GraphQL endpoint takes ~4.1 seconds due to a nestjs-query-generated SELECT DISTINCT query on the fieldMetadata table that takes 2+ seconds, caused by expensive DISTINCT deduplication across wide rows with multiple jsonb columns.
Fix: The fix replaces the slow `fields(paging: { first: 1000 }) { edges { node { ... } } }` CursorConnection query with `fields: fieldsList { ... }` using a GraphQL field alias in the REST metadata query builder.
**What changed:**
```typescript file=packages/twenty-server/src/engine/api/rest/metadata/query-builder/utils/fetch-metadata-fields.utils.ts lines=70-76
const fieldsPart = selector?.fields
? `
fields: fieldsList {
${fieldsSelection}
}
`
: '';
```
**Why this works:**
1. **Eliminates the slow `SELECT DISTINCT`**: The `fields(paging: { first: 1000 })` CursorConnection was routed through nestjs-query's auto-generated resolver which unconditionally adds `SELECT DISTINCT` across all columns of `fieldMetadata` — including 4 large jsonb columns — taking ~2045ms. Switching to `fieldsList` uses the existing `@ResolveField` on `ObjectMetadataResolver` (lines 189–213) that loads fields via a DataLoader backed by a multi-level cache (local memory → Redis → database).
2. **Field alias preserves the response key**: Using `fields: fieldsList` as a GraphQL field alias means the response still contains the key `fields` (not `fieldsList`), so no consumer-facing breaking change occurs. The `cleanGraphQLResponse` utility handles the new shape correctly: a plain array is not an object (`isObject` returns false for arrays), so it is assigned directly — producing the same `fields: [{...}]` output as before.
3. **Single-file, 5-line change**: Entirely within the REST metadata query builder utility; no framework code, shared utilities, or other callers are modified.