Server
Hono
Uplo ships with a framework-agnostic route handler that returns a standard fetch handler — perfect for Hono on any runtime (Node, Bun, Cloudflare Workers, Deno).
npm i @uplo/node honoimport { Hono } from 'hono'
import { createUplo } from '@uplo/node'
import { createUploRouteHandler } from '@uplo/server/route-handler'
import { createPrismaAdapter } from '@uplo/adapter-prisma'
import { createS3Service } from '@uplo/service-s3'
const uplo = createUplo({
config: {
privateKey: process.env.APPLICATION_SECRET,
signedIdExpiresIn: 60 * 60,
},
adapter: createPrismaAdapter({ prisma }),
services: {
s3: createS3Service({
isPublic: false,
region: process.env.AWS_REGION,
bucket: process.env.AWS_BUCKET,
accessKeyId: '*****',
secretAccessKey: '*****',
}),
},
attachments: {},
})
const app = new Hono()
const uploHandler = createUploRouteHandler({ uplo })
app.all('/uplo/*', (c) => uploHandler(c.req.raw))
export default appYou can change the mount path with the basePath option:
const uploHandler = createUploRouteHandler({ uplo, basePath: '/api/uplo' })Routes
POST /uplo/create-direct-upload: creates a blob and returns a Signed ID with upload URL and headers. Returns201, or422with{ success: false, error: { message } }for invalid requests, unknown attachments, failed validation or disabled direct uploads.
Authentication
The route handler has no authentication, so anyone who can reach it can upload files to your bucket. Add your own middleware before it:
app.use('/uplo/*', async (c, next) => {
const user = await getCurrentUser(c) // your auth
if (!user) {
return c.json({ success: false, error: { message: 'Unauthorized' } }, 401)
}
await next()
})
app.all('/uplo/*', (c) => uploHandler(c.req.raw))Uploading alone does not attach a file to a record. Check that the user can
change the record before calling attachSignedFile.