If you’re a developer then there’s a strong chance you do a fair bit of work in the terminal. We use it for things like making new directories or files, cloning Git repositories, or setting up a new app with a command like <span class="code-inline">npx create-pinata-app</span> (try that one by the way). To help streamline work in the terminal many people also utilize Command Line Interfaces or CLI’s. They work like programs or shortcuts to multiple different script files or binaries to accomplish an array of different tasks. Pinata has had a Node.js CLI for several years that allowed people to upload files to IPFS. It’s been well loved, however not without its problems.
When embarking on a project like this, the best thing you can do is start small. Focus on the smallest step and then slowly grow into more complex goals. Since we already had an existing CLI, we knew what functionality it would have to have. First, we needed to be able to authenticate a user with the Pinata JWT. To start we just wrote a simple Go program to make sure we could run a function. Then, we made a quick little function to test authentication with an API request.
Now that we had this and could run it with the JWT variable, we needed to adjust it to accept a JWT, write it to the user’s dot files, then read it from that file and make sure it worked the same way.
With the JWT saved to the user’s computer, we can now just read it for all of our other CLI functions rather than making them provide it every single time.
Now that we have authorization taken care of it was time to try something a bit harder: uploading a file. While it can sound a little daunting, all it takes is a bit of research and perhaps AI to figure out the best way to send a file via an API request. Then we just have to conform it to what the Pinata API expects. Since we wanted this upload function to accept files or folders without distinction, we made a helper function to check what the input was and append it accordingly.
We also made a separate function to create the multipart request you normally need for file uploads. In it, we take in multiple arguments for the files, the version of the CID, and the name you would like for the file to be called once uploaded to Pinata.
Finally, we can put it all together for an Upload function that handles errors, formats the response, and prints results to the user.
Now that we have our main functions, the next goal is to build the CLI. Thankfully there are lots of great frameworks and tools to make the process easier, and personally, we fell in love with <span class="code-inline">urfave/cli</span>. It makes everything pretty straightforward when it comes to building commands. Here’s what our CLI would look like with just authentications and uploads.
The urfave framework allows you to set up different commands with names, help tips, and more. You can also include flags for certain logic, so for instance, on upload, you can run <span class="code-inline">pinata upload --version 0</span> and it would return a V0 CID <span class="code-inline">QmVLwvmGehsrNEvhcCnnsw5RQNseohgEkFNN1848zNzdng</span> vs the default V1 CID <span class="code-inline">bafkreih5aznjvttude6c3wbvqeebb6rlx5wkbzyppv7garjiubll2ceym4</span>. With this, we now have our MVP!
Now that we have a general pattern and structure for our CLI, adding other commands is easy! For instance, to add a delete command to <span class="code-inline">delete</span> a file, we just add a new file called <span class="code-inline">Delete.go</span> and add in our code to make a request with the CID as the argument.
Then we just add that command to our main list of commands for the CLI.
With this, we can run <span class="code-inline">pinata delete QmVLwvmGehsrNEvhcCnnsw5RQNseohgEkFNN1848zNzdng</span> and it will unpin the file from our account. But what if you don’t have the CID? Well, let’s add in a file list too!
This one is interesting as the <span class="code-inline">/data/pinList</span> endpoint has a lot of acceptable queries, so depending on what the user enters we’ll add in several here and add the command to our CLI like the others.
With this command, I can easily fetch my 10 latest files with <span class="code-inline">pinata list</span>! We purposely make the output of the command to be formatted JSON so developers can pipe it into files or other services, as well as plenty of flags to search and filter their results.
After we had built out this CLI and tested it we noticed quite a boost in performance compared to its Node.js predecessor, and it gave us the opportunity to explore what else we could make it do. The result is a CLI that mimics most of the Pinata API and gives you the tools you need to extend your IPFS workflow. It’s been so fun and easy to use that I find myself using it in my own workflows all the time for projects like Cosmic Cowboys and beyond. Don’t take my word for it; try it out now! Just visit this link and follow the download instructions and usage guide to get started.
This project is just the beginning as we plan to port the Pinata API into a new Typescript SDK, as well as other programming languages. Our community has done a great job making their own implementations, but we’re excited to focus on developers and continue supporting them no matter what language they’re working in.