Self-hosted Moralis server: local devchain & cloud functions

Hi there,

Following the recent changes requiring Moralis users to self host their server, I am currently migrating a project to a self hosted server with Redis & MongoDB as per the Moralis Server Walkthrough tutorial available at this link https://www.youtube.com/watch?v=l2qTyc-V9cM
I am now facing the below challenges:

  1. How to sync with my local dev chain (hardhat)?
    The previous way of doing it was with the following terminal command:

moralis-admin-cli connect-local-devchain --chain hardhat --moralisSubdomain xxxxxxxxxxxx.grandmoralis.com --frpcPath ./frp/frpc
Could you please advise how to achieve this now that I am self hosting my Moralis server with a mongoDB ?

  1. How to sync my Moralis cloud functions with the new self hosted server?
    The previous way of doing it was with the following terminal command:

moralis-admin-cli watch-cloud-folder XXXXXXXXXXXXXXX --moralisSubdomain xxxxxxxxxxxx.grandmoralis.com --autoSave 1 --moralisCloudfolder ./cloudFunctions

  1. Do we still use Moralis.Cloud.afterSave, Moralis.Object.extend, Moralis.Query… for the cloud functions and query when using a self hosted server?
    If not could you please advise the new approach.

A complete tutorial on this topic would be greatly appreciated.
Any guidance on these questions would be greatly needed to progress our projects.

Thank you.

you can not use a self hosted server with a local devchain

you can edit directly the cloud file where the cloud code is found for your local parse server, it should be a file path that is used directly in configs

you can use Parse.Cloud instead of Moralis.Cloud in cloud code syntax

Thank you for the quick answer.

  • Regarding adding a MongoDB column, do I still use Moralis.Object.extend(“New column”) ? Or what would the way to do it with a self hosted server?
  • When can we expect a functionality enabling to use a self hosted server with a local devchain ?

You can use Parse.Object instead of Moralis.Object

we don’t know if we will add that functionality for using local devchain with a self hosted server

Thanks, in which file of this repo should I copy the adjusted cloud functions code into?

it looks like this is the file with the cloud code

https://github.com/MoralisWeb3/Moralis-JS-SDK/blob/main/demos/parse-server-migration/src/cloud/main.ts

Thank you.
Some more questions related to self hosting:

Following the Patrick Collins FCC course on NFT market place, we have the addEvents.js file (see code below) in the NextJS front end to listen to events using Moralis:

const Moralis = require("moralis-v1/node")
require("dotenv").config()
const contractAddresses = require("./constants/networkMapping.json")
let chainId = process.env.chainId || 31337
let moralisChainId = chainId == "31337" ? "1337" : chainId
const contractAddress = contractAddresses[chainId]["NftMarketplace"][0]

const serverUrl = process.env.NEXT_PUBLIC_SERVER_URL
const appId = process.env.NEXT_PUBLIC_APP_ID
const masterKey = process.env.masterKey

async function main() {
    await Moralis.start({ serverUrl, appId, masterKey })
    console.log(`Working with contract address ${contractAddress}`)

    let ItemListedOptions = {
        // Mortalis understands a local chain is 1337
        chainId: moralisChainId,
        address: contractAddress,
        sync_historical: true,
        topic: "ItemListed(address, address, uint256, uint256)",
        abi: {
            anonymous: false,
            inputs: [
                {
                    indexed: true,
                    internalType: "address",
                    name: "seller",
                    type: "address",
                },
                {
                    indexed: true,
                    internalType: "address",
                    name: "nftAddress",
                    type: "address",
                },
                {
                    indexed: true,
                    internalType: "uint256",
                    name: "tokenId",
                    type: "uint256",
                },
                {
                    indexed: false,
                    internalType: "uint256",
                    name: "price",
                    type: "uint256",
                },
            ],
            name: "ItemListed",
            type: "event",
        },
        tableName: "ItemListed",
    }

    let ItemBoughtOptions = {
        chainId: moralisChainId,
        address: contractAddress,
        topic: "ItemBought(address, address, uint256, uint256)",
        sync_historical: true,
        abi: {
            anonymous: false,
            inputs: [
                {
                    indexed: true,
                    internalType: "address",
                    name: "buyer",
                    type: "address",
                },
                {
                    indexed: true,
                    internalType: "address",
                    name: "nftAddress",
                    type: "address",
                },
                {
                    indexed: true,
                    internalType: "uint256",
                    name: "tokenId",
                    type: "uint256",
                },
                {
                    indexed: false,
                    internalType: "uint256",
                    name: "price",
                    type: "uint256",
                },
            ],
            name: "ItemBought",
            type: "event",
        },
        tableName: "ItemBought",
    }

    let ItemCanceledOptions = {
        chainId: moralisChainId,
        address: contractAddress,
        topic: "ItemCanceled(address, address, uint256)",
        sync_historical: true,
        abi: {
            anonymous: false,
            inputs: [
                {
                    indexed: true,
                    internalType: "address",
                    name: "seller",
                    type: "address",
                },
                {
                    indexed: true,
                    internalType: "address",
                    name: "nftAddress",
                    type: "address",
                },
                {
                    indexed: true,
                    internalType: "uint256",
                    name: "tokenId",
                    type: "uint256",
                },
            ],
            name: "ItemCanceled",
            type: "event",
        },
        tableName: "ItemCanceled",
    }

    const listedResponse = await Moralis.Cloud.run("watchContractEvent", ItemListedOptions, {
        useMasterKey: true,
    })
    const boughtResponse = await Moralis.Cloud.run("watchContractEvent", ItemBoughtOptions, {
        useMasterKey: true,
    })

    const canceledResponse = await Moralis.Cloud.run("watchContractEvent", ItemCanceledOptions, {
        useMasterKey: true,
    })
    if (listedResponse.success && canceledResponse.success && boughtResponse.success) {
        console.log("Success! Database Updated with watching event")
    } else {
        console.log("Something went wrong...")
    }
}

