Implement file upload with Functions
Build a Hono endpoint that checks an uploaded file and writes it to an Object Storage bucket from a function.
In this tutorial, you will build an upload endpoint that stores each file it receives in an Object Storage bucket. You will create a Hono project, write the upload handler, create a bucket with write access, deploy the project, and post a file to it.
The complete source of this example is in the file-upload package of the functions examples repository.
Prerequisites
- An Azion account. To create one, refer to How to create an account on Azion.
- Azion CLI installed. Refer to Azion CLI.
- Object Storage enabled on your account. Refer to Object Storage.
- Node.js version 18 or higher.
- A terminal and a code editor.
1. Create the project
Azion CLI creates a project from a framework preset. For the full Hono walkthrough, refer to How to build with Hono.
To create the project:
Run the login command:
With no credential flags, the CLI opens a browser-based flow. It stores the credentials locally, and every later command is authorized against your account.
Run Azion CLI in the directory that holds your projects:
Enter a name, or press enter to accept the suggestion:
Azion CLI lists one preset per framework:
The remaining stages deploy the project instead of running it locally:
Azion CLI names the folder after the project:
The folder holds the source code of the Hono application that the template supplies.
2. Write the upload handler
The handler answers a POST request on /upload. It parses the multipart form, checks the file, and writes it to a bucket. The write goes through the createObject method of the Azion Storage library.
The entry field of azion.config.js names the entry file of the project. Open that file and replace its contents with this code:
export default app is the ES Modules handler pattern, which Azion recommends over the Service Worker pattern. Four decisions sit in the code:
- A
filefield that holds no file returns400with the body{"message":"Invalid file"}. - A file above the
maxSizeconstant of 2 MB returns413with the body{"message":"File size exceeds 2MB limit"}. createObjectwrites the file to the bucket named by theBUCKET_NAMEenvironment variable. The object key is the name of the file.- A failure inside
createObjectreturns500with the body{"message":"Error uploading file"}, and the exception reaches the function logs.
3. Configure storage permissions
The access level of a bucket decides what Azion Runtime does with it. A function writes objects only to a bucket whose access is read_write. A bucket set to read_only answers reads and rejects writes, and Azion Runtime reaches no content in a bucket set to restricted.
A bucket name is unique across all Azion accounts. It takes 6 to 63 characters, accepts letters, numbers, and the hyphen (-), and never starts with azion.
The handler reads the bucket name from the BUCKET_NAME environment variable. To create the bucket and store its name:
The bucket exists on your account, and Azion Runtime writes objects to it.
Enter the same name you gave the bucket:
The variable is stored on the account, and the function reads it with Azion.env.get('BUCKET_NAME'). A variable whose key contains password, pwd, secret, key, hash, encrypted, passcode, auth, or token is sent as a secret by default. The key BUCKET_NAME carries none of those substrings. The --secret flag defaults to true, so --secret false stores the bucket name as a plain value. A new value reaches a function only after the function is deployed again.
4. Deploy the project
To send the project to Azion:
Print the authenticated account:
The terminal prints the email address of the account the commands run against.
In the project folder, run:
The deploy uploads the function code and configures the application. It also creates the routing rules, applies the storage permissions, and returns a domain. The domain has the format https://xxxxxxx.map.azionedge.net. Propagation takes a few minutes, so wait before you request the endpoint.
5. Verify the upload
To store a file through the endpoint:
Write one line into a local file:
The file upload-test.txt exists in the current directory.
Send the file in a multipart form, under the file field:
The response body carries the stored object:
Read the keys the bucket holds:
The key upload-test.txt appears in the list.
The endpoint now stores every file it accepts. A request whose file field holds no file answers 400, and a file above 2 MB answers 413. When a request answers 500, read the function logs for the exception behind it.