Skip to content

Recipe: translate range types to/from Postgres' format instead of the object version #341

Open
@dmfay

Description

Sometimes the old way is more convenient :)

import { GraphQLScalarType } from "graphql"
import type { SchemaBuilder, Build } from "graphile-build"
import type { PgType } from "graphile-build-pg"

/**
 * This plugin switches the expression of range types back to Postgres' native
 * `[lower,upper)` formats instead of Postgraphile's more verbose nested-object
 * `{start: {value, inclusive}, end: {...}` version.
 */
export default function TranslateRangesPlugin(builder: SchemaBuilder) {
    builder.hook("build", (build: Build) => {
        const {
            pgIntrospectionResultsByKind,
            pgRegisterGqlInputTypeByTypeId,
            pgRegisterGqlTypeByTypeId,
            pg2GqlMapper,
            pgSql: sql,
        } = build

        // find our target type oid. If you want to change the behavior for a
        // built-in range type, you can use one of the following default oids:
        //
        // 3904 int4range
        // 3906 numrange
        // 3908 tsrange
        // 3910 tstzrange
        // 3912 daterange
        // 3926 int8range
        const myRange = pgIntrospectionResultsByKind.type.find(
            (t: PgType) => t.namespaceName === "my_schema" && t.name === "my_range"
        )

        if (!myRange) {
            return build // something went horribly wrong, abort
        }

        // declare a new kind of scalar
        const MyRangeType = new GraphQLScalarType({
            name: "MyRange",
            description: "An int8range dedicated to a more specific purpose",
            serialize: (a: string) => a,
            parseValue: (a: string) => a,
        })

        // register input and output types for the oid
        pgRegisterGqlInputTypeByTypeId(myRange.id, () => MyRangeType)
        pgRegisterGqlTypeByTypeId(myRange.id, () => MyRangeType)

        // define mappings to and from
        pg2GqlMapper[myRange.id] = {
            // SQL -> GQL
            map: (output: string) => output,
            // GQL -> SQL; substitute your target range type for `int8range` as necessary
            unmap: (input: string) => sql.fragment`${sql.value(input)}::int8range`,
        }

        return build // pass the build object back for the next stage
    })
}

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions