Site icon Chainstack

Developing DApps: The ultimate guide to building on Chainstack

Developing Dapps Ultimate Guide 1024x576 logo

Welcome, aspiring Web3 developers! We at Chainstack understand that the blockchain ecosystem can often seem overwhelming with its vastness and nuances. That’s why we’ve put together this comprehensive guide designed specifically with you in mind.

With Chainstack, developing decentralized applications becomes a streamlined process. We’re here to help you navigate the complexities and make the most out of our diverse range of supported blockchain networks and features.

This guide covers everything from choosing the ideal blockchain network for your DApp, making sense of Layer 1 and Layer 2 networks, and leveraging the reliability-enhancing features of Chainstack, to interpreting error codes and selecting apt authentication methods.

Whether you’re just starting your Web3 journey or looking to refine your development processes, this guide offers the essential insights to elevate your projects. Let’s explore together!

How to choose the right chain for your project?

Your choice of blockchain protocol can determine the fate of your project. Therefore, a comprehensive understanding of Layer 1 and Layer 2 networks can prove invaluable.

Layer 1 networks

Layer 1 networks are the foundational blockchains that foster decentralization and utmost security. Examples include Ethereum, BNB Smart Chain, and so forth. They offer a robust, trustless environment for DApps but can sometimes be hindered by scalability issues and high gas fees. Their key characteristics include:

Leading Layer 1 networks supported by Chainstack

Layer 2 networks

Contrastingly, Layer 2 networks are the secondary networks or ‘off-chain’ solutions designed to overcome the limitations of Layer 1. They are often quicker, more scalable, and have lower transaction fees. These include Plasma, Rollups, and more. Key characteristics of L2 networks include:

Top layer 2 networks supported by Chainstack

Making an informed choice between Layer 1 and Layer 2 requires getting to grips with the advantages and limitations of both. As each DApp has its unique requirements and audiences, there isn’t a one-size-fits-all solution. Read Solving the Blockchain Trilemma: A Look at Some Scaling Solutions to better understand Layer 2 solutions.

How to make your DApp more reliable with Chainstack?

As DApp developers, you’re tasked with the mission to create decentralized applications that are not only innovative but run seamlessly, ensuring a satisfactory user experience. That’s where we step in, offering robust solutions like our Global Nodes that amplify your DApp’s reliability and performance.

Let’s break it down. A Global Node allows access to Ethereum nodes worldwide, which in turn ensures high-grade uptime and performance in every corner of the globe. This unique feature of Chainstack can drastically reduce latency, leading to swift and efficient transactions.

Our nodes aren’t confined to specific locations. They are present in diversely located data centers worldwide, ensuring your Dapp remains operational round-the-clock, regardless of your audience’s geographical position. And the best part? This wide spectrum is automatically available to you by default—no extra step, no added configuration.

The main advantages of Chainstack Global Nodes are as follows:

How to integrate Chainstack Global Nodes into your project?

By leveraging Chainstack Global Nodes, you can take your DApp’s performance to the next level, leaving behind worries of network congestion or node collapse. Get ready to witness the power of a seamless global infrastructure in action.

Web3JS example:

const { Web3 } = require("web3");
const NODE_URL = "YOUR_CHAINSTACK_WSS_ENDPOINT";

// Reconnect options
const reconnectOptions = {
  autoReconnect: true,  // Automatically attempt to reconnect
  delay: 5000,          // Reconnect after 5 seconds
  maxAttempts: 10,      // Max number of retries
};

const web3 = new Web3(
  new Web3.providers.WebsocketProvider(NODE_URL, undefined, reconnectOptions)
);

async function subscribeToNewBlocks() {
  try {
    // Create a new subscription to the 'newBlockHeaders' event
    const event = "newBlockHeaders";
    const subscription = await web3.eth.subscribe(event); // Changed to 'newHeads'

    console.log(`Connected to ${event}, Subscription ID: ${subscription.id}`);

    // Attach event listeners to the subscription object for 'data' and 'error'
    subscription.on("data", handleNewBlock);
    subscription.on("error", handleError);
  } catch (error) {
    console.error(`Error subscribing to new blocks: ${error}`);
  }
}

