Skip to content

Directives API Reference

This section provides detailed API documentation for GraphQL directives in django-graphex.

BaseExtraGraphQLDirective

Abstract base class for all custom GraphQL directives.

class BaseExtraGraphQLDirective(GraphQLDirective)

Methods

get_args() (staticmethod)

Define the arguments that the directive accepts.

Returns: dict of GraphQL arguments

Example:

@staticmethod
def get_args():
    return {
        "format": GraphQLArgument(
            GraphQLString,
            description="Format string"
        )
    }

resolve(value, args, directive, root, info, **kwargs) (staticmethod)

Process the field value with the directive.

Parameters: - value (Any): The field value to transform - args (dict): The directive's coerced arguments. The middleware builds this with graphql.execution.values.get_directive_values, so variables are already resolved, defaults applied and values type-coerced. Read arguments with args.get("name") — never parse the AST by hand. - directive (DirectiveNode): GraphQL directive AST node (kept for introspection / backwards compatibility) - root (Any): Root object - info (ResolveInfo): GraphQL resolve info - **kwargs: Additional keyword arguments

Returns: Transformed value

Arguments can be variables

Because args is built from get_directive_values, any directive argument may be supplied as a GraphQL variable, e.g. query($n: Int!) { title @truncate(length: $n) }.


Standard GraphQL Directives (spec built-ins)

