@heyfilo/sdk
v0.1.1
Published
Official heyfilo SDK — receive published content via webhook, store images outside the build, and serve them live with no rebuild.
Maintainers
Readme
@heyfilo/sdk
Receive published content from heyfilo via webhook in a Node/TypeScript app. Verifies the signature, downloads images to your own domain, rewrites the post body, and serves images live with no rebuild.
npm i @heyfilo/sdkGetting your secret
You don't invent the secret — heyfilo generates it. In the heyfilo portal go to Publishing → Connect via API → Add, enter a name + your webhook URL, and click Create. heyfilo shows the signing secret once — copy it into your environment:
# .env
HEYFILO_SECRET=whsec_...You can Reveal or Regenerate that secret later from the same screen. Pass it
to the SDK as secret (below). If you regenerate it, update HEYFILO_SECRET and
restart your app.
Next.js (App Router)
// app/api/heyfilo-webhook/route.ts
import { createWebhookHandler } from '@heyfilo/sdk/next'
export const POST = createWebhookHandler({
secret: process.env.HEYFILO_SECRET!,
onPost: async (post) => { await db.posts.upsert(post) },
})// app/media/[...path]/route.ts
export { GET } from '@heyfilo/sdk/next/media'Next.js (Pages Router)
// pages/api/heyfilo-webhook.ts
import { createPagesWebhookHandler } from '@heyfilo/sdk/next'
export const config = { api: { bodyParser: false } } // raw body for HMAC
export default createPagesWebhookHandler({ secret: process.env.HEYFILO_SECRET!, onPost })Express
import { webhookMiddleware, mediaRouter } from '@heyfilo/sdk/express'
app.post('/api/heyfilo-webhook',
express.raw({ type: 'application/json' }),
webhookMiddleware({ secret: process.env.HEYFILO_SECRET!, onPost }))
app.use('/media', mediaRouter())Options
| Option | Default | Notes |
|---|---|---|
| secret | — | HMAC secret / bearer / api-key |
| authType | 'hmac' | 'hmac' \| 'bearer' \| 'api_key' \| 'none' |
| mountPath | '/media' | Public path images are served at |
| storage | LocalStorage | Implement StorageBackend for S3/R2 |
| imageFailureMode | 'fail' | 'fail' = 5xx + clear reason (heyfilo retries); 'skip' = keep the original image URL and still save the post |
| onPost(post, raw) | — | Called after images are localized |
| onTest(raw) | — | connection.test ping |
| isDuplicate / markProcessed | — | Idempotency hooks (your DB) |
Deploying — pick a writable media directory (important)
The media directory resolves as env HEYFILO_MEDIA_DIR → dir option → <cwd>/.heyfilo/media
and is created automatically — but the app process must be able to write there.
The default (<app-root>/.heyfilo) often is not writable in production (the app
root may be owned by a different user, or read-only on serverless). When it can't
write, you'll get a clear error like:
Cannot write to media dir "/srv/app/.heyfilo" (EACCES). Set the HEYFILO_MEDIA_DIR env var…
Fix: set HEYFILO_MEDIA_DIR to a path your runtime user owns, and make sure the
media route reads from the same path. Example (VPS / PM2):
# .env — a storage dir the app user can write to
HEYFILO_MEDIA_DIR=/srv/apps/yoursite/storage/heyfilomkdir -p /srv/apps/yoursite/storage/heyfilo
chown -R <runtime-user> /srv/apps/yoursite/storageOn serverless (Vercel/Netlify) the disk is read-only/ephemeral — use an
object-storage storage backend (below) instead of the local filesystem.
Custom storage (serverless / S3 / R2)
import type { StorageBackend } from '@heyfilo/sdk'
const s3: StorageBackend = {
async exists(key) { /* ... */ return false },
async put(key, data, contentType) { /* upload */ },
async read(key) { /* return { data, contentType } or null */ return null },
}
createWebhookHandler({ secret, storage: s3, onPost })Develop
npm install
npm run build # tsc -> dist/
npm test # node --test (runs ../../contract/conformance vectors)MIT