// Fallback functions to react to the different events

// Event listener that logs the received block header data
function handleNewBlock(blockHeader) {
  console.log("New block header:", blockHeader);
}

// Event listener that logs any errors that occur
function handleError(error) {
  console.error("Error when subscribing to new block header:", error);
}

subscribeToNewBlocks();

ethersJS example:

const ethers = require("ethers");
const WebSocket = require("ws");

const NODE_URL =
  "YOUR_CHAINSTACK_WSS_ENDPOINT";

function createWebSocket() {
  const ws = new WebSocket(NODE_URL);

  ws.on("close", () => {
    console.log("Disconnected. Reconnecting...");
    setTimeout(() => {
      provider = new ethers.WebSocketProvider(createWebSocket());
      startListening();
    }, 3000);
  });

  ws.on("error", (error) => {
    console.log("WebSocket error: ", error);
  });

  return ws;
}

let provider = new ethers.WebSocketProvider(createWebSocket());

function startListening() {
  provider.on("block", async (blockNumber) => {
    console.log("New block number:", blockNumber);
    const block = await provider.getBlock(blockNumber);
    console.log("Block details:", block);
  });
}

startListening();

How to use API endpoints on Chainstack

API endpoints are essential for your DApp, as they define the routes for interacting with the database and handling client-server communication effectively. With an appropriate understanding and utilization of various API endpoints, you, as a Web3 developer, can elevate the performance of your DApp.

In Chainstack, each API endpoint corresponds to a different datatype, service, or function. These endpoints allow your DApp to interact with protocol and node data, among other services, facilitating seamless data exchange.

https://chainstack.com/wp-content/uploads/2024/06/Deploying-an-Elastic-Node-in-Advanced-mode-on-Chainstack.mp4
Figure 1: Deploying a Global Node in Advanced mode on Chainstack demo

Always remember, that good command over API endpoints not only enhances your application’s performance but also elevates the end-user experience to a new height. It’s like mastering the art of communication in the digital world, and as a developer, you would know how vital that can be!

Below are examples in JavaScript and Python to help you get familiar with the Chainstack platform API.

API key authentication

The Chainstack API uses API keys to authenticate requests. Include your API key in the Authorization header. The header value should be the Bearer prefix followed by the secret key generated through the platform’s user interface.

Here’s an example using curl:

curl -X GET '<https://api.chainstack.com/v1/organization/>' \\\\
--header 'Authorization: Bearer YOUR_API_KEY'

API calls using JavaScript

This example shows how to interact with the Chainstack platform API using Node.js and the Axios library. It illustrates how to communicate with the API using code.

Set the environment variable:

BEARER_TOKEN="YOUR_API_KEY"

Ensure you install axios and dotenv before running the code:

npm i axios dotenv

require('dotenv').config();
const axios = require('axios');

async function getOrganization() {
  try {
    const response = await axios.get('<https://api.chainstack.com/v1/organization/>', {
      headers: {
        'Authorization': `Bearer ${process.env.BEARER_TOKEN}`
      }
    });
    console.log(response.data);
  } catch (error) {
    console.error(error);
  }
}

getOrganization();

This example uses the “Get Organization name and ID” endpoint to fetch information about the organization. It’s a straightforward way to incorporate API calls into your routine tasks.

API calls using Python

This example demonstrates how to interact with the Chainstack platform API using Python and the requests library. It shows how to communicate with the API using Python code.

Ensure you install requests and python-dotenv before running the code:

pip install requests python-dotenv

import os
from dotenv import load_dotenv
import requests

load_dotenv()

