Skip to main content

Advanced Query Patterns

The Lumar GraphQL API supports patterns that go well beyond single-resource lookups. Understanding these patterns lets you fetch exactly the data you need in fewer requests and express complex filtering logic. This page covers production patterns used by real API clients.

Multi-root queries

GraphQL allows a single request to call multiple top-level resolvers at once. Instead of making four sequential HTTP requests to load a project settings screen, you can combine them into one query. All resolvers execute in parallel on the server and the results arrive in a single response.

tip

Multi-root queries are the single most effective way to eliminate loading waterfalls in UI applications. Use them whenever you need data from more than one top-level field on the initial render.

Operation: query GetProjectSettings($projectId: ObjectID!, $accountId: ObjectID!) { getProject(id: $projectId) { id name primaryDomain } getAccount(id: $accountId) { id maxCrawlRate subscription { plan { code } } } getScheduleFrequencies { code name } getUserAgents { code name isMobile } }Variables: { "projectId": "TjAwN1Byb2plY3Q2MjAyMw", "accountId": "TjAwN0FjY291bnQ3MTU" }Response Example: { "data": { "getProject": { "id": "TjAwN1Byb2plY3Q2MjAyMw", "name": "Lumar Docs", "primaryDomain": "https://www.lumar.io/" }, "getAccount": { "id": "TjAwN0FjY291bnQ3MTU", "maxCrawlRate": 5, "subscription": { "plan": { "code": "enterprise" } } }, "getScheduleFrequencies": [ { "code": "Daily", "name": "Daily" }, { "code": "Weekly", "name": "Weekly" } ], "getUserAgents": [ { "code": "desktop", "name": "Desktop Chrome", "isMobile": false }, { "code": "mobile", "name": "Mobile Chrome", "isMobile": true } ] } }
GetProjectSettingsTry in Explorer
GraphQL
query GetProjectSettings($projectId: ObjectID!, $accountId: ObjectID!) {
getProject(id: $projectId) {
id
name
primaryDomain
}
getAccount(id: $accountId) {
id
maxCrawlRate
subscription {
plan { code }
}
}
getScheduleFrequencies {
code
name
}
getUserAgents {
code
name
isMobile
}
}

The response contains getProject, getAccount, getScheduleFrequencies, and getUserAgents side-by-side in data. There is no overhead beyond a single round trip.

Field aliasing

GraphQL aliases let you call the same field more than once in a single query, each time with different arguments, and map each result to a distinct key in the response. This is commonly used to fetch multiple "views" of the same connection without multiple requests.

The example below fetches two separate crawl lists from a single project: the most recently created crawl (regardless of status) and the five most recently finished crawls.

Operation: query GetProjectCrawlsSummary($projectId: ObjectID!) { getProject(id: $projectId) { id lastCrawl: crawls(first: 1, orderBy: [{ field: createdAt, direction: DESC }]) { nodes { id statusEnum finishedAt } } recentFinished: crawls( first: 5 filter: { statusEnum: { eq: Finished } } orderBy: [{ field: finishedAt, direction: DESC }] ) { nodes { id finishedAt } } } }Variables: { "projectId": "TjAwN1Byb2plY3Q2MjAyMw" }Response Example: { "data": { "getProject": { "id": "TjAwN1Byb2plY3Q2MjAyMw", "lastCrawl": { "nodes": [ { "id": "TjAwNUNyYXdsMTU4MzI0Ng", "statusEnum": "Crawling", "finishedAt": null } ] }, "recentFinished": { "nodes": [ { "id": "TjAwNUNyYXdsMTU4MzI0NQ", "finishedAt": "2025-03-20T14:30:00.000Z" }, { "id": "TjAwNUNyYXdsMTU4MzI0NA", "finishedAt": "2025-03-13T09:15:00.000Z" } ] } } } }
GetProjectCrawlsSummaryTry in Explorer
GraphQL
query GetProjectCrawlsSummary($projectId: ObjectID!) {
getProject(id: $projectId) {
id
lastCrawl: crawls(first: 1, orderBy: [{ field: createdAt, direction: DESC }]) {
nodes {
id
statusEnum
finishedAt
}
}
recentFinished: crawls(
first: 5
filter: { statusEnum: { eq: Finished } }
orderBy: [{ field: finishedAt, direction: DESC }]
) {
nodes {
id
finishedAt
}
}
}
}

