
Upload & Categorize Files with Supabase Storage and Secure URL Generation
Description
Categories
🔧 Engineering🤖 AI & Machine Learning
Nodes Used
n8n-nodes-base.setn8n-nodes-base.setn8n-nodes-base.setn8n-nodes-base.setn8n-nodes-base.setn8n-nodes-base.stickyNoten8n-nodes-base.stickyNoten8n-nodes-base.stickyNoten8n-nodes-base.stickyNoten8n-nodes-base.stickyNote
PriceFree
Views0
Last Updated11/28/2025
workflow.json
{
"meta": {
"instanceId": "1eec3d74182f3fda9f29f20c85422320a70882840e1b07acd098d5b4a836392a",
"templateCredsSetupCompleted": true
},
"nodes": [
{
"id": "ece7684b-64fa-4c80-9697-c6e7a4999eeb",
"name": "When Executed by Another Workflow",
"type": "n8n-nodes-base.executeWorkflowTrigger",
"position": [
-1420,
620
],
"parameters": {
"workflowInputs": {
"values": [
{
"name": "mime_type"
},
{
"name": "original_filename"
},
{
"name": "binary_data_base64"
}
]
}
},
"typeVersion": 1.1
},
{
"id": "6d600da3-a9e7-4015-bc73-a0fb5e400724",
"name": "Prepare Upload Data",
"type": "n8n-nodes-base.set",
"position": [
-940,
440
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "bucket-mapping",
"name": "bucket_name",
"type": "string",
"value": "={{ \n (() => {\n const mimeType = $json.mime_type || 'application/octet-stream';\n if (mimeType.startsWith('image/')) return 'image-files';\n if (mimeType.startsWith('audio/')) return 'audio-files';\n if (mimeType.startsWith('video/')) return 'video-files';\n return 'document-files';\n })()\n}}"
}
]
},
"includeOtherFields": true
},
"typeVersion": 3.4
},
{
"id": "5b5b91e9-b082-4d57-a191-c0fd71c84eae",
"name": "Success Response",
"type": "n8n-nodes-base.set",
"position": [
200,
440
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "action-type",
"name": "action",
"type": "string",
"value": "s3_upload"
},
{
"id": "success-status",
"name": "status",
"type": "string",
"value": "success"
}
]
},
"includeOtherFields": true
},
"typeVersion": 3.4
},
{
"id": "aa6355d3-5c2e-4b55-9f04-fcd1f5246428",
"name": "Convert to File",
"type": "n8n-nodes-base.convertToFile",
"notes": "Instead of the code note i would prefer to use the n8n dedicated note for this. only thing i am not sure of now is if i map the fields correct. ",
"position": [
-720,
440
],
"parameters": {
"options": {
"fileName": "={{ $json.original_filename }}",
"mimeType": "={{ $json.mime_type }}"
},
"operation": "toBinary",
"sourceProperty": "binary_data_base64"
},
"typeVersion": 1.1
},
{
"id": "2c1f3cf0-3726-4dd9-9121-e060e684d633",
"name": "temp form to test workflow",
"type": "n8n-nodes-base.formTrigger",
"position": [
-1420,
300
],
"webhookId": "1ccfd79e-c611-46e5-90c9-4f84d54ee1af",
"parameters": {
"options": {
"path": "action-workflows-testform",
"appendAttribution": false
},
"formTitle": "test workflow form",
"formFields": {
"values": [
{
"fieldLabel": "original_filename",
"requiredField": true
},
{
"fieldType": "textarea",
"fieldLabel": "binary_data_base64",
"requiredField": true
},
{
"fieldLabel": "mime_type"
}
]
},
"formDescription": "use this form to test action workflows without having to use another workflow"
},
"typeVersion": 2.2
},
{
"id": "64932e5f-b7fc-46ad-9946-41703fec5823",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1800,
-100
],
"parameters": {
"color": 4,
"width": 1100,
"height": 260,
"content": "## 📦 Upload files to Supabase Storage\n\n- Link to [Supabase docs](https://supabase.com/docs/guides/storage)\n\nThis workflow shows how you could upload files to a Supabase Storage Instance. It's primarily designed to be called by other workflows, as there wasn't a default node for this yet, and this could be usefull in quite a lot of other workflows. \n\nIn this particular example we use mime-type to sort files to specific buckets, but this is of course dependent on your specific storage structure. \n\nThe output is a signed url for the object, to avoid having to share secrets for entire buckets to applications that need the object as input"
},
"typeVersion": 1
},
{
"id": "96202c5f-d481-4b20-a526-7ba0c79396a1",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1800,
180
],
"parameters": {
"width": 340,
"height": 340,
"content": "## 🧪 Quick Test Form during building\n\n\nThis form allowed me to quickly check this flow without having to execute another workflow.\n\nOf course you could also pin data, but given the fact i wanted to check different filetypes i opted for this temporary form, you can populate the base64 field with base64 encoded string of testfile (using for example `base64 -i /path/to/file | pbcopy`)\n\n⚠️ Remove before putting flow live."
},
"typeVersion": 1
},
{
"id": "c95e5ad4-0bad-4670-814b-198f51ecb1bb",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1800,
540
],
"parameters": {
"color": 3,
"width": 340,
"height": 280,
"content": "## ⚖️ Pro's and Con's of base64 encoding here\n\nI used the encoding step to be able to define input schema in this node below, it comes with a trade off though: file size of base 64 encoded files are 33% larger, also execution data is becoming quite large. \n\n⚠️Carefully consider if this is usable if you are working with large files. "
},
"typeVersion": 1
},
{
"id": "36278709-336e-43a7-8f66-6f7b29cdca74",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1020,
180
],
"parameters": {
"color": 5,
"width": 460,
"height": 240,
"content": "## 🔧Highlevel dataprep explanation\n\n\n**Prepare Upload Data:** maps inputs to buckets based on mimetype - passes through all other inputs.\n\n**Convert to file:** converts base64 encoded files back to actual files. (see red sticky node at start)"
},
"typeVersion": 1
},
{
"id": "0a2490ad-78ce-4252-884d-900e9848d865",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
-540,
180
],
"parameters": {
"color": 6,
"width": 640,
"height": 240,
"content": "## 🔑 About Supabase Storage\n\nSupabase Storage uses HTTP API with Bearer token authentication, not traditional AWS S3 SDK protocols.\n\nIn this set up we use an anon key/service key. We dont want to use that key all the time, so we generate a signed url to the object.\n\n⚠️ Replace the url in these nodes with your own."
},
"typeVersion": 1
},
{
"id": "309c7e12-bcfa-4756-85e8-2d54639296a6",
"name": "Upload to Supabase Storage",
"type": "n8n-nodes-base.httpRequest",
"onError": "continueErrorOutput",
"position": [
-460,
440
],
"parameters": {
"url": "=https://api-sb.janwillemaltink.com/storage/v1/object/{{ $('Prepare Upload Data').item.json.bucket_name }}/{{ $('Prepare Upload Data').item.json.original_filename }}",
"method": "POST",
"options": {},
"sendBody": true,
"contentType": "binaryData",
"authentication": "predefinedCredentialType",
"inputDataFieldName": "data",
"nodeCredentialType": "supabaseApi"
},
"credentials": {
"supabaseApi": {
"id": "rpq8Z0VD0KxUEfDd",
"name": "Supabase account"
}
},
"typeVersion": 4.2
},
{
"id": "ad17d932-adc3-4a93-8e02-1f52fe8a8196",
"name": "Generate Signed Url",
"type": "n8n-nodes-base.httpRequest",
"onError": "continueErrorOutput",
"position": [
-220,
440
],
"parameters": {
"url": "=https://api-sb.janwillemaltink.com/storage/v1/object/sign/{{ $json.Key }}",
"method": "POST",
"options": {},
"jsonBody": "{\n\t\"expiresIn\": 2592000\n}",
"sendBody": true,
"specifyBody": "json",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "supabaseApi"
},
"credentials": {
"supabaseApi": {
"id": "rpq8Z0VD0KxUEfDd",
"name": "Supabase account"
}
},
"typeVersion": 4.2
},
{
"id": "9be14ebe-84f1-4d91-8c54-392fabf89c05",
"name": "Upload Error Response",
"type": "n8n-nodes-base.set",
"position": [
-260,
620
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "action-type-error",
"name": "action",
"type": "string",
"value": "supabase_upload"
},
{
"id": "error-status",
"name": "status",
"type": "string",
"value": "error"
}
]
},
"includeOtherFields": true
},
"typeVersion": 3.4
},
{
"id": "8dbc6a1e-1a6b-4cc1-ac9f-51fe38c386b8",
"name": "add domain",
"type": "n8n-nodes-base.set",
"position": [
-20,
440
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "d346fd86-3905-4f89-8754-6966c9725a77",
"name": "full_signedURL",
"type": "string",
"value": "=https://api-sb.janwillemaltink.com/storage/v1{{ $json.signedURL }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "e21970f6-23d8-4e23-b42b-f5636dd52358",
"name": "Sign Error Response",
"type": "n8n-nodes-base.set",
"position": [
-20,
620
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "action-type-error",
"name": "action",
"type": "string",
"value": "supabase_sign"
},
{
"id": "error-status",
"name": "status",
"type": "string",
"value": "error"
}
]
},
"includeOtherFields": true
},
"typeVersion": 3.4
}
],
"pinData": {},
"connections": {
"add domain": {
"main": [
[
{
"node": "Success Response",
"type": "main",
"index": 0
}
]
]
},
"Convert to File": {
"main": [
[
{
"node": "Upload to Supabase Storage",
"type": "main",
"index": 0
}
]
]
},
"Success Response": {
"main": [
[]
]
},
"Generate Signed Url": {
"main": [
[
{
"node": "add domain",
"type": "main",
"index": 0
}
],
[
{
"node": "Sign Error Response",
"type": "main",
"index": 0
}
]
]
},
"Prepare Upload Data": {
"main": [
[
{
"node": "Convert to File",
"type": "main",
"index": 0
}
]
]
},
"Upload to Supabase Storage": {
"main": [
[
{
"node": "Generate Signed Url",
"type": "main",
"index": 0
}
],
[
{
"node": "Upload Error Response",
"type": "main",
"index": 0
}
]
]
},
"temp form to test workflow": {
"main": [
[
{
"node": "Prepare Upload Data",
"type": "main",
"index": 0
}
]
]
},
"When Executed by Another Workflow": {
"main": [
[
{
"node": "Prepare Upload Data",
"type": "main",
"index": 0
}
]
]
}
}
}