def get_nodes():
    try:
        response = requests.get(
            '<https://api.chainstack.com/v1/nodes/>',
            headers={'Authorization': f'Bearer {os.getenv("BEARER_TOKEN")}'}
        )
        response.raise_for_status()
        print(response.json())
    except requests.exceptions.RequestException as err:
        print(f"An error occurred: {err}")

get_nodes()

This example calls the “List all Nodes” endpoint to fetch data about the nodes deployed by your organization. This makes it easy to introduce API calls into your Python workflow.

Best practices for error handling in API requests

While developing DApps, handling HTTP status codes is vital to ensure a responsive and glitch-free interaction for your users. These codes, three-digit numbers, provide both users and developers with a snapshot of how the request has fared.

Successes, for example, are generally coded with 2xx, but you’re more likely to encounter 4xx and 5xx when things aren’t as smooth. The 400 series is used when the client seems to be at fault, say, owing to a bad request or unauthorized access. On the other hand, the 500 series indicates that the problem pertains to server errors.

Implementing proper error handling in your DApps can substantially enhance customer experience, as users will be provided detailed feedback in case something goes wrong, guiding their next actions. Though it might seem an uphill task, keep in mind that understanding and correctly working with these status codes can spell the difference between a great DApp and a mediocre one.

Practical example for error handling in API requests

To handle HTTP status codes, we first need to know how to retrieve them. In Python, this can be done using the status_code attribute of the response object. This attribute holds the status code returned by the server for the HTTP request.

Let’s consider a scenario where we want to get the logs of the latest block. Here is an example of how to do this using Python:

import json
import requests

node_url = 'YOUR_CHAINSTACK_ENDPOINT'

headers = {
    "Content-Type": "application/json"
}
payload = {
    "jsonrpc": "2.0",
    "method": "eth_getLogs",
    "params": [
        {
            "fromBlock": "latest",
            "toBlock": "latest",
        }
    ],
    "id": 1
}

response = requests.post(node_url, headers=headers, json=payload)
print(response.text)

If the above code runs successfully, it will output the logs for the latest block. This indicates that the response code received by the client (you, who made the request) was 200. To retrieve the response code of the request shown above, you can use the following:

# Considering this request:
response = requests.post(node_url, headers=headers, json=payload)

# Here's how we can get the response code for such request:
response_code = response.status_code
print('status code:', response_code)

This will store the HTTP status code of the response in the status_code attribute. Now that you know how to retrieve the status code of a response, you can move on to handling these codes and analyzing error responses.

Analyzing error responses

In addition to dealing with response codes, it’s also important to analyze other information in the response to understand and address errors. This can be particularly useful when the server returns a 4xx or 5xx status code, indicating a client or server error.

For instance, let’s consider a possible response for an eth_getLogs request that contains an error message:

{"jsonrpc":"2.0","id":1,"error":{"code":-32000,"message":"failed to get logs for block #192001 (0xa388fd..65beb8)"}}

In this case, the server returned a JSON object with an error field, which contains additional information about the error that occurred. We can extract this information in our Python code as follows:

response = requests.post(node_url, headers=headers, json=payload)
response_code = response.status_code
print('status code:', response_code)

if response_code == 200:
    response_data = json.loads(response.text)
    if 'error' in response_data:
        error_content = response_data['error']
        print('Error:', error_content)

In this code, we first check if the response’s status code is 200, indicating a successful request. If it is not, we parse the JSON content of the response and check if it contains an error field. If it does, we store the content of this field in the error_content variable. This information can be used to implement retry logic and keep a record of whenever these errors occur.

Importance of implementing retry logic

Incorporating retry logic into your code can significantly enhance the reliability of your application. By leveraging the tools and techniques we have discussed, you can implement a retry mechanism that automatically handles temporary failures and retries the request when necessary. This can reduce the impact of temporary failures, increase system availability, and ensure data integrity. In the worst-case scenario, this enables you to keep track of the errors you face with precise timestamps.

