Skip to main content

Query module

The query module facilitates interactions with Joystream GraphQL APIs exposed by:

It allows executing queries against those APIs in a fully type-safe way, without the need for any additional complex setup.

This is possible thanks to GenQL which generates a TypeScript GraphQL client from a GraphQL schema.

Create query API

import { QueryNodeApi } from "@joystream/sdk-core/query/queryNode";
const qnApi = new QueryNodeApi("https://mainnet.joystream.dev/query/graphql");

Configuration

All QueryApis (ie. QueryNodeApi, OrionApi, StorageSquidApi), accept an optional configuration object as a second argument to their constructors (after url):

export type Config = {
// Maximum size of an array of inputs to a single query
// (for example, max. chunk size of ids in `query.ENTITY.byIds`)
// Default: 1000
inputBatchSize: number
// Maximum number of results to fetch in a single query
// Default: 1000
resultsPerQueryLimit: number
// Maximum number of requests that can be sent concurrently to GraphQL server
// Default: 20
concurrentRequestsLimit: number
// Additional GenQL client options
clientOptions?: ClientOptions
}

Execute queries

Get entity by id

Queries an entity by its ID.

Syntax

qApi.query.ENTITY_NAME.byId(ID) // Selects all scalar fields qApi.query.ENTITY_NAME.byId(ID, SELECTION) // Selects specified fields

Examples

import { SnippetParams } from '../../snippet'

export default async function ({ qnApi, log }: SnippetParams) {
// @snippet-begin
// Get all scalar fields of member by id=336
const member = await qnApi.query.Membership.byId('336')
log(member)
// @snippet-end
}
import { SnippetParams } from '../../snippet'

export default async function ({ qnApi, log }: SnippetParams) {
// @snippet-begin
// Get proposal by id=9, along with some of its details
const proposal = await qnApi.query.Proposal.byId('9', {
__scalar: true, // Retrieve all scalar fields of Proposal
details: {
__typename: true,
on_CreateWorkingGroupLeadOpeningProposalDetails: {
group: {
name: true,
},
},
// ...handle other types if needed
},
})
log(proposal)
// @snippet-end
}
import { SnippetParams } from '../../snippet'

export default async function ({ qnApi, log }: SnippetParams) {
// @snippet-begin
// Get election by id, along with candidating members and their vote power
const election = await qnApi.query.ElectionRound.byId('00000014', {
__scalar: true, // Retrieve all scalar fields of ElectionRound
candidates: {
member: { id: true, handle: true },
votePower: true,
},
})
log(election)
// @snippet-end
}

Get first result

Retrieves first entity matching provided conditions.

Syntax

