Notice: 2025-06-14
Development progress is summarized here.
https://zenn.dev/nakamura196/books/41693d2d017082
Overview
Starting with the following article, I have been prototyping a digital cultural heritage management system using blockchain.
This time, I modified the system so that uploaded data is recognized as NFTs.

This is a learning process, so there may be incomplete aspects, but I hope it serves as a helpful reference.
Usage Page
The file upload method remains the same as before. A link to the detail page was added to the list page displayed after upload.

Clicking the link navigates to the following detail screen.

Implementation
This section was written by AI.
1. Making the Contract NFT-Compatible
The existing digital cultural heritage management contract was modified to comply with the ERC721 standard as an NFT contract.
Main Changes:
1. Adding OpenZeppelin Libraries
import "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721URIStorageUpgradeable.sol";
2. Changing the Contract Inheritance Structure
contract DigitalHeritage is
Initializable,
OwnableUpgradeable,
UUPSUpgradeable,
ERC721Upgradeable,
ERC721URIStorageUpgradeable
{
// ...
}
3. Updating the Initialization Function
function initialize() public initializer {
__Ownable_init(msg.sender);
__UUPSUpgradeable_init();
__ERC721_init("Digital Heritage", "DH");
__ERC721URIStorage_init();
}
4. NFT Minting on Cultural Heritage Registration
function registerHeritage(
string memory _name,
string memory _description,
string memory _imageUrl,
string memory _tokenURI
) public {
uint256 id = heritages.length;
// Save cultural heritage data
heritages.push(Heritage({
id: id,
name: _name,
description: _description,
imageUrl: _imageUrl,
owner: msg.sender,
timestamp: block.timestamp,
isDeleted: false
}));
// Mint as NFT
_safeMint(msg.sender, id);
_setTokenURI(id, _tokenURI);
emit HeritageRegistered(id, msg.sender, _name);
}
2. Implementing the Metadata Management System
To support the standard NFT metadata format, server-side metadata generation and upload functionality was implemented.
1. Metadata Generation in API Route
// src/app/api/upload/route.js
const uploadMetadataToPinata = async (name, description, imageUrl) => {
const metadata = {
name: name,
description: description,
image: imageUrl,
attributes: [
{
trait_type: "Registration Date",
value: new Date().toISOString()
},
{
trait_type: "Category",
value: "Digital Cultural Heritage"
}
]
};
// Convert JSON to Blob and upload to Pinata
const jsonBlob = new Blob([JSON.stringify(metadata, null, 2)], {
type: 'application/json'
});
// Upload to IPFS using Pinata API
// ...
};
2. Frontend Integration
// Flow: Image upload -> Metadata generation -> Contract registration
const handleSubmit = async (e) => {
// 1. Upload image to IPFS
const imageUrl = formData.imageUrl;
// 2. Generate and upload metadata on the server side
const metadataFormData = new FormData();
metadataFormData.append('type', 'metadata');
metadataFormData.append('name', formData.name);
metadataFormData.append('description', formData.description);
metadataFormData.append('imageUrl', imageUrl);
const metadataResponse = await fetch('/api/upload', {
method: 'POST',
body: metadataFormData
});
const metadataResult = await metadataResponse.json();
// 3. Register with the contract (including token URI)
await registerHeritage(
formData.name,
formData.description,
imageUrl,
metadataResult.url
);
};
Summary
I hope this serves as a helpful reference in the process of learning Web3 and NFTs.