Implementing retry logic is particularly important when dealing with 5xx server errors. These errors indicate a problem with the server and are often temporary. By implementing retry logic, your application can automatically retry the request after a short delay, giving the server a chance to recover. This can significantly improve the user experience by reducing the number of failed requests the user has to deal with.

Implementing retry logic in code

Now that we understand the importance of implementing retry logic, let’s dive into how to implement it in our Python code. Our retry logic aims to automatically retry the request when a temporary failure occurs, such as a 5xx server error or a connection error.

Here’s an example of how to implement retry logic in Python, using both the response code and error messages to determine when to retry a request:

import json
import time
import requests

node_url = 'YOUR_CHAINSTACK_ENDPOINT'
headers = { "Content-Type": "application/json" }
payload = { 
    "jsonrpc": "2.0", 
    "method": "eth_getLogs", 
    "params": [ {"fromBlock": "latest", "toBlock": "latest"} ],
    "id": 1
}

# Max retries
retries = 5
delay = 1 # Seconds

def get_logs():
    for i in range(retries):
        response = requests.post(node_url, headers=headers, json=payload)
        
        if response.status_code != 200:
            print(f"Request failed with status code {response.status_code}. Retrying attempt {i+1}...")
            time.sleep(delay)
            continue
        
        response_data = json.loads(response.text)
        
        if 'error' in response_data:
            print(f"There was an error in attempt {i+1}: {response_data['error']}")
            time.sleep(delay)
            continue
        
        logs = response_data.get("result", [])
        
        if len(logs) > 0:
            block_number = int(logs[0]['blockNumber'], 16)
            print(f"Block number from eth_getLogs call: {block_number} in attempt {i+1}")
            print('Processing the event logs...')
            return
        
        print(f"Result is empty for this block in attempt {i+1}")                
        time.sleep(delay)

get_logs()

The retry logic is governed by a for loop that runs up to a predefined maximum number of attempts (the retries variable). For each iteration of the loop, which represents an attempt to fetch the logs, the code performs the following steps:

  1. Send request: A POST request is sent to the Ethereum node with the defined headers and payload.
  2. Check status code: If the HTTP status code of the response is not 200 (indicating a successful request), the code prints a message indicating the request failed and the current attempt number. Then, it waits for the specified delay period (the delay variable) before proceeding to the next iteration of the loop. This delay helps in cases where the server might be temporarily overloaded or experiencing other transient issues.
  3. Handle errors: If the status code is 200, the response is parsed into JSON format and checked for an error key. If error is present, the code prints a message with the error details and the current attempt number, waits for the specified delay period, and proceeds to the next iteration of the loop. This handles cases where the request was technically successful, but the response indicates an error condition that might be resolved with a retry.
  4. Check empty results: If there’s no error key but the result is empty, the code prints a message indicating this fact and the current attempt number, waits for the specified delay period, and proceeds to the next iteration of the loop. This handles situations where the request was successful but didn’t provide any logs to process.

If the function hasn’t returned by the end of the loop (meaning it hasn’t successfully processed a set of logs), it will have retried the request the maximum number of times. At this point, the function will exit, effectively giving up on fetching logs after exhausting all the allowed attempts.

Using response codes and error messages in retry logic

As seen in the example, both the response code and error messages are used in the retry logic. The response code indicates whether the request was successful, while error messages provide detailed information about what went wrong.

By using both pieces of information, the retry logic becomes more intelligent and effective. For instance, the request can be retried immediately if the error message indicates a temporary problem, or it can wait for a longer delay if the error message indicates a more serious issue.

Logging error messages also helps keep a record of the errors, which is useful for debugging and improving the application.

Common problems and gotchas

While handling HTTP status codes and implementing retry logic can improve the reliability of your application, there are some common problems and gotchas to be aware of.

Importance of effective retry logic and robust error backlogs

Without effective retry logic and robust error backlogs, your application may not recover from temporary failures, leading to a poor user experience and potential data loss.