qApi.query.ENTITY_NAME.first({ where: WHERE_ARGS, select: SELECTION, // Optional, by default all scalar fields are selected orderBy: ORDER_BY_LIST // Optional })

Examples

import { SnippetParams } from '../../snippet'

export default async function ({ qnApi, log }: SnippetParams) {
// @snippet-begin
// Get all scalar fields of a member by their handle
const member = await qnApi.query.Membership.first({
where: { handle_eq: 'lezek' },
})
log(member)
// @snippet-end
}
import { SnippetParams } from '../../snippet'

export default async function ({ qnApi, log }: SnippetParams) {
// @snippet-begin
// Get id, handle and totalChannelsCreated of a member with highest number of channels created
const member = await qnApi.query.Membership.first({
select: { id: true, handle: true, totalChannelsCreated: true },
orderBy: ['totalChannelsCreated_DESC'],
})
log(member)
// @snippet-end
}

Get multiple entities by ids

Retrieves multiple entities by their ids.

Will execute multiple queries in case the list of ids is very large to avoid hitting the 2 MB request size limit.

The exact number of entities retrieved in a single query can be controlled with config.inputBatchSize.

Syntax

qApi.query.ENTITY_NAME.byIds(IDS) // Selects all scalar fields qApi.query.ENTITY_NAME.byIds(IDS, SELECTION) // Selects specified fields

Examples

import { SnippetParams } from '../../snippet'

export default async function ({ qnApi, log }: SnippetParams) {
// @snippet-begin
// Get all scalar fields of a few different members:
const members = await qnApi.query.Membership.byIds(['4129', '3234', '957'])
log(members)
// @snippet-end
}
import { SnippetParams } from '../../snippet'

export default async function ({ qnApi, log }: SnippetParams) {
// @snippet-begin
// Get a few proposals along with some of their details
const proposals = await qnApi.query.Proposal.byIds(['9', '10', '11'], {
__scalar: true,
details: {
on_CreateWorkingGroupLeadOpeningProposalDetails: {
group: {
name: true,
},
},
// ...handle other types if needed
},
})
log(proposals)
// @snippet-end
}

Get multiple entities from a list

Oftentimes you may have a list of values and would like to query some associated entities based on those values.

A specific example may be a list of ids, in which case you can use byIds method.

But those values may not always ids, they can also be, for example:

  • membership handles,
  • ids of an associated entity (e.g. bag ids of storage data objects),

If the list you have is very large, you may want to make use of the features that byIds method provides, like auto-chunking and query parallelization.

Fortunately this is possible with byMany method.

Syntax

qApi.query.ENTITY_NAME.byMany({   input: VALUES, // List of values to query from   where: WHERE_FUNCTION, // Takes a chunk of values and returns the where conditions   select: SELECTION, // Optional, by default all scalar fields are selected })

Examples

import { SnippetParams } from '../../snippet'

export default async function ({ qnApi, log }: SnippetParams) {
// @snippet-begin
// Get all scalar fields of a few different members by their handles:
const members = await qnApi.query.Membership.byMany({
input: ['leet_joy', 'Jenny', 'Codefikeyz'],
where: (handles) => ({ handle_in: handles }),
})
log(members)
// @snippet-end
}
import { SnippetParams } from '../../snippet'

export default async function ({ qnApi, log }: SnippetParams) {
// @snippet-begin
// Get specific fields of a few different members by their handles:
const members = await qnApi.query.Membership.byMany({
input: ['leet_joy', 'Jenny', 'Codefikeyz'],
where: (handles) => ({ handle_in: handles }),
select: {
id: true,
handle: true,
metadata: {
name: true,
about: true,
},
},
})
log(members)
// @snippet-end
}

Pagination

Pagination queries are useful for fetching larger quantities of data or loading more data on demand.

Joystream services use Subsquid GraphQL servers which provide different kinds of pagination.

  • Relay-style pagination using Connection queries (works in newer versions of Subsquid) is used by:
    • OrionApi
    • StorageSquidApi
  • Offset pagination using limit and offset (works in older version of Subsquid) is used by:
    • QueryNodeApi

The query module of Joystream SDK provides a simple interface for running queries with pagination.

Syntax

qApi.query.ENTITY_NAME.paginate({   select: SELECTION,   orderBy: ORDER_BY_LIST,   where: WHERE_ARGS,   pageSize: PAGE_SIZE, // Optional, config.resultsPerQueryLimit will be used by default })

The paginate method returns a Pagination object which matches the following interface:

interface Pagination<Entity> {
// True if next page is available, false otherwise.
hasNextPage: boolean

// Fetches next page of entities
nextPage(): Promise<Entity[]>

// Fetches all pages and combines them into a single result.
// This may take a while and consume a lot of memory, so use with caution!
fetchAll(): Promise<Entity[]>

// Fetches a specific number of entities, running multiple queries if needed.
fetch(items: number): Promise<Entity[]>
}

Examples

import { SnippetParams } from '../../snippet'

export default async function ({ qnApi, log }: SnippetParams) {
// @snippet-begin
// Get ids and handles of ALL members,
// fetching no more than 1000 members in a single query
const members = await qnApi.query.Membership.paginate({
orderBy: ['createdAt_ASC'],
select: { id: true, handle: true },
pageSize: 1000,
}).fetchAll()
log(members)
// @snippet-end
}
import { SnippetParams } from '../../snippet'

export default async function ({ qnApi, log }: SnippetParams) {
// @snippet-begin
// Fetch data about historical elections and log each page of 10 results separately
const electionPagination = qnApi.query.ElectionRound.paginate({
select: {
id: true,
isFinished: true,
candidates: {
member: {
id: true,
handle: true,
},
votePower: true,
},
},
where: { isFinished_eq: true },
orderBy: ['createdAt_DESC'],
pageSize: 10,
})
let i = 1
while (electionPagination.hasNextPage) {
const page = await electionPagination.nextPage()
log(`Page ${i}`)
log(page)
++i
}
// @snippet-end
}

Custom queries

If you have more specific needs, you can access the underlying GenQL client directly and take advantage of its type-safe interface to execute any GraphQL query you wish.

Examples

import { SnippetParams } from '../../snippet'

export default async function ({ qnApi, log }: SnippetParams) {
// @snippet-begin
const result = await qnApi.client.query({
postsByText: {
__args: {
text: 'Joystream',
limit: 10,
},
item: {
__typename: true,
on_ForumPost: {
id: true,
text: true,
},
},
},
})
log(result)
// @snippet-end
}

Read the GenQL documentation to find out more about how to use the GenQL client.