Skip to content

Grids: Filtering, Sorting & Pagination

The primaTime API provides a powerful grid system for querying collections of entities. This system supports cursor-based pagination, multi-field filtering, and sorting — enabling you to build efficient data tables, reports, and lists.

Connection Pattern

All collection queries follow the Relay Connection specification:

graphql
type ProjectConnection {
  edges: [ProjectEdge!]!
  pageInfo: PageInfo!
}

type ProjectEdge {
  node: Project!
  cursor: String!
}

type PageInfo {
  endCursor: String
  hasNextPage: Boolean!
  hasPreviousPage: Boolean!
  startCursor: String
  totalCount: Int!
}

Pagination

Basic Pagination

Use first and after for forward pagination:

graphql
query GetProjects($first: Int!, $after: String) {
  projects(first: $first, after: $after) {
    edges {
      node {
        id
        title
        code
      }
      cursor
    }
    pageInfo {
      hasNextPage
      endCursor
      totalCount
    }
  }
}

First page:

json
{ "first": 20 }

Next page:

json
{ "first": 20, "after": "cursor_from_previous_page" }

Backward Pagination

Use last and before for backward pagination:

graphql
query GetProjectsBackward($last: Int!, $before: String) {
  projects(last: $last, before: $before) {
    edges {
      node { id title }
      cursor
    }
    pageInfo {
      hasPreviousPage
      startCursor
    }
  }
}

Skip Pagination

For offset-based pagination (useful for "jump to page"):

graphql
query GetProjectsWithSkip($first: Int!, $skip: Int) {
  projects(first: $first, skip: $skip) {
    edges { node { id title } }
    pageInfo { totalCount }
  }
}

Page 3 with 20 items per page:

json
{ "first": 20, "skip": 40 }

Filtering

Filters are passed via the query parameter using SearchFilterQuery:

graphql
input SearchFilterQuery {
  search: Search           # Full-text search
  filters: Filters         # Field-specific filters
  orders: [OrderBy!]       # Sorting
}

Search across multiple text fields:

graphql
query SearchProjects($term: String!) {
  projects(first: 20, query: {
    search: { term: $term }
  }) {
    edges {
      node { id title code }
    }
  }
}
json
{ "term": "website redesign" }

Field Filters

The Filters input supports three filter types:

graphql
input Filters {
  simple: [SimpleFilter!]   # Single value comparison
  list: [ListFilter!]       # IN / NOT_IN operations
  between: [BetweenFilter!] # Range comparisons
}

Filter Operators

OperatorDescriptionExample Use
EQUALExact matchStatus = "Active"
NOT_EQUALNot equalPriority ≠ "Low"
GREATER_THANGreater thanCreated > date
GREATER_THAN_OR_EQUALGreater or equalAmount ≥ 100
LESS_THANLess thanDue date < date
LESS_THAN_OR_EQUALLess or equalBudget ≤ 1000
LIKEPattern matchTitle contains "web"
INValue in listStatus in ["Open", "Active"]
NOT_INValue not in listType not in ["Draft"]
BETWEENRange (inclusive)Date between X and Y
IS_NULLValue is nullClient is not set
IS_NOT_NULLValue is not nullAssignee is set

Simple Filters

Single value comparisons:

graphql
query FilteredTasks {
  tasks(first: 20, query: {
    filters: {
      simple: [
        { field: TASK_PRIORITY, operator: EQUAL, value: "HIGH" }
        { field: TASK_STATUS_TYPE, operator: NOT_EQUAL, value: "DONE" }
      ]
    }
  }) {
    edges { node { id title priority } }
  }
}

List Filters

IN/NOT_IN operations for multiple values:

graphql
query TasksInStatuses {
  tasks(first: 20, query: {
    filters: {
      list: [
        { 
          field: TASK_STATUS_TYPE, 
          operator: IN, 
          values: ["TODO", "IN_PROGRESS"] 
        }
      ]
    }
  }) {
    edges { node { id title status { title } } }
  }
}

Between Filters

Range queries:

graphql
query TimeRecordsInRange {
  timeRecords(first: 50, query: {
    filters: {
      between: [
        {
          field: TIME_RECORD_START,
          operator: BETWEEN,
          valueFrom: "2024-01-01T00:00:00.000Z",
          valueTo: "2024-01-31T23:59:59.999Z"
        }
      ]
    }
  }) {
    edges {
      node {
        id
        start
        end
        duration
      }
    }
  }
}

Null Checks

graphql
# Tasks with an assignee
query AssignedTasks {
  tasks(first: 20, query: {
    filters: {
      simple: [
        { field: TASK_ASSIGNEE, operator: IS_NOT_NULL, value: "" }
      ]
    }
  }) {
    edges { node { id title assignee { profile { firstName } } } }
  }
}

# Tasks without a due date
query TasksWithoutDueDate {
  tasks(first: 20, query: {
    filters: {
      simple: [
        { field: TASK_DUE_DATE, operator: IS_NULL, value: "" }
      ]
    }
  }) {
    edges { node { id title } }
  }
}

Combining Filters

Multiple filters are combined with AND logic:

graphql
query ComplexFilter {
  tasks(first: 20, query: {
    search: { term: "api" },
    filters: {
      simple: [
        { field: TASK_PRIORITY, operator: IN, value: "HIGH" },
        { field: COMMON_ARCHIVED, operator: EQUAL, value: "ONLY_NOT_ARCHIVED" }
      ],
      list: [
        { field: TASK_PROJECT, operator: IN, values: ["proj_1", "proj_2"] }
      ],
      between: [
        { 
          field: TASK_DUE_DATE, 
          operator: BETWEEN, 
          valueFrom: "2024-01-01", 
          valueTo: "2024-03-31" 
        }
      ]
    }
  }) {
    edges { node { id title } }
  }
}

Sorting

Specify sort order with orders:

graphql
query SortedProjects {
  projects(first: 20, query: {
    orders: [
      { field: PROJECT_CLIENT, direction: ASC },
      { field: PROJECT_TITLE, direction: ASC }
    ]
  }) {
    edges {
      node {
        id
        title
        client { title }
      }
    }
  }
}

Sort Direction

graphql
enum OrderDirection {
  ASC   # Ascending (A-Z, 0-9, oldest first)
  DESC  # Descending (Z-A, 9-0, newest first)
}

Available Filter Fields

Query available filters for any entity type:

graphql
query AvailableTaskFilters {
  availableFilters(entityType: TASK) {
    filters {
      field
      dataType
      operators
      entityType    # For relation fields
      enumType      # For enum fields
    }
  }
  
  availableOrders(entityType: TASK) {
    orders
  }
}

Common Filter Fields

  • COMMON_ID
  • COMMON_CREATED_AT
  • COMMON_ARCHIVED

Archive Filter

Control visibility of archived items:

graphql
query ActiveProjects {
  projects(first: 20, query: {
    filters: {
      simple: [
        { field: COMMON_ARCHIVED, operator: EQUAL, value: "ONLY_NOT_ARCHIVED" }
      ]
    }
  }) {
    edges { node { id title } }
  }
}

Archive filter values:

  • ONLY_ARCHIVED — Show only archived items
  • ONLY_NOT_ARCHIVED — Show only active items (default behavior)
  • ALL — Show both archived and active items

primaTime API Documentation