File Handlers (IPFS)
File data sources allow you to fetch and index content from IPFS. A typical use case is an NFT contract that stores metadata CIDs on-chain — you index the URI event to get the CID, then the file handler parses the JSON metadata.
Setup
Declare a file template in graphite.toml:
[[contracts]]
name = "ERC721"
abi = "abis/ERC721.json"
address = "0x..."
start_block = 1000000
[[contracts.event_handlers]]
event = "URI(string,uint256)"
handler = "handle_uri"
[[templates]]
name = "NFTMetadata"
kind = "file/ipfs"
handler = "handle_nft_metadata"
Creating a File Data Source
In your event handler, trigger the file fetch by calling data_source::create_file:
#![allow(unused)] fn main() { #[handler] pub fn handle_uri(event: &ERC721URIEvent, ctx: &graphite::EventContext) { // Store a pending NFT record let token_id = hex(&event.token_id); NFT::new(&token_id) .set_token_id(event.token_id.clone()) .set_content_uri(event.uri.clone()) .save(); // Trigger the IPFS fetch data_source::create_file("NFTMetadata", &event.uri); } }
Writing the File Handler
#![allow(unused)] fn main() { use graphite::json; #[handler(file)] pub fn handle_nft_metadata(content: &[u8], ctx: &graphite::FileContext) { // ctx.id — data source ID // ctx.address — address of the contract that created this data source let value = match json::from_bytes(content) { Some(v) => v, None => return, }; let name = json::get_string(&value, "name").unwrap_or_default(); let description = json::get_string(&value, "description").unwrap_or_default(); let image = json::get_string(&value, "image").unwrap_or_default(); NFTMetadata::new(&ctx.id) .set_name(name) .set_description(description) .set_image(image) .save(); } }
JSON Parsing
#![allow(unused)] fn main() { use graphite::json; let value = json::from_bytes(content)?; // Get typed fields let name: Option<String> = json::get_string(&value, "name"); let count: Option<i64> = json::get_i64(&value, "count"); let flag: Option<bool> = json::get_bool(&value, "active"); let nested: Option<&JsonValue> = json::get_object(&value, "attributes"); }
Direct IPFS Access
You can also call ipfs.cat directly inside a regular event handler:
#![allow(unused)] fn main() { use graphite::ipfs; #[handler] pub fn handle_uri(event: &ERC721URIEvent, ctx: &graphite::EventContext) { if let Some(content) = ipfs::cat(&event.uri) { // parse content immediately } } }
Note: ipfs.cat is synchronous and blocks the handler. For large amounts of IPFS content, the file data source template approach is preferred as it allows parallel fetching.
ENS Resolution
#![allow(unused)] fn main() { use graphite::ens; let name: Option<String> = ens::name_by_address(&address_bytes); }
See the file-ds example for a complete working subgraph.