GreenFairy.Scalar (GreenFairy v0.3.0)

View Source

Defines a custom GraphQL scalar type with a clean DSL.

Usage

defmodule MyApp.GraphQL.Scalars.DateTime do
  use GreenFairy.Scalar

  scalar "DateTime" do
    parse fn
      %Absinthe.Blueprint.Input.String{value: value}, _ ->
        case DateTime.from_iso8601(value) do
          {:ok, datetime, _} -> {:ok, datetime}
          _ -> :error
        end
      _, _ -> :error
    end

    serialize fn datetime ->
      DateTime.to_iso8601(datetime)
    end
  end
end

CQL Operators

Define custom operators for filtering on this scalar type. This example uses the geo library from Hex (https://hex.pm/packages/geo):

defmodule MyApp.GraphQL.Scalars.Point do
  use GreenFairy.Scalar

  @moduledoc "GraphQL scalar for Geo.Point from the geo library"

  scalar "Point" do
    description "A geographic point (longitude, latitude)"

    parse fn
      %Absinthe.Blueprint.Input.Object{fields: fields}, _ ->
        lng = get_field(fields, "lng") || get_field(fields, "longitude")
        lat = get_field(fields, "lat") || get_field(fields, "latitude")
        {:ok, %Geo.Point{coordinates: {lng, lat}, srid: 4326}}
      _, _ ->
        :error
    end

    serialize fn %Geo.Point{coordinates: {lng, lat}} ->
      %{lng: lng, lat: lat}
    end

    # Define available operators for CQL
    operators [:eq, :near, :within_distance]

    # Define custom CQL input type (Hasura-style with underscores)
    cql_input "CqlOpPointInput" do
      field :_eq, :point
      field :_near, :point_near_input
      field :_within_distance, :point_distance_input
      field :_is_null, :boolean
    end

    # PostGIS-compatible filter using ST_DWithin
    filter :near, fn field, %Geo.Point{} = point, opts ->
      distance_meters = opts[:distance] || 1000
      {:fragment, "ST_DWithin(?::geography, ?::geography, ?)", field, point, distance_meters}
    end

    filter :within_distance, fn field, %{point: point, distance: distance} ->
      {:fragment, "ST_DWithin(?::geography, ?::geography, ?)", field, point, distance}
    end
  end

  defp get_field(fields, name) do
    Enum.find_value(fields, fn %{name: n, input_value: %{value: v}} ->
      if n == name, do: v
    end)
  end
end

Options

  • :description - Description of the scalar type (can also use @desc)

Summary

Functions

Defines a custom CQL operator input type for this scalar.

Defines how to apply a filter operator for this scalar type.

Defines the CQL operators available for this scalar type.

Defines a custom GraphQL scalar type.

Functions

cql_input(name, list)

(macro)

Defines a custom CQL operator input type for this scalar.

This generates a CqlOp{Scalar}Input type with fields for each operator.

Example

scalar "Point" do
  operators [:eq, :near, :within_distance]

  cql_input "CqlOpPointInput" do
    field :_eq, :point
    field :_near, :point_near_input
    field :_within_distance, :point_distance_input
  end
end

Hasura-style Operators

By convention, CQL operators use underscore prefixes (_eq, _near, etc.) to match Hasura's filtering syntax.

filter(operator, func)

(macro)

Defines how to apply a filter operator for this scalar type.

Examples

# Simple filter
filter :near, fn field, value ->
  {:geo_near, field, value}
end

# Filter with options
filter :within_radius, fn field, value, opts ->
  radius = opts[:radius] || 10
  {:geo_within, field, value, radius}
end

filter(operator, opts, func)

(macro)

operators(ops)

(macro)

Defines the CQL operators available for this scalar type.

Example

scalar "Point" do
  operators [:eq, :near, :within_distance]
  # ...
end

scalar(name, opts \\ [], list)

(macro)

Defines a custom GraphQL scalar type.

Examples

scalar "DateTime" do
  parse fn input, _ ->
    case DateTime.from_iso8601(input.value) do
      {:ok, datetime, _} -> {:ok, datetime}
      _ -> :error
    end
  end

  serialize fn datetime ->
    DateTime.to_iso8601(datetime)
  end
end