main()
    .then(() => process.exit(0))
    .catch((error) => {
        console.error(error)
        process.exit(1)
    })

Now that we are self hosting, are we still allowed to use these functions in the front end:

  1. Moralis.start({ serverUrl, appId, masterKey })
  2. Moralis.Cloud.run("watchContractEvent, … , {

You will have to use streams api for events, there is a plugin for that for parse server in our documentation.

And it will not work with a devchain, you will have to use a testnet

1 Like

Hello. I’m trying the same course using self-hosted moralis server. To walk around the call of the “watchContractEvent” I created a function so i could just continue the course without having to find other solutions while learning using local devchain.

Parse.Cloud.define('watchContractEvent', async ({ params, user, ip }: any) => {
  let provider: any;
  if (params['chainId'] == 1337) {
    provider = new ethers.providers.WebSocketProvider('http://127.0.0.1:8545/');
  } else {
    provider = new ethers.providers.WebSocketProvider(process.env.GOERLI_RPC_WEBS!);
  }

  const contractAddress = params['address'];

  const contract = new ethers.Contract(contractAddress, [params['abi']], provider);
  if (params['tableName'] == 'ItemBought') {
    contract.on('ItemBought', (buyer, nftAddress, tokenId, price, event) => {
      const ItemBought = Parse.Object.extend('ItemBought');
      const itemBought = new ItemBought();
      itemBought.set('buyer', buyer);
      itemBought.set('nftAddress', nftAddress);
      itemBought.set('tokenId', tokenId);
      itemBought.set('price', ethers.utils.formatUnits(price, 6));
      itemBought.save();
    });
    return { success: true };
  }
  if (params['tableName'] == 'ItemListed') {
    contract.on('ItemListed', (seller, nftAddress, tokenId, price, event) => {
      const ItemListed = Parse.Object.extend('ItemListed');
      const itemListed = new ItemListed();
      itemListed.set('seller', seller);
      itemListed.set('nftAddress', nftAddress);
      itemListed.set('tokenId', tokenId);
      itemListed.set('price', ethers.utils.formatUnits(price, 6));
      itemListed.save();
    });
    return { success: true };
  }
  if (params['tableName'] == 'ItemCanceled') {
    contract.on('ItemCanceled', (seller, nftAddress, tokenId, event) => {
      const ItemCanceled = Parse.Object.extend('ItemCanceled');
      const itemCanceled = new ItemCanceled();
      itemCanceled.set('seller', seller);
      itemCanceled.set('nftAddress', nftAddress);
      itemCanceled.set('tokenId', tokenId);
      itemCanceled.save();
    });
    return { success: true };
  }
  return { success: false };
});

it’s not a good solution but is working and i’m listening to the events on my local devchain and committing it to local mongodb.

3 Likes

Many thanks @Samuelbcst!
Where did you put that function? Should it goes into the ./cloud/main.ts file of your parse server migration?

yes, it looks like you have to add it in that file specific to cloud code and you have to call that function after that

1 Like

@Samuelbcst if you could share your updated repos (parse server + nextJS front end) in relation to the Patrick Collins FCC course (NFT marketplace chapter using Moralis v2) that would be greatly appreciated.

1 Like

@Tristan3000 have you figure it out away to set it up.Im following the same course and stuck at this part for a few day and dont know how to set the server. If possible could you share me your’s code for this part. I would be really appreciate