Without aliases, a second call to crawls in the same selection set would be a syntax error. With aliases (lastCrawl: and recentFinished:), both calls are valid and their results are returned under those keys.

Nested filtering with boolean logic

The filter argument accepts _or, _and, and _not arrays that can be nested arbitrarily. This lets you express conditions such as "give me all crawls that are currently running, queued, or paused" without fetching everything and filtering client-side.

Using _or to match any of several enum values

Operation: query GetActiveCrawls($projectId: ObjectID!) { getProject(id: $projectId) { id crawls( first: 20 filter: { _or: [ { statusEnum: { eq: Crawling } } { statusEnum: { eq: Queued } } { statusEnum: { eq: Paused } } ] } orderBy: [{ field: createdAt, direction: DESC }] ) { nodes { id statusEnum createdAt } totalCount } } }Variables: { "projectId": "TjAwN1Byb2plY3Q2MjAyMw" }Response Example: { "data": { "getProject": { "id": "TjAwN1Byb2plY3Q2MjAyMw", "crawls": { "nodes": [ { "id": "TjAwNUNyYXdsMTU4MzI0Ng", "statusEnum": "Crawling", "createdAt": "2025-03-27T08:00:00.000Z" }, { "id": "TjAwNUNyYXdsMTU4MzI0NQ", "statusEnum": "Queued", "createdAt": "2025-03-26T22:00:00.000Z" } ], "totalCount": 2 } } } }
GetActiveCrawlsTry in Explorer
GraphQL
query GetActiveCrawls($projectId: ObjectID!) {
getProject(id: $projectId) {
id
crawls(
first: 20
filter: {
_or: [
{ statusEnum: { eq: Crawling } }
{ statusEnum: { eq: Queued } }
{ statusEnum: { eq: Paused } }
]
}
orderBy: [{ field: createdAt, direction: DESC }]
) {
nodes {
id
statusEnum
createdAt
}
totalCount
}
}
}
tip

For matching multiple values of the same enum field, the in predicate is more concise than _or. Use _or when the conditions involve different fields.

Using _and to combine a status filter with a date range

Operation: query GetRecentFinishedCrawls($projectId: ObjectID!) { getProject(id: $projectId) { id crawls( first: 10 filter: { _and: [ { statusEnum: { eq: Finished } } { finishedAt: { ge: "2025-01-01T00:00:00Z" } } ] } orderBy: [{ field: finishedAt, direction: DESC }] ) { nodes { id statusEnum finishedAt } totalCount } } }Variables: { "projectId": "TjAwN1Byb2plY3Q2MjAyMw" }Response Example: { "data": { "getProject": { "id": "TjAwN1Byb2plY3Q2MjAyMw", "crawls": { "nodes": [ { "id": "TjAwNUNyYXdsMTU4MzI0NQ", "statusEnum": "Finished", "finishedAt": "2025-03-20T14:30:00.000Z" } ], "totalCount": 1 } } } }
GetRecentFinishedCrawlsTry in Explorer
GraphQL
query GetRecentFinishedCrawls($projectId: ObjectID!) {
getProject(id: $projectId) {
id
crawls(
first: 10
filter: {
_and: [
{ statusEnum: { eq: Finished } }
{ finishedAt: { ge: "2025-01-01T00:00:00Z" } }
]
}
orderBy: [{ field: finishedAt, direction: DESC }]
) {
nodes {
id
statusEnum
finishedAt
}
totalCount
}
}
}

The _and array requires every condition to be true. Combining a status check with a date boundary is a common pattern for auditing or reporting workflows.

See the Filtering guide for the full set of available predicates for each field type.

Inline arguments on nested fields

Connection fields anywhere in the query tree accept their own first, after, filter, and orderBy arguments. You do not have to fetch the parent first and then issue a follow-up query for child data.

The example below loads a paginated list of projects and, for each project, the most recently finished crawl -- all in a single request.

