Skip to content

Converting bytea and other binary types #2667

@zxpectre

Description

@zxpectre

Summary

What would be the right way to cast/convert binary types like 'bytea' into hexadecimal instead of base64 strings?

I was trying this plugin based on official source code but

  • If I try to register again a Base64EncodedBinary GQL type I get name collision errors
  • When I create a new HexEncodedBinary GQL type I cannot find a right way to map pg types like bytea to this one.

Also is there a way to properly detect all the types being converted to base64 to apply this conversion to all of them automatically?

import "graphile-config";
import { EXPORTABLE } from "graphile-build";
import {
  TYPES,
} from "@dataplan/pg";

// would cause a collision with already registered Base64EncodedBinary
// const useEncoding='base64';
// const inflectionName='Base64EncodedBinary'

const useEncoding = 'hex';
const inflectionName = 'HexEncodedBinary'

export const BinaryCodecPlugin: GraphileConfig.Plugin = {
    name: "BinaryCodecPlugin",
    version: "0.0.1",
    description: `Encodes/decodes binary types as '${useEncoding}' strings`,
    after: [
        "CommonTypesPlugin", 
        "PgBasicsPlugin"
    ],

    schema: {
        hooks: {
            init: {
                provides: ["pg-standard-types"],
                callback: (_, build) => {
                    
                    const doConnection = (name: string) => {
                        build.registerCursorConnection?.({
                            typeName: build.inflection.builtin(name),
                            // When dealing with scalars, nulls are allowed in setof
                            nonNullNode: false,
                            scope: {
                                isPgConnectionRelated: true,
                            },
                        });
                    };

                    build.registerScalarType(
                        build.inflection.builtin(inflectionName),
                        {},
                        () => ({
                            description: build.wrapDescription(
                                `Binary data encoded using ${useEncoding}`,
                                "type",
                            ),
                            serialize: EXPORTABLE(
                                () =>
                                    function serialize(data: unknown) {
                                        if (Buffer.isBuffer(data)) {
                                            //console.log(`[BinaryCodecPlugin] serialize:`,data.toString(useEncoding))
                                            return data.toString(useEncoding);
                                        } else {
                                            throw new Error(
                                                `${inflectionName} can only be used with Node.js buffers.`,
                                            );
                                        }
                                    },
                                [],
                            ),
                            parseValue: EXPORTABLE(
                                (GraphQLError) =>
                                    function parseValue(data: unknown) {
                                        if (typeof data === "string") {
                                            return Buffer.from(data, useEncoding);
                                        } else {
                                            throw new GraphQLError(
                                                `${inflectionName} can only parse string values.`,
                                            );
                                        }
                                    },
                                [build.graphql.GraphQLError],
                            ),
                            parseLiteral: EXPORTABLE(
                                (GraphQLError, Kind) =>
                                    function parseLiteral(ast) {
                                        if (ast.kind !== Kind.STRING) {
                                            // TODO: add name to this error
                                            throw new GraphQLError(
                                                `${inflectionName} can only parse string values`,
                                            );
                                        }
                                        return Buffer.from(ast.value, useEncoding);
                                    },
                                [build.graphql.GraphQLError, build.graphql.Kind],
                            ),
                        }),
                        `graphile-build-pg built-in (${inflectionName})`,
                    );
                    doConnection(inflectionName);



                    // tried this and other approaches but seems to do nothing, also feels hacky:
                    build.setGraphQLTypeForPgCodec(TYPES['bytea'],'bytea',inflectionName);



                    return _;

                }
            }

        },
    },
};

Additional context

    "@graphile-contrib/pg-many-to-many": "^2.0.0-beta.9",
    "@graphile/pg-aggregates": "^0.2.0-beta.8",
    "@graphile/simplify-inflection": "^8.0.0-beta.7",
    "graphile-config": "^0.0.1-beta.17",
    "pg": "^8.16.3",
    "postgraphile": "^5.0.0-beta.43",
    "postgraphile-plugin-connection-filter": "^3.0.0-beta.8"
    "ts-node": "^10.9.2",
    "typescript": "^5.9.2"

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