An effective retry logic should consider the nature of the error and adjust its behavior accordingly. For example, if the error is temporary (such as a 5xx server error), the retry logic should wait for a short delay before retrying the request. If the error is permanent (such as a 4xx client error), the retry logic should not retry the request, but rather log the error and notify the user.

A robust error backlog helps track errors in your application, allowing for more effective debugging and issue resolution. It also provides valuable insights into the performance and reliability of your application, helping to identify areas for improvement.

How to create a .env file with all your Chainstack endpoints in Python

Chainstack offers a robust API that enables efficient data retrieval about your projects and endpoints. In this example, we will use the “list all nodes” API.

This API allows you to seamlessly extract a comprehensive list of nodes associated with your account. Additionally, we will demonstrate how to automate the creation of a .env file with the fetched data.

Follow these steps to create a Python script that retrieves endpoint data from the Chainstack API and automates the creation of a .env file with the fetched data.

Step 1: Create a new Python file

  1. Create file: Create a new Python file in your project’s root directory (the same location where you created the .env file). Name this file get_endpoints.py.
  2. Purpose: This naming convention indicates the file’s purpose, which is to retrieve endpoint data.

Step 2: Add code to the Python file

  1. Open file: Open get_endpoints.py in your preferred code editor.
  2. Paste script: Add the following Python script designed to interact with the Chainstack API, retrieve blockchain node information, and create a configuration file with these details.
import requests
import os
import logging
from dotenv import load_dotenv
from web3 import Web3
from typing import Optional, Dict, Any

# Load environment variables
load_dotenv()

# Constants
CHAINSTACK_API_KEY = os.getenv('CHAINSTACK_API_KEY')
OUTPUT_FILE_NAME = 'rpc.env'

# Initialize logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

def fetch_chainstack_data(api_key: str) -> Optional[Dict[str, Any]]:
    """Fetch data from Chainstack API."""
    url = "<https://api.chainstack.com/v1/nodes/>"
    headers = {
        "accept": "application/json",
        "authorization": f"Bearer {api_key}"
    }

    try:
        response = requests.get(url, headers=headers)
        response.raise_for_status()
        data = response.json()
        logging.info(f"Fetched {len(data.get('results', []))} items from Chainstack.")
        return data
    except requests.RequestException as e:
        logging.error(f"Failed to fetch data from Chainstack: {e}")
        return None

def process_chainstack_item(item: Dict[str, Any]) -> Dict[str, str]:
    """Process a single item from Chainstack data."""
    logging.debug(f"Processing item: {item['name']} with ID {item['id']}")
    return {
        'id': item['id'],
        'name': item['name'],
        'details': item['details'],
        'http_endpoint': item['details'].get('https_endpoint'),
        'auth_key': item['details'].get('auth_key'),
        'configuration': item['configuration'],
        'client': item['configuration'].get('client')
    }

def connect_to_web3(reconstructed_endpoint: str) -> bool:
    """Connect to a Web3 endpoint."""
    logging.debug(f"Attempting to connect to Web3 endpoint: {reconstructed_endpoint}")
    try:
        w3 = Web3(Web3.HTTPProvider(reconstructed_endpoint))
        if w3.is_connected():
            chain_id = w3.eth.chain_id
            logging.info(f"Connected to {reconstructed_endpoint} with chain ID: {chain_id}")
            return True
        else:
            logging.warning(f"Failed to connect to {reconstructed_endpoint}")
    except Exception as e:
        logging.error(f"An error occurred while connecting to {reconstructed_endpoint}: {e}")
    return False

def sanitize_name(name: str) -> str:
    """Sanitize the endpoint name for use as an environment variable key."""
    return name.replace(" ", "_").replace("-", "_").replace("/", "_").upper()

