• Time to read 5 minutes
NFT-Based Luxury Watch Certificate: Explaining the IPFS JSON Web Service

NFT-Based Luxury Watch Certificate: Explaining the IPFS JSON Web Service

A Decentralized App for Minting, Viewing and Transferring Ownership of NFT-based Certificates of Authenticity for Luxury Watches. 

This is the 3rd and final part of the series of articles to demonstrate how Non-Fungible Tokens (NFTs) may replace paper-based Certificate of Authenticity for luxury goods such as watches.

Refer to Part 1 to learn the business logic and Part 2 to walk through the DApp.

The BreitLex DApp saves the watches' metadata and pictures on InterPlanetary File System (IPFS). This is done by making API calls to Pinata via a JSON-based API service that I run on Glitch

Developers may access the source codes of this project on my Github Repository.

In this part, I will walk through steps to store the metadata and photo of each NFT to IPFS with Pinata.


Pinata is an IPFS pinning service that allows users to upload and share their contents to the IPFS network. We will use Pinata's API to save our NFT's JSON metadata and the watch's image to IPFS. There are various alternatives to Pinata including:

  1. Infura IPFS
  2. Eternum
  3. Or host your own!

For an in-depth walkthrough of IPFS and why it is "permanent" compared to storing the NFT's metadata and images on in say, Google Cloud, read this.

To begin, sign up for a free Pinata account.

At your profile, click API Keys. 

Create a new API key with admin access. Copy the API key and the API secret and save it somewhere. We will use this in the next step.

JSON Web Service

(This set of codes was developed with reference to Pinata's Service API documents)

This NodeJS-based JSON Web Service allows calls to be made to Pinata to pin new NFT metadata and pictures to IPFS. I host the Web Service on Glitch and make calls to it from the BreitLex DApp.

The source code for the JSON Web Service can be found here

const pinataSDK = require('@pinata/sdk');
const multer = require('multer');
const express = require('express');
const streamifier = require('streamifier');
const app = express();
const pinata = pinataSDK('your key', 'your secret');
const port = 3000;
const storage = multer.memoryStorage();
const upload = multer({ storage: storage });
const ipfsuri = "https://ipfs.io/ipfs/";
const cors=require("cors"); 

This section of the JSON Web Service imports the required libraries as follows:

  • Pinata: to make the API calls to pin contents to IPFS
  • Multer: to upload files
  • Express: NodeJS's web framework
  • Streamifier: converts a buffer into a readable stream
  • Cors: to restrict calls to this JSON web service to specific sources

Replace 'your key' and 'your secret' with the respective Pinata API key and API secret that you saved from the previous step at const pinata = pinataSDK('your key', 'your secret').

  credentials: true,
  origin: ["http://localhost:8080", "https://jacksonng.org"]

This CORS setting restricts calls to the JSON API from localhost and my website at jacksonng.org.

app.post('/nftwrite', upload.single('image'), function (req, res, next) {

A route method nftwrite is defined.

const mystream = streamifier.createReadStream(req.file.buffer);

//file more than 1MB? Drop and exit!
if (Buffer.byteLength(req.file.buffer)>= 1000000){
    res.status(500).send('Too big. Please keep to files below 1MB.');

It reads the file buffer of the NFT's image to check if it's more than 1 Megabyte in size. It exits with an error message if the image file size is too big.

mystream.path = req.file.originalname;
const options = {
    pinataMetadata: {
        name: req.file.originalname,
    pinataOptions: {
        cidVersion: 0

//pin the picture
pinata.pinFileToIPFS(mystream, options).then((result) => {
    //construct the metadata
    const body = {
        "model": req.body.model,
        "manufactured-date": req.body.manufactureddate,
        "serial-number": req.body.serialnumber,
        "photo": ipfsuri + result.IpfsHash
    const options = {
        pinataMetadata: {
            name: req.body.serialnumber,
        pinataOptions: {
            cidVersion: 0

If all is well, a call is made to pinata.pinFileToIPFS to pin the image on IPFS. It also constructs a JSON structure body that consists of:

  • req.body.model: The watch's model, extracted from the request (req) when this method is called.
  • req.body.manufactureddate: The watch's manufactured date, extracted from the request (req) when this method is called.
  • req.body.serialnumber: The watch's serial number, extracted from the request (req) when this method is called.
  • ipfsuri + result.IpfsHash: A URL to the watch's image, constructed by concatenating the IPFS's URL and the IpfsHash that Pinata returned when the image of the watch was successfully pinned to IPFS.
pinata.pinJSONToIPFS(body, options).then((result) => {
    //ok done, return the hash to caller
    res.json({ IpfsHash: result.IpfsHash });
}).catch((err) => {
    //handle error here
    res.status(500).send('Something broke!')

Finally, the JSON contents of body is pinned to IPFS by making a call to pinata.pinJSONToIPFS. It then returns the JSON content's IPFS Hash result.IpfsHash to the caller.

Use Postman to test the API call as follows:

Return to Pinata's file manager. If the upload is successful, the image and JSON Metadata that you have pinned to IPFS will appear here.

Decentralized App

BreitLex's Decentralized App makes a call to the JSON Web Service when the user presses the [Mint] button to mint an NFT for a newly manufactured watch. In doing so, he also provides the watch's Model, Manufacturered Date, Serial Number, and Picture. 

Refer to the source code of the Decentralized App on the project's Github Repository.

var data = new FormData();
var fileInput = document.getElementById("file-watchpic").files[0];
data.append("image", fileInput, fileInput.name.toString());
data.append("model", document.getElementById("txt-model").value);
data.append("manufactureddate", document.getElementById("txt-manufactured-date").value);
data.append("serialnumber", document.getElementById("txt-serial-number").value);

The picture of the watch is transferred to the fileInput variable. Together with the user's inputs for model, manufactureddate and serialnumber, these are saved into a data variable.

xhr.open("POST", "https://breitlexipfsapi.glitch.me/nftwrite");

data is then sent to the JSON Web Service's nftwrite method.

xhr.addEventListener("readystatechange", (event)=> {
     if (event.target.readyState === 4)  {

An event listener is declared to listen for 'readystatechange'. readystatechange is received from the JSON Web Service when the watch's picture as well as its JSON Metadata have been successfully pinned to IPFS and the IPFS hash is returned. The DApp is now ready to mint a new NFT token with the IPFS hash that it received. 

var myipfsURI = ipfsURI + JSON.parse(event.target.responseText).IpfsHash;
this.breitlexNFTContract.methods.mint(this.account, myipfsURI).send({from: this.account})
      .on('error', function(error, receipt) { 

It reads the IpfsHash that the JSON Web Service method nftwrite returns and uses it to execute the Smart Contract call mint to mint the NFT for the watch, and saving the IPFS hash of the watch's JSON Metadata as the NFT's URI.


While the hype is on NFT-based digital art, I believe that the killer app for NFT lies in the technology's ability to mint immutable digital twins of physical assets that survive even after its creator is long gone.

This makes NFT imminently suitable to replace paper certificates of luxury goods meant to be handed from one generation to another - like that Patek Philippe watch that you never really own, but merely look after for the next generation.

"You never really destroy an NFT, it just outlives you."

This completes the NFT-Based Luxury Watch Certificate series:

  1. NFT-Based Luxury Watch Certificate: How It Works
  2. NFT-Based Luxury Watch Certificate: Decentralized App Demo
  3. NFT-Based Luxury Watch Certificate: Explaining the IPFS JSON Web Service (this part)

If you enjoyed this tutorial, perhaps you may also wish to read:

Photo by David Bruno Silva on Unsplash