graphile / graphile/crystal

v5: Safe way to rename / remove / relocate fields

Open
#2,841 3 comments 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
12.9k
Forks
625
Avg merge
5h 23m
Merged PRs (30d)
24

Description

# v5: Safe way to rename / remove / relocate fields (e.g. move `totalCount` into `aggregate.count` for Hasura-style aggregates)

Hi 👋. I’m using PostGraphile v5 with `@graphile/pg-aggregates` and Grafast, and I’m trying to build a plugin that reshapes the *entire* schema for connection types to look more like Hasura’s aggregate API.

Concretely, for any GraphQL object type that:

- is backed by a `ConnectionStep` (i.e. `Self.extensions.grafast.assertStep.$$export.exportName === "ConnectionStep"`), and
- exposes one of the aggregate fields added by `@graphile/pg-aggregates` (e.g. `aggregates`, `aggregate`, `groupedAggregates`),

I’d like to be able to:

1. **Rename fields** (e.g. rename a connection root field or `aggregates` → `aggregate`).
2. **Remove fields** at the connection level (e.g. hide `totalCount` / `count` from the top level).
3. **Clone / relocate fields** while preserving their Grafast plan (e.g. move `totalCount` so that it is also available as `aggregate.count` inside the aggregate container).

The end goal is a Hasura-like shape such as:

```graphql
foo_aggregate {
aggregate {
count
sum { ... }
avg { ... }
}
groupedAggregate { ... }
}
```

…where `aggregate.count` behaves exactly like the existing `totalCount`/`count` on the connection (same semantics, same plan), and ideally we can optionally drop the top-level `totalCount` field.

## What I tried

I experimented with:

- `schema.hooks.GraphQLObjectType_fields` (v5 hook API), and
- `processSchema(...)` post-build transformations, and
- copying the `GraphQLField` / `GraphQLFieldConfig` and/or `extensions.grafast` from the connection’s `totalCount` field into the aggregate container type.

This “works” at the type level (the new `aggregate.count` field appears in the schema), but at runtime it tends to break the aggregate object resolution – `aggregate` becomes `null` – because the copied plan was written assuming the parent step is the connection step, not the aggregate step.

In other words, naïvely cloning the field or its plan isn’t safe without deeper knowledge of the internal Step graph.

## Request

Would it be possible to have either:

- A **documented pattern / example plugin** that shows how to:
- rename / hide fields on `ConnectionStep`-backed types, and
- expose a `count` field inside the aggregate container that reuses the existing connection-level `totalCount` logic **safely**, or

- A **first-class option in `@graphile/pg-aggregates` / core** that exposes a Hasura-style `aggregate.count` (optionally also allowing the top-level `totalCount` to be removed), or

- A small **API hook** that lets a plugin get from the aggregate Step back to the underlying connection Step (so a custom `aggregate.count` plan can delegate to the same logic as `totalCount` without relying on internal, undocumented assumptions)?

I’m specifically trying to avoid:

- casting everything to `any` and poking at private properties, and
- brittle hacks that rely on internal Step structures that may change.

A supported way to do “rename / remove / relocate field while preserving its Grafast plan” for connection/aggregate types would make Hasura-style compatibility much easier to maintain.

Thanks a lot for any guidance or API hooks you can provide 🙏

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.