Back to blog

How to Upload Files with Bun and Pinata

How to Upload Files with Bun and Pinata

Steve

If you’ve been in the developer ecosystem for the last few years, then you’ve probably heard of Bun.sh. It took the dev world by storm last year when it reached its V1 release, becoming an all-in-one tool for Javascript or Typescript. In a lot of ways, it works as a drop-in replacement for Node.js, giving users the ability to install packages, run Javascript or Typescript files, bundle them, and even do tests natively. Not only that, Bun is blazing fast since it’s written in Zig, putting Javascript back on the table for handling high volumes of requests and processes. While it’s not perfect, this is one awesome developer tool that you should try out.

Since it can do so many things, we’ll show you not just one, but TWO ways you can upload files with Pinata using Bun! To start, we’ll show you how easy it is to upload a local file from your computer to Pinata, and then we’ll show you how to setup a server that can accept files and upload them to Pinata as well. Let’s get started!

Setup

Before we kick off and start writing some code, you’ll need to do just a few things first.

Pinata

Naturally, you’ll need a Pinata account, which you can sign up for free here. Once you have an account, we’ll need to grab the following:

  • PINATA_JWT - You can get this by navigating to the API Keys tab on the left sidebar, then clicking “New Key” in the top right. Go ahead and give the key admin permissions and a name, then save the key info somewhere safe. We’ll primarily be using the longer JWT provided.
  • GATEWAY_URL - When you sign up for an account with Pinata, you’ll automatically receive a gateway, which is like your own personal CDN to retrieve files through. Click on the “Gateways” tab on the left side bar, and you should see your gateway domain in the format example.mypinata.cloud. Copy that domain and store it with your key as we’ll use it shortly.

Project

After you have your Pinata info ready, we’ll need to make a new project with Bun. Make sure you have Bun installed on your machine by visiting their website and following the instructions here. Once installed, you’ll want to open your terminal and run the following commands:

mkdir pinata-bun
cd pinata-bun
bun init
bun add pinata

This will make a new folder called pinata-bun. Move into that folder, initialize it with Bun (just select all the default options), and then install the Pinata SDK as a dependency. Now you can open this folder in your text editor of choice and create a new file in the root of the project called .env with the contents below; make sure you put your own credentials here!

PINATA_JWT= # The API key we made earlier
GATEWAY_URL= # Your gateway domain in the form example.mypinata.cloud

Next, make a new file called pinata.ts in the root of the project, as well and put in the following code:

import { PinataSDK } from "pinata";

export const pinata = new PinataSDK({
	pinataJwt: process.env.PINATA_JWT,
	pinataGateway: process.env.GATEWAY_URL,
});

This will create an instance of the Pinata SDK and export it so we can use it throughout the project.

Last, but certainly not least, we’ll need an image to upload! You can use any image you want in this tutorial, just make sure to drop the file into the root of the project and use the file name accordingly. However, if you want a special file for this project to follow it exactly as we have it here, click this link to get an image of Pinnie! :)

Local Files

The first upload method we’ll do is uploading a local file, which in this case is the pinnie.png file we just downloaded. Make a new file in the project called local.ts and paste in this code:

import { pinata } from "./pinata";

(async () => {
	const file = Bun.file("pinnie.png", { type: "image/png" }) as unknown as File;
	const upload = await pinata.upload.file(file);
	console.log(upload);
})();

In this file, we import the SDK instance we just made a little while ago, then create an immediately invoked function where everything happens. To start, we make a new file instance using Bun.file to read our local pinning.png , which is a huge convenience boost compared to using Node. Once you have the file, you can just pass it into the upload.file method and you’re done! To test this out, run the command below in your terminal.

bun local.ts

If successful, you should see a response like this:

{
  id: "c2e47d43-c4b1-4a79-8a37-0593dc64add6",
  name: "pinnie.png",
  cid: "bafkreih5aznjvttude6c3wbvqeebb6rlx5wkbzyppv7garjiubll2ceym4",
  created_at: "2024-09-09T03:37:44.133Z",
  size: 32928,
  number_of_files: 1,
  mime_type: "image/png",
  user_id: "126be64f-28ea-4b91-8db3-4e2ba8dd4854",
}

It’s seriously that simple to upload a local file with Bun!

Server

With this next example, we’ll build a server using Bun that accepts incoming requests and handles file uploads. This can come in handy if you want to authenticate someone in your own server before uploading a file to Pinata, rather than doing it directly from the client. Make a new file called server.ts in your project, and put this code inside of it:

import { pinata } from "./pinata";

const server = Bun.serve({
	async fetch(req) {
		// handle your auth here

		const path = new URL(req.url).pathname;

		// respond with text/html
		if (path === "/") return new Response("Welcome to Bun!");

		// receive POST data from a form
		if (req.method === "POST" && path === "/upload") {
			const data = await req.formData();
			const file = data.get("file") as File;
			const upload = await pinata.upload.file(file);
			return Response.json(upload);
		}

		return new Response("Path not found", { status: 404 });
	},
});

console.log(`Listening on ${server.url}`);

One of Bun’s built-in tools is Bun.serve, which lets us setup a simple server to handle requests and send data back. Inside the serve method, we have an async fetch where the request is passed in as a query parameter. Generally, this is where you would want to setup your auth, and you can see how we did that in this previous blog post. Once we have that, we can parse it to get our path and make routes based on it. Outside of our root “Welcome to Bun!” we have /upload, which only works if you are making a POST request. With it, we can parse incoming formData and get the attached file. Then, we can simply upload the file to Pinata and return the response as JSON!

To test this out, we need to first run the server with this command:

bun --hot server.ts

This will not only start the server but reload it if we make changes to the file. Once running, and printing that it’s listening on port 3000, open a new terminal window and run this command in the project directory:

curl -X POST "<http://localhost:3000/upload>" \\
			-F [email protected]

If successful, you should have made a POST request to the /upload path with the pinnie.png file attached and you should get a response back. Pretty slick!

Wrapping Up

As you can probably tell, we really like to use Bun when we can. Its especially helpful for running single Typescript files like we just did without having to do any extra steps or commands. You could say “it just works,” and that’s how we feel about Pinata too. You shouldn’t have to spend a bunch of time configuring and wresting with S3 and other complicated upload solutions when you can upload a file in minutes using Pinata. Don’t take my word for it, give it a shot today and experience speed, simplicity, and security like never before.

Subscribe to paid plan image

Share this post:

Stay up to date

Join our newsletter for the latest stories & product updates from the Pinata community.