We're planting a tree for every job application! Click here to learn more

Implementation of an Ethereum RESTful API

Nemanja Grubor

9 Nov 2021

•

7 min read

Implementation of an Ethereum RESTful API
  • Ethereum

Introduction

In this article, we will be going to implement an Ethereum RESTful API (Application Programming Interface). We will also be going to overview the technology stack for this task.

This article is for people who would like to learn how to implement a simple, Ethereum based API. Note that the project is fully Back-End, i.e. it doesn't have any UI except third-party tools that we will be going to overview.

What is a RESTful API?

A RESTful (or REST) API is an application programming interface that has constraints of REST architectural style. REST stands for Representational State Transfer.

API is a set of definitions for building an application.

REST is a set of architectural constraints.

Note: Later in this article, we will see how we can combine REST and MVC (Model-View-Controller), which are totally different concepts, but which can coexist.

Model-View-Controller Design Pattern

Model-View-Controller (MVC) is a design pattern that divides program logic into three elements:

  • Model. Model is the main component of the pattern. It is the dynamic data structure of an application, and it is independent of the user interface. It manages the data and the logic of the application.
  • View. View can be seen as the front-end for an application.
  • Controller. Controller accepts the input and converts it to commands for the model or view.

Task

Implement an Ethereum based RESTful API, with the following functionalities:

  • Ethereum address creation.
  • Storing the address in a database.
  • Deposit assets to the stored address.
  • Assets withdrawal from the stored address.

Technology Stack and Tools

  • ExpressJS
  • MongoDB
  • MongoDB Compass UI
  • Ganache
  • Infura
  • Postman

Prerequisites

Before using this API, it is needed to have installed:

  • NodeJS
  • MongoDB (version 3.2 is used)
  • MongoDB Compass UI (recommended UI for MongoDB)
  • Ganache
  • Postman

It is also needed to create an Infura account and a project. Project ID and mnemonic will be used in the .env file.

Note: You can copy-paste a mnemonic from Ganache, or generate it via the following command:

npx mnemonics

After installation of NodeJS, it is needed to install Node modules in the project folder. When everything is ready:

  • Start the MongoDB server.
  • Start MongoDB Compass UI
  • Enter the following connection string: mongodb://127.0.0.1/account
  • Start the API server using the following command: npm run build && node ./dist

Note: API server port is 4030.

Functionalities

  • Account Creation
  • Deposit
  • Withdrawal

Account Creation

Account (Ethereum address) can be created with the POST request in Postman API, as follows:

POST http://localhost:4030/account/createAccount

Ethereum address and a private key are displayed in the command line.

Ethereum address is stored in MongoDB.

Note: You should copy-paste the private key somewhere, for deposit/withdrawal usage. Private keys are shown with the 0x prefix, and you should ignore that prefix.

Deposit

It is possible to use one of the Ganache accounts that already have some assets as a sender.

While the API server is running, go inside the src/eth-controller.js file, and in the eth_deposit function, insert an address and a private key of your Ganache account. In the same function, for the receiver parameter, insert your newly created Ethereum address.

Deposit from Ganache account to a newly created address is possible via Postman API, as follows:

POST http://localhost:4030/account/deposit

Withdrawal

Here, one of the Ganache accounts is used as a receiver.

While the API server is running, go inside the src/eth-controller.js file, and in the eth_withdraw function, insert an address and a private key of your newly created account. In the same function, for the receiver parameter, insert one of the Ganache accounts.

Withdrawal from a newly created account to a Ganache account is possible via Postman API, as follows:

POST http://localhost:4030/account/withdraw

Technology Stack and Tools Overview

Before we begin with the implementation, we will overview a technology stack for our project.

NodeJS

node.jpg

NodeJS is a server-side JavaScript platform. It runs as a stand-alone application, rather than browser-only JS. Node.JS strives for non-blocking and asynchronous programming. The asynchronous mode can be dangerous. There is a term callback hell, which is a big issue caused by coding with complex nested callbacks.

It is a popular platform for building RESTful APIs, so it is somehow natural to use it in our project.

ExpressJS

express.png

Express is a web framework for NodeJS. It can be seen as a layer built on the top of the NodeJS, that helps manage a server and routes.

It has a Model-View-Controller (MVC) structure, which makes it suitable for usage.

