Documentation
GraphQL where
Map URL filters to a GraphQL where object and back again.
Applications often need URL filters to drive a GraphQL where input. Keep that mapping explicit: the filter-query schema owns the URL shape, and a small codec owns conversion to and from GraphQL variables.
import {
booleanField,
buildEqFilters,
defineSchema,
enumField,
stringField,
type InferFilters,
} from '@plumile/filter-query';
type ProjectWhere = {
archived?: boolean;
owner?: { id?: string };
status?: 'ACTIVE' | 'PAUSED';
title?: { contains?: string };
};
export const projectFilters = defineSchema({
: stringField(['eq']),
: enumField(['ACTIVE', 'PAUSED'], ['eq']),
: stringField(['contains']),
: booleanField(['eq']),
});
export type ProjectFilters = InferFilters<typeof projectFilters>;
export function toProjectWhere(filters: ProjectFilters): ProjectWhere | null {
const where: ProjectWhere = {};
iffilters.ownerId?.eq != null {
where.owner = {: filters.ownerId.eq };
}
iffilters.status?.eq != null {
where.status = filters.status.eq;
}
iffilters.title?.contains != null {
where.title = {: filters.title.contains };
}
iffilters.archived?.eq != null {
where.archived = filters.archived.eq;
}
return Object.keyswhere.length > 0 ? where : null;
}
export function fromProjectWhere(where: ProjectWhere | null): ProjectFilters {
ifwhere == null {
return buildEqFilters{};
}
return {
...buildEqFilters<typeof projectFilters>{
: where.owner?.id,
: where.status,
: where.archived,
},
...where.title?.contains != null
? {: {: where.title.contains } }
: {},
};
}Use field names such as ownerId when the URL should be stable and readable, even if the GraphQL input is nested. Keep aliases and nested paths in this mapping layer instead of spreading GraphQL shapes throughout UI code.