def create_env_file(endpoint_info_dict: Dict[str, Dict[str, str]], filename: str = OUTPUT_FILE_NAME) -> None:
    """Create a .env file from the endpoint info dictionary."""
    logging.info(f"Preparing to write {len(endpoint_info_dict)} endpoints to .env file.")
    with open(filename, 'w') as file:
        for endpoint, info in endpoint_info_dict.items():
            sanitized_name = sanitize_name(info['name'])
            file.write(f'{sanitized_name}_URL="{endpoint}"\\\\n')
        logging.info(f".env file created successfully at {filename}.")

def main() -> None:
    """Main function to orchestrate the process."""
    logging.info("Starting main process.")
    if not CHAINSTACK_API_KEY:
        logging.error("Chainstack API key not found.")
        return

    json_data = fetch_chainstack_data(CHAINSTACK_API_KEY)
    if not json_data:
        return

    endpoint_info_dict = {}
    for item in json_data.get('results', []):
        data = process_chainstack_item(item)
        reconstructed_endpoint = f"{data['http_endpoint']}/{data['auth_key']}"
        if connect_to_web3(reconstructed_endpoint):
            endpoint_info_dict[reconstructed_endpoint] = {'name': data['name']}

    if endpoint_info_dict:
        create_env_file(endpoint_info_dict)
    else:
        logging.info("No endpoint information to write to .env file.")
    logging.info("Main process completed.")

if __name__ == "__main__":
    main()

Explanation of the script

Environment setup and logging

Fetch data from the Chainstack API

Process Chainstack data

Test Web3 connection

Sanitize data and create .env file

Main execution flow

Executing this script will efficiently process and validate your Chainstack endpoints. It identifies those endpoints that successfully return a chain ID, indicating their active and functional status. These validated endpoints are then neatly recorded into a .env file.

With this setup, you gain a streamlined and organized method to access and utilize all your Chainstack endpoints. This approach simplifies endpoint management and enhances the overall efficiency of your interactions with Chainstack services. Note that non-EVM endpoints or improperly formatted ones will not work and will show a warning, which you can build on and improve.

Authentication methods available on Chainstack

Safeguarding data and asserting secure interactions is paramount. API authentication is essential in application programming interface (API) development, as it verifies the identities of applications or users utilizing the API.

Chainstack prides itself on offering several secure API authentication methods that can fortify your DApps against unauthorized breaches, each with its advantages and considerations. Let’s briefly explore the five primary methods used for authentication first, keeping in mind only the first two are actively used on Chainstack.

API keys

API keys are the simplest form of API authentication. The client includes an API key, a unique identifier, in the header or as a parameter in the URL. The server matches the key to a corresponding key in its database and, if it matches, grants access. Although easy to implement, API keys can be misused if intercepted.

On Chainstack, API keys are typically used with the platform API.

Basic authentication

Basic authentication involves sending a user ID and password with each API request. The credentials are Base-64 encoded but not encrypted, making them easily decipherable by anyone who intercepts the transmission. Basic authentication should always be used over HTTPS to add an additional layer of security.

While basic authentication can be used for nodes, it won’t work with the Chainstack platform API, as API keys are used for that instead.

Digest authentication

Digest authentication is a step up from basic authentication, where the client sends a hashed (or digested) version of the password. It’s more secure than basic authentication because even if an attacker intercepts the hashed password, they cannot use it to make API requests.

Digest authentication is not typically used on Chainstack.

OAuth (Open Authorization)

OAuth is a more complex but secure authentication method. It enables third-party applications to make requests on behalf of a user without needing their password. OAuth2, the latest version, uses short-lived access tokens rather than user credentials for authentication.

OAuth authentication is not typically used on Chainstack.

JWT (JSON Web Tokens)

JWT is a token-based authentication that allows for stateless authentication. An encoded string of characters represents a payload of data, which often includes issued at time (iat), expiration time (exp), and not before (nbf) statements.

JWT authentication is not typically used on our platform with the exception of some Chainstack Marketplace applications.

Comparison table

MethodSecurityComplexity
API KeyLowLow
Basic AuthenticationMedium (if used over HTTPS)Low
Digest AuthenticationMediumMedium
OAuthHighHigh
JWTHighMedium