MongoDB

mongodb.png

MongoDB is a document-oriented database. MongoDB and other NoSQL databases have applications in Blockchain, so we are going to use MongoDB for our project, rather than some Relational Database Management System (RDBMS).

MongoDB Compass

MongoDB Compass is the official GUI for MongoDB. It is an alternative to Mongo Shell. There are also other GUIs that can be used with MongoDB.

Ganache

ganache.png

Ganache has two components:

  • Ganache CLI (Command-Line Interface). Ganache CLI allows running a local ethereum blockchain locally. This blockchain is not connected to any public testnet, nor the mainnet.
  • Ganache GUI (Graphical-User Interface). Ganache GUI is a graphical interface for Ganache CLI. It can run as a stand-alone desktop app.

Infura

infura.jpg

Infura is an API that provides the tools which allow blockchain applications to be taken from testing to deployment, with access to Ethereum and IPFS.

These are a few examples of possible problems that can be solved by Infura:

  • Long initialization time. It can take a really long time to sync a node with the Ethereum blockchain.

  • Cost. It can get expensive to store the full Ethereum blockchain.

Infura solves these problems by requiring no syncing and no complex set-ups.

Note that there are also, other service providers like Infura.

Postman

postman.png

Postman is an API for building and using APIs. It has the ability to make various types of HTTP requests and save environments for later use

Here, we are going to test HTTP POST requests.

Directory Structure

Since we have a small number of files in this project, we will keep a simple directory structure. Note that you should make additional folders for MVC design pattern (Model, View, and Controller folders).

Here is the directory structure:

project
└───.env
└───package.json
└───dbconn.js
└───model.js
└───eth-controller.js
└───controller.js
└───route.js

From this directory structure, we can see how REST can be combined with MVC. File routes.js represents a REST module in our API.

Implementation

Now that we have seen an overview of this API and an overview of used technologies, we can start with the implementation.

We will start with defining a package.json file:

{
  "name": "eth-api",
  "version": "1.0.0",
  "description": "",
  "main": "dbconn.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "body-parser": "^1.18.3",
    "express": "^4.16.3",
    "mongodb": "^3.1.4",
    "mongoose": "^5.2.14",
    "web3": "^1.0.0-beta.36",
    "dotenv": "^8.2.0",
    "nodemon": "^2.0.7"
  }
}

Next, we will define a dbconn.js file, which is a server for this API:

const express = require('express');
const bodyParser = require('body-parser');
const mongoose = require('mongoose');
require('./model.js');
const account = require('./route.js');

const app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));

let port = 4030;

mongoose.Promise = global.Promise;
mongoose.connect('mongodb://127.0.0.1/account', { useNewUrlParser: true });

let db = mongoose.connection;
db.on('error', console.error.bind(console, 'MongoDB connection error:'));
let accountDB = db.collection("accounts");

app.use('/account', account);

app.listen(port, () => {
    console.log('Server is up and running on port number ' + port);
    console.log(
    accountDB != null ?
    accountDB.name + " database found" :
    accountDB.name + " database not found"
);
});

We will proceed with Model-View-Controller (MVC) design pattern. Note that, since we are building a Back-End API, there is no View (user-interface).

Model

Here is a model.js file:

const mongoose = require('mongoose');
const Schema = mongoose.Schema;

let AccountSchema = new Schema({
  ETH: {type: String, required: false, max: 64},
});

module.exports = mongoose.model('Account', AccountSchema);

Controller

We will divide the Controller module into two parts. The goal of this is to wrap the Web3 "magic" in one file and then call those functions in basic controller logic.

Here is an eth-controller.js file, which contains Web3 "magic":

const Web3 = require ('web3');
const Accounts = require('web3-eth-accounts');

const accounts = new Accounts('ws://127.0.0.1:4030');
const web3 = new Web3(new Web3.providers.HttpProvider(`https://rinkeby.infura.io/v3/${process.env.INFURA_PROJECT_ID}`));

exports.get_new_address = async function (req,res) {
    let ethData = {};
    try {
        ethData = await web3.eth.accounts.create();
                  console.table(ethData);
                  ethData.result = ethData.address;
                  return ethData.result;
        } catch(err) {
            ethData.result = err.message
            console.log ( chalk.red ( "REQUEST ERROR" ) );
            return ethData.result;
     }
     console.log(ethData.result);
     return ethData.result;
}

