@geekapps/silo-fastify
v0.3.0
Published
Fastify plugin to embed Silo file handling in any Fastify app
Readme
@geekapps/silo-fastify
Fastify plugin that lets any Fastify app expose presigned-upload routes backed by the central Silo API — no local disk storage. The plugin authenticates to Silo with a service account (OAuth2 client-credentials) and proxies the multipart-upload flow: callers ask for an upload URL, PUT the file bytes directly to storage, then hit the complete route to finalize.
This package is a server plugin, not a client SDK. For browser/Next.js uploads use @geekapps/silo-nextjs instead.
Install
import Fastify from "fastify";
import siloPlugin from "@geekapps/silo-fastify";
const app = Fastify();
await app.register(siloPlugin, {
apiUrl: "https://api.silo.geekapps.com.br",
serviceAccount: {
issuerUrl: "https://api.silo.geekapps.com.br",
clientId: process.env.SILO_CLIENT_ID!,
clientSecret: process.env.SILO_CLIENT_SECRET!,
},
});Options (SiloPluginOptions)
| Option | Required | Default | Description |
| ---------------- | -------- | ----------- | ----------------------------------------------------- |
| apiUrl | yes | — | Base URL of the central Silo API |
| serviceAccount | no | — | { issuerUrl, clientId, clientSecret } for OAuth2 client-credentials auth — required for the built-in /silo/* routes and silo.request |
| issuerUrl | no | serviceAccount.issuerUrl | OAuth2 issuer used by silo.generateToken; only needed on its own if you use generateToken without also setting serviceAccount |
| defaultBucket | no | "default" | Bucket used when the caller doesn't specify one |
| prefix | no | /silo | Route prefix |
Upload flow
POST {prefix}/upload/start— body:{ fileName, mimeType, size, bucket?, isPrivate?, expiresIn?, image?, video? }. Starts a multipart upload on the central Silo API and returns a presigned URL for part 1:{ "fileId": "...", "uploadId": "...", "uploadUrl": "https://..." }The caller
PUTs the file bytes touploadUrldirectly (bypassing this server) and keeps the returnedETag.For files split into more than one part,
GET {prefix}/upload/:fileId/part?uploadId=&partNumber=returns a presigned URL for any additional part.POST {prefix}/upload/:fileId/complete— the caller reports completion here once all parts are uploaded. Body:{ uploadId, parts: [{ PartNumber, ETag }], image?, video? }. This finalizes the file on Silo and returns the resulting file record.DELETE {prefix}/upload/:fileId— body:{ uploadId }. Aborts an in-progress upload.
Downloads
POST {prefix}/files/:fileId/sign — body: { ttl?, disposition? }. Proxies to the central Silo API's sign endpoint and returns a presigned download URL.
Decorators
fastify.silo.request(path, init?)— call any other central Silo API route directly, using the same service-account auth as the routes above. RequiresserviceAccountto be set.fastify.silo.generateToken(clientIdEnvVar, clientSecretEnvVar)— mints a short-lived Silo access token for a service account. Takes env var names, not values — it readsprocess.env[clientIdEnvVar]/process.env[clientSecretEnvVar]itself, so a dev calling this never has the raw secret in hand to accidentally log, hardcode, or hand to the wrong client. Use this to hand a token to an untrusted client (e.g. a mobile app) that needs to talk to Silo directly —@geekapps/silo-react-nativeis built around exactly this pattern, requiring a token per call that your backend hands out.// .env: SILO_SA_BILLING_ID=..., SILO_SA_BILLING_SECRET=... app.get("/api/silo-token", { onRequest: [app.authenticate] }, async (request) => { const token = await app.silo.generateToken("SILO_SA_BILLING_ID", "SILO_SA_BILLING_SECRET"); return { token }; });Tokens are cached per resolved
(clientId, clientSecret)pair — callinggenerateTokenrepeatedly with the same env var names reuses the cached token until shortly before it expires. Requires eitherserviceAccount.issuerUrlor the standaloneissuerUrloption to be set. Put your own auth in front of a route like this (as shown above) — anyone who can call it gets a working Silo token.
⚠️ No caller authentication
None of the routes authenticate who is calling — they only authenticate this server to the central Silo API via the service account. Anyone who can reach these routes can start/complete uploads and mint signed download URLs. Put your own auth (API gateway, internal network boundary, or a Fastify onRequest hook) in front of this plugin if callers need to be authenticated or scoped.
