Using the Enjin API
Harness the full power of the Enjin Platform.
Introduction to the Enjin API
The Enjin API is built using GraphQL, a powerful query language for APIs that allows clients to request exactly the data they need. Unlike traditional RESTful APIs, which require multiple endpoints for different data requirements, GraphQL enables more efficient data retrieval through a single endpoint. This flexibility reduces the number of network requests and optimizes performance, making it particularly advantageous for applications that require efficient data handling, such as blockchain and NFT platforms.
What you'll need:
- Some Enjin Coin to pay for Transaction Fees.
You can obtain cENJ (Canary ENJ) for testing from the Canary faucet.- An Enjin Platform Account.
Understanding GraphQL vs. RESTful API Calls
To illustrate the differences between GraphQL and RESTful API calls, let's consider an example of creating a token. In a typical RESTful API, you might have an endpoint like /createToken
with a POST request that includes the necessary data in the request body. This might look something like:
RESTful API Example:
POST /createToken
Content-Type: application/json
{
"recipient": "cxLU94nRz1en6gHnXnYPyTdtcZZ9dqBasexvexjArj4V1Qr8f",
"collectionId": 2406,
"tokenId": 0,
"initialSupply": 1,
"cap": "INFINITE"
}
In contrast, GraphQL consolidates the operation into a single query or mutation, allowing you to specify precisely which fields you want to be returned. This reduces over-fetching and under-fetching of data.
GraphQL Example:
mutation CreateToken {
CreateToken(
recipient: "cxLU94nRz1en6gHnXnYPyTdtcZZ9dqBasexvexjArj4V1Qr8f",
collectionId: 2406,
params: {
tokenId: { integer: 0 },
initialSupply: 1,
cap: { type: INFINITE }
}
) {
id
method
state
}
}
In this GraphQL mutation, you define the CreateToken
operation and specify the parameters directly within the query. The response is tailored to include only the fields you request, such as id
, method
, and state
.
GraphQL's ability to query multiple resources in a single request, its strong type system, and its flexibility in querying data make it a powerful tool for interacting with the Enjin Blockchain, providing a seamless experience for developers building with NFTs.
With this understanding, you're ready to dive deeper into the Enjin API, starting with how to authenticate using access tokens.
Access Tokens
Access tokens assume a pivotal role in facilitating your application's interaction with the GraphQL API.
These tokens serve a dual purpose:
- They grant your application access to the platform.
- They allow for automatic request approval and signing using the Wallet Daemon.
To get started
- login to the Testnet Enjin Platform to create your account
- Create an API token
- When making HTTP requests to the GraphQL endpoints, add your API token to the headers to be authenticated, like so:
"Authorization": "<API Token Key Here>"
- To automate requests made from the Enjin Platform using a wallet daemon, you need to configure your daemon with the API token.
For more details head over to Using the Wallet Daemon
HTTP
Every data transfer strictly adheres to the HTTP/1.1 standard, with HTTPS encryption mandatory for all endpoints to ensure secure communication.
Since the GraphQL API is built upon HTTP, it seamlessly integrates with any programming language that supports HTTP libraries, including popular options like cURL
and urllib
.
Overview of GraphQL Endpoints
The Enjin API is structured around four distinct GraphQL endpoints, each designed to handle specific sets of queries and mutations. This segmentation allows for efficient and organized interactions tailored to different functional areas within the Enjin ecosystem:
-
Core Operations: This endpoint handles fundamental blockchain operations such as creating collections, minting and transferring MultiTokens, freezing, and burning MultiTokens . It provides the essential tools for managing and interacting with NFTs on the Enjin Blockchain.
-
Marketplace: Focused on marketplace activities, this endpoint encompasses operations like listing tokens for sale, purchasing tokens, and bidding on auctions. It facilitates seamless transactions and interactions within the Enjin Marketplace.
-
Beam: The Beam endpoint manages operations related to the Beam system, which is used for distributing tokens claimable via QR codes. This feature is particularly useful for promotional activities and engaging user experiences.
-
Fuel Tanks: This endpoint is dedicated to the Fuel Tank system, which allows for subsidizing transaction fees. It supports operations that manage and configure fuel tanks, enabling cost-effective transactions on the blockchain.
GraphQL Endpoints
Testnet:
- Core Operations
http://platform.canary.enjin.io/graphql
- Marketplace
http://platform.canary.enjin.io/graphql/marketplace
- Beam
http://platform.canary.enjin.io/graphql/beam
- Fuel Tanks
http://platform.canary.enjin.io/graphql/fuel-tanks
Mainnet:
- Core Operations
http://platform.enjin.io/graphql
- Marketplace
http://platform.enjin.io/graphql/marketplace
- Beam
http://platform.enjin.io/graphql/beam
- Fuel Tanks
http://platform.enjin.io/graphql/fuel-tanks
API Reference
For a comprehensive guide on exploring and utilizing GraphQL queries and mutations with the Enjin API, please refer to our API Reference.
GraphiQL Playground
The GraphiQL Playground serves three purposes:
- Explore available operations.
- Write operations using an intuitive interface.
- View responses and test your operations before coding them.
Step 1: Open GraphiQL Playground
Choose between Testnet or Mainnet.
Explore "Core Operations" for core operations like managing tokens, wallets, users, and transactions.
GraphiQL Playground:
Please be aware that the provided links are specifically for the GraphiQL Playground, a graphical user interface designed for exploring and interacting with GraphQL APIs, specifically for the Enjin Platform.
If you wish to execute GraphQL requests programmatically from your local environment or a cloud-based application, you will need to utilize the actual GraphQL endpoint URLs. These can be found in the HTTP section of our documentation.
Testnet:
Mainnet:
Step 2: Open the explorer
Click the Show GraphQL Explorer
button.
Here, you can view a complete list of available queries and mutations, along with the associated fields and arguments for each of them.
Step3: Add a query or mutation to start building your operation
Add a query
or mutation
into the "write" panel, and you'll see the list of available options in the explorer update to suit the operation type.
Step 4: Select which operations and datapoints you require
Click the required data points to automatically add them to the write column.
This feature eliminates any guesswork when structuring your operations and helps you make the most of the Enjin Platform Schema.
Step 5: Click the play button to execute your operation
To execute your operation and view the response, simply click the Execute Query
button.
You can execute any on-chain operation here. You can even mint tokens and view wallet data on Mainnet.
GraphQL Operation Structures
The Enjin API features an intricately crafted Platform Schema that is finely tuned to empower the development of scalable and high-speed blockchain-based games, ensuring an immersive experience for players.
To interface with it directly, you will need to understand how GraphQL operations are structured.
Operation Name
The Operation Name such as GetTokenInfo
is a user-defined identifier for a specific operation.
It allows you to organize and distinguish different operations within a single request, making it easier to handle responses and manage complex queries or mutations efficiently.
query GetTokenInfo { #'GetTokenInfo' is the Operation Name that distinguishes this operation from others
GetToken(
collectionId: 7153
tokenId: { integer: 10 }
) {
supply
}
}
{
"data": {
"GetToken": {
"supply": "1000"
}
}
}
Fields
Fields serve as a means to precisely specify the data you want to retrieve in the response of an operation.
Fields enable you to fine-tune the granularity of data retrieval, ensuring you receive precisely what you need.
When crafting Queries and Mutations, the Enjin Platform defines what fields are available for you to request.
For instance, when using the GetToken
query, you can request the supply
field, which corresponds to a specific piece of information stored on the blockchain.
query {
GetToken(
collectionId: 7153
tokenId: { integer: 10 }
) {
supply # supply is a Field that returns the token's supply
}
}
{
"data": {
"GetToken": {
"supply": "1000"
}
}
}
Arguments
You can pass data into an operation by using arguments.
Most Queries and Mutations define a set of input data you can use, for example in the GetToken
query you can pass in a collectionId
and a tokenId
argument.
query {
GetToken(
collectionId: 7153 #collectionId is an argument defining which collection to pull data from
tokenId: { integer: 10 } #tokenId is an argument defining which token to pull data from
) {
supply
}
}
{
"data": {
"GetToken": {
"supply": "1000"
}
}
}
Sometimes arguments will be required, sometimes they will be optional, and some operations may not take any arguments at all.
The API will tell you if you try to run an operation without a required argument.
Aliases
The alias, such as TokenInfo
and CurrentSupply
in the queries provided below, serve as a custom labels for specific fields within the operation.
Aliases allow you to rename the field in the response, making the data more intuitive and organized, especially when dealing with multiple fields or nested queries within a GraphQL request.
Operation Alias:
Usually used in batch operations, it allows you to provide a clear and descriptive name for each operations.
You might use it to distinguish between multiple similar queries or to provide context for the purpose of the query.
query GetTokenInfo {
TokenInfo: GetToken( #TokenInfo is the ALIAS that labels the field
collectionId: 7153
tokenId: { integer: 10 }
) {
supply
}
}
{
"data": {
"TokenInfo": {
"supply": "1000"
}
}
}
Field Alias:
This option is useful when you want to label a specific field within the response.
It allows you to rename individual fields in the response for clarity or to avoid naming conflicts.
query GetTokenInfo {
GetToken(
collectionId: 7153
tokenId: { integer: 10 }
) {
CurrentSupply: supply
}
}
{
"data": {
"GetToken": {
"CurrentSupply": "1000"
}
}
}
Variables
Various code blocks in the Enjin Documentation use variables instead of hardcoding arguments. This allows the dynamic insertion of data as you run mutations and queries.
For example, when a player picks up an item, your game needs to fetch the token information using the token's ID. To dynamically insert the token ID to the query, we should use a variable.
That way, we will have one "template" for token fetching query, and the game will decide which token to fetch based on the player actions.
To work with variables in GraphQL, we first need to declare the $variableName
, as the variables accepted by the operation. Our operations has two arguments - Collection ID and Token ID, so we'll name the variables $collection_id
and $token_id
respectively. Both arguments are of type BigInt
, so the variable declaration will look like that:
query GetTokenInfo($collection_id: BigInt! $token_id: BigInt) {
Note the exclamation mark (!) after the first variable declaration type BigInt. This represents a non-nullable variable (essentially a required field).
We also need to replace the static value in the operation with the $variableName
GetToken(
collectionId: $collection_id //Collection ID 7153 was hardcoded here
tokenId: { integer: $token_id } //Token ID 10 was hardcoded here
)
And the final step is to pass variableName: value
in a separate, transport-specific (usually JSON) variables dictionary:
{
"collection_id": 7153,
"token_id": 10
}
Here's the final operation code:
Query:
query GetTokenInfo($collection_id: BigInt! $token_id: BigInt) {
GetToken(
collectionId: $collection_id
tokenId: { integer: $token_id }
) {
supply
}
}
Variables:
{
"collection_id": 37108,
"token_id": 0
}
Types
Types in GraphQL serve as blueprints for defining the shape and composition of the data available through the API, encompassing a collection of fields representing specific pieces of information.
These structures lay the foundation for shaping queries and mutations, ensuring precise data retrieval and manipulation.
Query:
A query
is used to retrieve data from the Enjin Platform.
In this example, we're querying for information about a specific token.
query GetTokenInfo {
GetToken(
collectionId: 7153
tokenId: { integer: 10 }
) {
supply # Returns the current supply of the token
}
}
{
"data": {
"TokenInfo": {
"supply": "1000"
}
}
}
The GetToken
query retrieves token information.
We specify the collectionId
and tokenId
as parameters to identify the token we want.
The fields tokenId
and supply indicate the data we want to receive in the response.
Mutation:
A mutation
is used to modify data on the Enjin Platform. For example, you can use a mutation to update a user's profile information.
In this example, we're minting a new token.
mutation MintToken {
MintToken(
recipient: "0xaa89f9099742a928051c41eadba188ad4e863539ff96f16722ae7850271c2921"
collectionId: "7154"
params: {
amount: 1
tokenId: { integer: 6533 }
}
) {
id # Returns the transaction ID
}
}
{
"data": {
"MintToken": {
"id": "1234" // request ID
}
}
}
The MintToken
mutation mints a new token.
We provide the necessary arguments, such as recipient
, collectionId
, and params
, to define the minting process.
The field id
indicate the data we want to receive in the response.
Batch Query:
A batch query
allows you to send multiple mutation operations in a single request to reduce network overhead.
Each query have its own set of parameters and return data.
query BatchGetTokenInfo {
TokenInfo1: GetToken(
collectionId: 7153 # Specifies the collection ID for the first token
tokenId: { integer: 10 } # Specifies the unique identifier of the first token
) {
supply # Returns the current supply of the first token
}
TokenInfo2: GetToken(
collectionId: 4553 # Specifies the collection ID for the second token
tokenId: { integer: 55 } # Specifies the unique identifier of the second token
) {
supply # Returns the current supply of the second token
}
}
{
"data": {
"TokenInfo1": {
"supply": 100 // Current supply of the first token
},
"TokenInfo2": {
"supply": 200 // Current supply of the second token
}
}
}
In this batch query, we retrieve information for two different tokens with distinct collectionId
and tokenId
.
Each token's data is aliased using TokenInfo1
and TokenInfo2
.
Batch Mutation
A batch mutation
allows you to send multiple mutation operations in a single request to reduce network overhead.
Each mutation can have its own set of parameters and return data.
mutation {
MintToken1: MintToken(
recipient: "0xaa89f9099742a928051c41eadba188ad4e863539ff96f16722ae7850271c2921" # Specifies the recipient's address for the first MintToken operation
collectionId: "7154" # Specifies the collection ID for the first MintToken operation
params: {
amount: 1 # Specifies the amount to mint for the first MintToken operation
tokenId: { integer: 6533 } # Specifies the unique identifier of the first token to mint
}
) {
id # Returns the request ID for the first MintToken operation
}
MintToken2: MintToken(
recipient: "0x1234567890abcdef" # Specifies the recipient's address for the second MintToken operation
collectionId: "7155" # Specifies the collection ID for the second MintToken operation
params: {
amount: 2 # Specifies the amount to mint for the second MintToken operation
tokenId: { integer: 6534 } # Specifies the unique identifier of the second token to mint
}
) {
id # Returns the request ID for the second MintToken operation
}
}
{
"data": {
"MintToken1": {
"id": "1234" // request ID for the first MintToken operation
},
"MintToken2": {
"id": "1235" // request ID for the second MintToken operation
}
}
}
In this batch mutation, we have two separate MintToken
mutations, labeled using the MintToken1
and MintToken2
aliases.
Both mutations are minting two different tokens into two separate addresses.
Pagination
Some GraphQL queries returns results in multiple pages. In GraphQL, these are called "Connections", and each of the elements in the connection is called "Edge".
For example, the GetWallet query returns an object of type Wallet. Wallet objects contains tokenAccount which is of type TokenAccountConnection. Since this object is of type 'connection', it uses pagination.
By default, if you don't specify which page to query, the first page will be returned.
To check if the response contains another page for a certain object, use the pageInfo.hasNextPage property from the connection.
query FetchingWalletTokens{
GetWallet(account: "cxKnfok66R8BAuzGcypxqirYcqW7E9Spn4z5UZSVRuUHAcrTQ"){
tokenAccounts{
pageInfo{
hasNextPage #Returns whether there's a next page or not
}
edges{
node{
token{
tokenId
}
balance
}
}
}
}
}
{
"data": {
"GetWallet": {
"tokenAccounts": {
"pageInfo": {
"hasNextPage": true // Indicates that there is another page available.
},
"edges": [
{
"node": {
"token": {
"tokenId": "5"
},
"balance": "1"
}
},
{...},
{...}
]
}
}
}
}
By default, this connection returns 15 edges (tokens) per page.
Note: The amount of edges per page can be adjusted by using slicing.
To navigate to the next page, use pagination combined with the edge cursor.
First, add the cursor field to the query response. (Alternatively, you can add the pageInfo.endCursor field to fetch the cursor of the last edge in the connection)
query FetchingWalletTokens{
GetWallet(account: "cxKnfok66R8BAuzGcypxqirYcqW7E9Spn4z5UZSVRuUHAcrTQ"){
tokenAccounts{
pageInfo{
hasNextPage
endCursor #Returns The cursor of the last tokenAccount in the current page
}
edges{
cursor #Returns The tokenAccount cursor
node{
token{
tokenId
}
balance
}
}
}
}
}
{
"data": {
"GetWallet": {
"tokenAccounts": {
"pageInfo": {
"hasNextPage": true,
"endCursor": "eyJpZCI6Mjk0NzQsIl9wb2ludHNUb05leHRJdGVtcyI6dHJ1ZX0" // The cursor of the last tokenAccount in the current page
},
"edges": [
{
"cursor": "eyJpZCI6MzU3NCwiX3BvaW50c1RvTmV4dEl0ZW1zIjp0cnVlfQ", // The tokenAccount cursor
"node": {
"token": {
"tokenId": "5"
},
"balance": "1"
}
},
{...},
{...},
{
"cursor": "eyJpZCI6Mjk0NzQsIl9wb2ludHNUb05leHRJdGVtcyI6dHJ1ZX0", // The tokenAccount cursor
"node": {
"token": {
"tokenId": "11"
},
"balance": "1"
}
}
]
}
}
}
}
Now we have the cursor of the last edge of the current page.
To navigate to the next page, add the after
parameter to the tokenAccounts
connection, and insert the cursor of the last edge of the current page.
query FetchingWalletTokens{
GetWallet(account: "cxKnfok66R8BAuzGcypxqirYcqW7E9Spn4z5UZSVRuUHAcrTQ"){
tokenAccounts(after: "eyJpZCI6Mjk0NzQsIl9wb2ludHNUb05leHRJdGVtcyI6dHJ1ZX0"){ #Indicates that we want to fetch tokenAccounts after the specified cursor.
pageInfo{
hasNextPage
endCursor
}
edges{
cursor
node{
token{
tokenId
}
balance
}
}
}
}
}
{
"data": {
"GetWallet": {
"tokenAccounts": {
"pageInfo": {
"hasNextPage": true,
"endCursor": "eyJpZCI6NTE2MzgsIl9wb2ludHNUb05leHRJdGVtcyI6dHJ1ZX0"
},
"edges": [
{
"cursor": "eyJpZCI6Mzc2MTYsIl9wb2ludHNUb05leHRJdGVtcyI6dHJ1ZX0",
"node": {
"token": {
"tokenId": "9"
},
"balance": "1"
}
},
{...},
{...},
{
"cursor": "eyJpZCI6NTE2MzgsIl9wb2ludHNUb05leHRJdGVtcyI6dHJ1ZX0",
"node": {
"token": {
"tokenId": "1"
},
"balance": "1"
}
}
]
}
}
}
}
To get all edges in the connection, continue navigating to the next page with a loop.
You'll know that you reached the last page when the hasNextPage
returns false
.
For more information, refer to the official GraphQL documentation.
Or, If you're ready to start building... Create a Collection.
Updated about 2 months ago