Operation: query GetAccountProjectsWithLatestCrawl($accountId: ObjectID!) { getAccount(id: $accountId) { id projects(first: 20, orderBy: [{ field: createdAt, direction: DESC }]) { nodes { id name primaryDomain lastCrawlStatus crawls(first: 1, orderBy: [{ field: finishedAt, direction: DESC }]) { nodes { id finishedAt statusEnum } } } pageInfo { hasNextPage endCursor } totalCount } } }Variables: { "accountId": "TjAwN0FjY291bnQ3MTU" }Response Example: { "data": { "getAccount": { "id": "TjAwN0FjY291bnQ3MTU", "projects": { "nodes": [ { "id": "TjAwN1Byb2plY3Q2MjAyMw", "name": "Lumar Docs", "primaryDomain": "https://www.lumar.io/", "lastCrawlStatus": "Finished", "crawls": { "nodes": [ { "id": "TjAwNUNyYXdsMTU4MzI0NQ", "finishedAt": "2025-03-20T14:30:00.000Z", "statusEnum": "Finished" } ] } } ], "pageInfo": { "hasNextPage": false, "endCursor": "MjA" }, "totalCount": 1 } } } }
GetAccountProjectsWithLatestCrawlTry in Explorer
GraphQL
query GetAccountProjectsWithLatestCrawl($accountId: ObjectID!) {
getAccount(id: $accountId) {
id
projects(first: 20, orderBy: [{ field: createdAt, direction: DESC }]) {
nodes {
id
name
primaryDomain
lastCrawlStatus
crawls(first: 1, orderBy: [{ field: finishedAt, direction: DESC }]) {
nodes {
id
finishedAt
statusEnum
}
}
}
pageInfo {
hasNextPage
endCursor
}
totalCount
}
}
}
warning

Nesting connections increases query cost. Keep first values small on nested connections — fetching 20 projects each with their own crawls connection can return a large amount of data quickly. Prefer smaller page sizes and paginate if you need more.

Requesting only what you need

Fetching fewer fields and shallower nesting makes queries faster and reduces the payload size. This matters both for latency and for staying within the rate limits.

Bloated query — slow and returns more data than needed

Operation: query GetProjectData($projectId: ObjectID!) { getProject(id: $projectId) { id name primaryDomain lastCrawlStatus createdAt updatedAt crawls(first: 50) { nodes { id statusEnum finishedAt createdAt crawlUrlsTotal crawlSegments(first: 100) { nodes { segmentId segmentVersion createdAt generatedAt failedAt failureReason } totalCount } } totalCount } } }Variables: { "projectId": "TjAwN1Byb2plY3Q2MjAyMw" }
GetProjectDataTry in Explorer
GraphQL
query GetProjectData($projectId: ObjectID!) {
getProject(id: $projectId) {
id
name
primaryDomain
lastCrawlStatus
createdAt
updatedAt
crawls(first: 50) {
nodes {
id
statusEnum
finishedAt
createdAt
crawlUrlsTotal
crawlSegments(first: 100) {
nodes {
segmentId
segmentVersion
createdAt
generatedAt
failedAt
failureReason
}
totalCount
}
}
totalCount
}
}
}

This query requests 50 crawls, each with 100 crawl segments and 6 fields per segment — up to 5,000 segment objects in a single response. Most callers only need a handful of fields to render a dashboard or trigger a workflow.

Lean query — fast and focused

Operation: query GetProjectCrawlStatus($projectId: ObjectID!) { getProject(id: $projectId) { id name lastCrawlStatus crawls(first: 5, orderBy: [{ field: createdAt, direction: DESC }]) { nodes { id statusEnum finishedAt } totalCount } } }Variables: { "projectId": "TjAwN1Byb2plY3Q2MjAyMw" }Response Example: { "data": { "getProject": { "id": "TjAwN1Byb2plY3Q2MjAyMw", "name": "Lumar Docs", "lastCrawlStatus": "Finished", "crawls": { "nodes": [ { "id": "TjAwNUNyYXdsMTU4MzI0NQ", "statusEnum": "Finished", "finishedAt": "2025-03-20T14:30:00.000Z" } ], "totalCount": 12 } } } }
GetProjectCrawlStatusTry in Explorer
GraphQL
query GetProjectCrawlStatus($projectId: ObjectID!) {
getProject(id: $projectId) {
id
name
lastCrawlStatus
crawls(first: 5, orderBy: [{ field: createdAt, direction: DESC }]) {
nodes {
id
statusEnum
finishedAt
}
totalCount
}
}
}

This version requests 5 crawls with 3 fields each and no nested connection. It returns in a fraction of the time.

tip

Rules of thumb:

  • Request only the fields you will actually use.
  • Keep first / last values as small as your use case allows.
  • Avoid nesting connection fields more than two levels deep unless necessary.
  • If you need large volumes of URL-level data, use report downloads instead of paginating crawlUrls.