Choosing the right authentication method for your API depends on your specific use case, including your security needs and the resources available for implementation.

Header authentication (bearer token)

Header authentication with a bearer token is a common method employed in API requests. This approach involves attaching an authorization header with a bearer token in each HTTP request to the server. This token is a cryptic string, ensuring that data access is only granted to the token’s bearer, hence the name.

In the context of the Chainstack platform, header authentication using a bearer token is fully supported for platform API requests. Users can authenticate their API calls on the platform by including the bearer token in their request headers.

However, bearer token authentication is currently unavailable for blockchain APIs. Blockchain nodes typically do not provide traditional user-based authentication. Instead, Chainstack uses API keys or similar mechanisms to authenticate requests to hosted nodes, which are not traditional bearer tokens.

Example of sending a header authenticated request to Chainstack API

To learn how to generate your Chainstack API key, refer to the documentation.

cURL example

curl --request GET \\\\
     --url <https://api.chainstack.com/v1/organization/> \\\\
     --header 'accept: application/json' \\\\
     --header 'authorization: Bearer YOUR_CHAINSTACK_API_KEY'

This example calls the Get Organization Name and ID API, which returns the organization name and ID associated with the API key. Replace YOUR_CHAINSTACK_API_KEY with the API key from the Chainstack console.

Example response

{
  "id": "RG-123-456",
  "name": "Cool Organization"
}

Selecting the appropriate authentication method

When choosing an authentication method, consider the following points:

Authenticating blockchain requests to a node

Chainstack offers two sets of credentials to access a node:

  1. Access via auth token
  2. Password-protected access

Access via auth token

You can use the endpoint with an auth token found in your Chainstack console:

Here’s how to retrieve the client version, one of the standard Ethereum JSON-RPC methods, using a POST request:

curl -X POST --location '<https://ethereum-mainnet.core.chainstack.com/YOUR_AUTH_TOKEN>' \\\\
--header 'Content-Type: application/json' \\\\
--data '{"jsonrpc":"2.0","method":"web3_clientVersion","params":[],"id":1}'

Password-protected access

For blockchain API requests, you can also use basic authentication:

You can find your username and password credentials in the Chainstack console.

To include the username and password in your cURL command, use the following format:

curl -X POST \\\\
  -u YOUR_USER_NAME:YOUR_PASSWORD \\\\
  -H "Content-Type: application/json" \\\\
  --data '{"jsonrpc":"2.0","method":"web3_clientVersion","params":[],"id":1}' \\\\
  <https://ethereum-mainnet.core.chainstack.com>

In this command, -u YOUR_USER_NAME:YOUR_PASSWORD includes your username and password for basic authentication. Replace YOUR_USER_NAME and YOUR_PASSWORD with your actual credentials.

Security reminder

Keeping your API key and username/password secure is critical to prevent unauthorized access to your blockchain node.

Further reading

Expand your Ethereum DApp development skills with these comprehensive Chainstack resources:

Bringing it all together

Developing a DApp is an exciting journey, one that can bring challenges and rewards in equal measure. As Web3 developers, it’s crucial to have an in-depth understanding of your tools, and Chainstack is committed to helping you every step of the way.

Over the course of this guide, we delved into the nuances of Layer 1 and Layer 2 network selection, the reliability of Chainstack Global Nodes, the importance of HTTP status code handling, and API authentication methods. But this is just the beginning.

The ever-evolving landscape of DApp development frequently presents new strategies and challenges. Keeping your knowledge bank updated, and making the most out of robust platforms like Chainstack can ensure your DApps remain efficient, secure, and powerful.

On behalf of Chainstack, we thank you for joining us on this enlightening journey into the essentials of DApp development. Together, let’s lead the charge in building a more decentralized world.

Power-boost your project on Chainstack

Have you already explored what you can achieve with Chainstack? Get started for free today.

Exit mobile version