exports.eth_deposit = async function(req, res) {

	const web3_2= new Web3('http://127.0.0.1:7545');

	//Insert other address and private key of a local Ganache account 
	const address = '';
	const privateKey = '';
	
	//Insert other, newly created address
	const receiver = '';
	
	console.log('Sending a transaction ...');
	
	const createTransaction = await web3_2.eth.accounts.signTransaction({
		from: address,
		to: receiver,
		value: web3_2.utils.toWei('2', 'ether'),
		gas: 21000,
	},
	privateKey
	);

	
	const receipt = await web3_2.eth.sendSignedTransaction(createTransaction.rawTransaction);
	console.log('Transaction successful');
}

exports.eth_withdraw = async function(req, res) {
	const web3_3= new Web3('http://127.0.0.1:7545');

	//Insert other address and private key of a newly created account 
	const address = '';
	const privateKey = '';                    
	
	//Insert other address from a local Ganache account
	const receiver = '';

	console.log('Sending a transaction ...');
	
	const createTransaction = await web3_3.eth.accounts.signTransaction({
		from: address,
		to: receiver,
		value: web3.utils.toWei('1', 'ether'),
		gas: 21000,
	},
	privateKey
	);
	
	const receipt = await web3_3.eth.sendSignedTransaction(createTransaction.rawTransaction);
	console.log('Transaction successful');

}

Here is a controller.js file, which calls eth-controller.js modules:

const mongoose = require ('mongoose');
const Account = mongoose.model ('Account');
const ethereum_controller = require('./eth-controller.js');
const express = require('express');

exports.new_account = async function (req, res) {
  let ethData;

  let newAccount = new Account (
    {
      ETH: req.body.ETH,
    }
  );

  ethData = await ethereum_controller.get_new_address();
  newAccount.ETH = ethData;

  newAccount.save ( function ( err, dbResponse ) {
    if ( err ) {
      res.send( err );
    }
    console.log ( "***" + ( dbResponse ) + "***" );
    res.send ( dbResponse );
  });
}

exports.deposit = async function(req, res) {
	let ethData;
	
	ethData = await ethereum_controller.eth_deposit();
}

exports.withdraw = async function(req, res) {
	let ethData;
	
	ethData = await ethereum_controller.eth_withdraw();
}

Router

Lastly, we will define a router file, which makes HTTP POST requests possible (route.js):

const express = require('express');
const account_controller = require('./controller.js');

const router = express.Router();

router.post('/createAccount', account_controller.new_account);
router.post('/deposit', account_controller.deposit);
router.post('/withdraw', account_controller.withdraw);

module.exports = router;

Conclusion

In this article, we have:

  • Learned about RESTful API.
  • Learned about MVC design pattern.
  • Defined a task.
  • Defined technology stack and tools.
  • Seen the prerequisites for using our API.
  • Seen the functionalities of our API.
  • Overviewed the technology stack and tools.
  • Defined the directory structure for our project.
  • Implemented our API.

We have seen how to implement a basic Ethereum based RESTful API. There are some bad practices in this implementation, e.g. hard-coded strings. This is just a demonstration for local use, but note that you should avoid any type of bad practice.

Did you like this article?

Nemanja Grubor

See other articles by Nemanja

Related jobs

See all

Title

The company

  • Remote

Title

The company

  • Remote

Title

The company

  • Remote

Title

The company

  • Remote

Related articles

JavaScript Functional Style Made Simple

JavaScript Functional Style Made Simple

Daniel Boros

•

12 Sep 2021

JavaScript Functional Style Made Simple

JavaScript Functional Style Made Simple

Daniel Boros

•

12 Sep 2021

WorksHub

CareersCompaniesSitemapFunctional WorksBlockchain WorksJavaScript WorksAI WorksGolang WorksJava WorksPython WorksRemote Works
hello@works-hub.com

Ground Floor, Verse Building, 18 Brunswick Place, London, N1 6DZ

108 E 16th Street, New York, NY 10003

Subscribe to our newsletter

Join over 111,000 others and get access to exclusive content, job opportunities and more!

© 2024 WorksHub

Privacy PolicyDeveloped by WorksHub