all_directives holds 30 directive instances: the library's own 25 plus the GraphQL specification's 5 — @skip, @include, @deprecated, @specifiedBy and @oneOf (graphql-core's specified_directives bundle). The last five are not django-graphex directives: @skip / @include are evaluated by the graphql-core executor (no GraphQLDirectiveMiddleware required), while @deprecated, @specifiedBy and @oneOf are type-system (SDL) directives that annotate the schema — they never appear in queries. Only the three documented below are ones you interact with; @specifiedBy (custom-scalar specification URLs) and @oneOf (exactly-one-key input objects) ride along with graphql-core's bundle and are never emitted by this library's own compiler.

@skip

Excludes a field, fragment spread, or inline fragment when if is true. Use a variable for the condition:

query Posts($skipAuthor: Boolean!) {
  posts {
    results {
      title
      author @skip(if: $skipAuthor) { name }
    }
  }
}
Argument Type Required Description
if Boolean! Yes Skip the selection when true

@include

The mirror image of @skip: includes the selection only when if is true:

query Posts($withComments: Boolean!) {
  posts {
    results {
      title
      comments @include(if: $withComments) {
        results { body }
      }
    }
  }
}
Argument Type Required Description
if Boolean! Yes Include the selection when true

@deprecated

Marks a field, argument, input field, or enum value as deprecated in the schema (SDL). Clients discover it through introspection. You don't hand-write this directive: pass deprecation_reason= in Python and the compiler emits it for you — on descriptor fields (Field / the typed shortcuts), on Django-mounted fields (DjangoObjectField, DjangoListObjectField, DjangoFilterListField, DjangoFilterPaginateListField, RetrieveField, QueryFields), and on mutation builders (CreateField, UpdateField, DeleteField, MutationFields). See Fields and Mutations for the full kwarg reference.

from django_graphex.core import ObjectType, CharField

class UserType(ObjectType):
    username = CharField()
    email = CharField(deprecation_reason="Use contactEmail instead.")
type UserType {
  username: String
  email: String @deprecated(reason: "Use contactEmail instead.")
}
Argument Type Required Description
reason String No Why it is deprecated (default: "No longer supported")

Introspection hides deprecated fields by default

__type { fields { name } } omits a deprecated field unless the client requests fields(includeDeprecated: true).


String Directives

DefaultGraphQLDirective

Provides default values for null or empty strings.

@default(to: "fallback value")

Only null and "" are substituted

Other falsy values are legitimate data and pass through untouched: 0, 0.0, false, an empty list and an empty object all keep their value. Substituting them would replace a real value with the to string and then fail serialization (Int cannot represent non-integer value, Expected Iterable, but did not find one).

Arguments

Argument Type Required Description
to String! Yes Value to use as default

Example

query {
  user {
    bio @default(to: "No bio available")
  }
}

String directives pass null through

Every string directive (@uppercase, @lowercase, @capitalize, @camel_case, @snake_case, @kebab_case, @swap_case, @strip, @title_case, @center, @replace, @truncate, @slugify) returns null for a null field value instead of rendering the literal text "None". This matches @floor, @ceil, @round and @abs.

Base64GraphQLDirective

Encode or decode strings using base64. Input is encoded as UTF-8, so non-ASCII characters (accented letters, emoji, etc.) are handled correctly without raising errors.

@base64(op: "encode")

Arguments

Argument Type Required Description
op String No Operation: "encode" or "decode"

Example

query {
  apiKey @base64(op: "encode")
  token @base64(op: "decode")
}

Case Conversion Directives

LowercaseGraphQLDirective

Convert string to lowercase.

@lowercase

UppercaseGraphQLDirective

Convert string to uppercase.

@uppercase

CapitalizeGraphQLDirective

Capitalize first character, lowercase the rest.

@capitalize

TitleCaseGraphQLDirective

Convert to title case (first letter of each word capitalized).

@title_case

SwapCaseGraphQLDirective

Swap case of all characters.

@swap_case

Example

query {
  user {
    name @uppercase
    email @lowercase
    title @title_case
    status @capitalize
  }
}

Code Style Directives

CamelCaseGraphQLDirective

Convert string to camelCase.

@camel_case

SnakeCaseGraphQLDirective

Convert string to snake_case.

@snake_case

KebabCaseGraphQLDirective

Convert string to kebab-case.

@kebab_case

Example

Each directive expects a specific input shape: @camel_case converts a snake_case string, while @snake_case and @kebab_case convert a space-separated Title Case string.

query {
  field {
    displayName @camel_case  # "user_profile" → "userProfile"
    apiName @snake_case      # "Api Name" → "api_name"
    urlSlug @kebab_case      # "Blog Post Title" → "blog-post-title"
  }
}

String Manipulation Directives

StripGraphQLDirective

Remove leading and trailing characters.

@strip(chars: " ")

Arguments

Argument Type Required Description
chars String No Characters to strip (default: whitespace)

CenterGraphQLDirective

Center string within a specified width.

@center(width: 20, fillchar: "-")

Arguments

Argument Type Required Description
width Int! Yes Total width of result string
fillchar String No Character to use for padding (default: space)

ReplaceGraphQLDirective

Replace occurrences of a substring.

@replace(old: "GraphQL", new: "GQL", count: 1)

Arguments

Argument Type Required Description
old String! Yes Substring to replace
new String! Yes Replacement string
count Int No Max number of replacements (-1 for all)

TruncateGraphQLDirective

Shorten a string to length characters, appending end (default "…"). Unless killwords is true, the string is cut on a word boundary.

@truncate(length: 20, end: "...", killwords: false)

Arguments

Argument Type Required Description
length Int! Yes Maximum number of characters to keep
end String No Suffix appended when truncated (default: "…")
killwords Boolean No Cut in the middle of words instead of on a boundary (default: false)

SlugifyGraphQLDirective

Convert a string into a URL-safe slug (Django slugify).

@slugify

Example

query {
  post {
    content @strip
    title @center(width: 50, fillchar: "=")
    text @replace(old: "API", new: "Application Programming Interface")
    summary @truncate(length: 80)                 # "A long summary…"
    heading @truncate(length: 10, killwords: true, end: "...")
    slug @slugify                                  # "My Post!" → "my-post"
  }
}

Number Directives

NumberGraphQLDirective

Format numbers using Python format strings. Format specs whose width or precision exceeds 100 are rejected with a field error (prevents memory exhaustion from client-supplied oversized specs).

@number(as: ".2f")

Arguments

Argument Type Required Description
as String! Yes Python format string (width and precision capped at 100)

Example

query {
  product {
    price @number(as: ".2f")      # "123.45"
    weight @number(as: ".3f")     # "12.500"
    rating @number(as: ".1%")     # "45.2%"
  }
}

CurrencyGraphQLDirective

Format numbers as currency.

@currency(symbol: "$")

Arguments

Argument Type Required Description
symbol String No Currency symbol (default: "$")

Example

query {
  product {
    priceUSD @currency             # "$123.45"
    priceEUR @currency(symbol: "€") # "€123.45"
    priceGBP @currency(symbol: "£") # "£123.45"
  }
}

@number and @currency only format String fields

Both directives produce a formatted string. A field typed Int or Float cannot serialize one, so on those fields the raw value is returned unchanged and the formatting is skipped. Expose the value as a String field (or a resolver returning a string) when you want the formatting to apply. The format-spec width/precision cap still applies on every field type.

Mathematical Directives

FloorGraphQLDirective

Return the floor of a number.

@floor

CeilGraphQLDirective

Return the ceiling of a number.

@ceil

RoundGraphQLDirective

Round a number to precision decimal places (default 0). With a precision of 0 or less, an integer is returned.

@round(precision: 2)

Arguments

Argument Type Required Description
precision Int No Number of decimal places (default: 0)

AbsGraphQLDirective

Return the absolute value of a number.

@abs

String / Float fields

@floor, @ceil, @round and @abs work on both Float and String fields: when the field's GraphQL type is String, the result is returned as a string. A null value passes through unchanged (returns null).

Example

query {
  stats {
    averageRating @floor       # 4.7 → 4
    priceEstimate @ceil        # 99.1 → 100
    score @round(precision: 1) # 4.27 → 4.3
    delta @abs                 # -3.5 → 3.5
  }
}

Date Directives

DateGraphQLDirective

Format dates and times using various formats.

@date(format: "YYYY-MM-DD")

Arguments

Argument Type Required Description
format String No Date format string (default: "default")

Format Options

Predefined Formats
Format Description Example
"default" Standard format "01 Dec 2023 14:30:00"
"iso" ISO 8601 format "2023-12-01T14:30:00"
"javascript" JS Date format "Fri Dec 01 2023 14:30:00"
"time ago" Relative time "2 hours ago"
"time ago 2d" Relative with fallback "Yesterday" or "Dec 01, 2023"
Custom Format Tokens
Token Description Example
YYYY 4-digit year 2023
YY 2-digit year 23
MMMM Full month name December
MMM Short month name Dec
MM Month number (padded) 12
DD Day of month (padded) 01
dddd Full day name Friday
ddd Short day name Fri
HH Hour (24h, padded) 14
hh Hour (12h, padded) 02
mm Minutes (padded) 30
ss Seconds (padded) 45
A AM/PM PM

Example

query {
  post {
    createdAt @date(format: "YYYY-MM-DD")         # "2023-12-01"
    updatedAt @date(format: "MMMM DD, YYYY")      # "December 01, 2023"
    publishedAt @date(format: "time ago")         # "2 hours ago"
    timestamp @date(format: "DD/MM/YY HH:mm A")   # "01/12/23 02:30 PM"
  }
}

List Directives

ShuffleGraphQLDirective

Randomly reorder list elements.

@shuffle

Example

query {
  post {
    tags @shuffle {
      name
      color
    }
  }
}

SampleGraphQLDirective

Get a random sample from a list. k is clamped to [0, len(list)], so a value larger than the list simply returns all elements.

@sample(k: 3)

Arguments

Argument Type Required Description
k Int! Yes Number of items to sample

Example

query {
  post {
    tags @sample(k: 3) {  # Get 3 random tags
      name
      color
    }
  }
}

UniqueGraphQLDirective

De-duplicate a list, preserving the original order. Handles unhashable items (e.g. dicts) by falling back to equality comparison.

@unique

Example

query {
  post {
    tags @unique  # ["a", "b", "a", "c"] → ["a", "b", "c"]
  }
}

Lists stay lists

List directives (@shuffle, @sample, @unique) return a list, so they can only be applied to list fields. They never mutate the underlying queryset / result cache — the source list is always copied first.


Middleware Integration

GraphQLDirectiveMiddleware

Middleware class that processes directives during query execution.

class GraphQLDirectiveMiddleware:
    def resolve(self, next, root, info, **args):
        # Process directives on field resolution
        pass

Django Settings

DJANGO_GRAPHEX = {
    'SCHEMA': 'myapp.schema.schema',
    'MIDDLEWARE': [
        'django_graphex.middleware.GraphQLDirectiveMiddleware',
    ],
}

Custom Directives

Creating Custom Directives

Define get_args() to declare the arguments and resolve() to transform the value. The middleware passes the coerced arguments as the args dict, so you just read args.get("name") — no AST parsing, and variables work for free.

from django_graphex.directives.base import BaseExtraGraphQLDirective
from graphql import GraphQLArgument, GraphQLNonNull, GraphQLString, GraphQLInt

class MaskGraphQLDirective(BaseExtraGraphQLDirective):
    """Keep the last `visible` characters, masking the rest with `char`."""

    @staticmethod
    def get_args():
        return {
            "visible": GraphQLArgument(
                GraphQLNonNull(GraphQLInt),
                description="Number of trailing characters to leave visible",
            ),
            "char": GraphQLArgument(
                GraphQLString,
                description="Masking character (default: '*')",
            ),
        }

    @staticmethod
    def resolve(value, args, directive, root, info, **kwargs):
        if not value:
            return value
        text = str(value)
        visible = args.get("visible") or 0
        char = args.get("char") or "*"
        if visible >= len(text):
            return text
        return char * (len(text) - visible) + text[len(text) - visible:]

Registering Custom Directives

from django_graphex.directives import all_directives
from django_graphex.schema import DjangoGraphQLSchema

# Add custom directive to the list (all_directives already bundles the five
# spec directives along with this library's own).
custom_directives = [
    *all_directives,
    MaskGraphQLDirective()
]

schema = DjangoGraphQLSchema(
    query=Query,
    directives=custom_directives
)

Using Custom Directives

query {
  user {
    creditCard @mask(visible: 4)            # "************3456"
    phone @mask(visible: 2, char: "x")      # "xxxxxxx89"
  }
}

Directive Utilities

Reading Arguments

You do not need to extract arguments from the AST. The middleware already coerces them (resolving variables and applying defaults) and passes them to resolve as the args dict:

@staticmethod
def resolve(value, args, directive, root, info, **kwargs):
    precision = args.get("precision") or 0   # default + variable aware
    ...

Type Checking Helper

def ensure_string(value):
    """Ensure value is a string."""
    if isinstance(value, str):
        return value
    return str(value) if value is not None else ""

Error Handling

Graceful Degradation

class SafeDirective(BaseExtraGraphQLDirective):
    @staticmethod
    def resolve(value, args, directive, root, info, **kwargs):
        try:
            # Directive processing logic
            return process_value(value)
        except Exception as e:
            # Log error and return original value
            logger.warning(f"Directive processing failed: {e}")
            return value

Validation

class ValidatedDirective(BaseExtraGraphQLDirective):
    @staticmethod
    def resolve(value, args, directive, root, info, **kwargs):
        if value is None:
            return None

        if not isinstance(value, (str, int, float)):
            raise ValueError("Directive only accepts string/number values")

        return process_value(value)

Performance Considerations

Caching Directive Results

from django.core.cache import cache

class CachedDirective(BaseExtraGraphQLDirective):
    @staticmethod
    def resolve(value, args, directive, root, info, **kwargs):
        cache_key = f"directive_{hash(str(value))}"
        cached_result = cache.get(cache_key)

        if cached_result is not None:
            return cached_result

        result = expensive_processing(value)
        cache.set(cache_key, result, 300)  # 5 minutes
        return result

Directive Ordering

# Directives are processed in the order they appear
query {
  field @strip @uppercase @truncate(length: 50)
  # 1. Strip whitespace
  # 2. Convert to uppercase
  # 3. Truncate to 50 characters
}

Best Practices

Directive Best Practices

  1. Handle Null Values: Always check for null/undefined values
  2. Provide Defaults: Use sensible defaults for optional arguments
  3. Validate Input: Validate argument types and values
  4. Error Gracefully: Return original value on processing errors
  5. Document Arguments: Provide clear descriptions for all arguments
  6. Consider Performance: Cache expensive operations when possible
  7. Test Thoroughly: Test with various input types and edge cases

Security Considerations

class SecureDirective(BaseExtraGraphQLDirective):
    @staticmethod
    def resolve(value, args, directive, root, info, **kwargs):
        # Validate permissions
        user = info.context.user
        if not user.is_authenticated:
            return value  # Don't process for unauthenticated users

        # Sanitize input
        safe_value = escape_html(str(value))

        return process_value(safe_value)

Testing Directives

import pytest
from graphql import graphql_sync
from django_graphex.directives import all_directives
from django_graphex.schema import DjangoGraphQLSchema

def test_uppercase_directive():
    schema = DjangoGraphQLSchema(
        query=Query,
        directives=all_directives
    )

    query = '''
        query {
            user {
                name @uppercase
            }
        }
    '''

    result = graphql_sync(schema.graphql_schema, query)
    assert result.data['user']['name'] == 'JOHN DOE'

This comprehensive API reference covers all directive classes and utilities in django-graphex, providing developers with the knowledge needed to use and create custom GraphQL directives for their applications.