@rikology/adonisjs-cloudinary
v0.4.0
Published
Cloudinary integration for AdonisJS v7 — upload, transform, and deliver media through the Cloudinary v2 SDK, with IoC container support, Edge helpers, and an optional FlyDrive bridge.
Downloads
530
Maintainers
Readme
@rikology/adonisjs-cloudinary
Cloudinary integration for AdonisJS v7 — upload, transform, and deliver media through the Cloudinary v2 SDK, with first-class IoC container support, Edge templating helpers, and an optional FlyDrive bridge.
Features
- 🚀 Typed service wrapping the Cloudinary v2 SDK, injectable anywhere via the IoC container
- 📸 Upload helpers for images, video, and raw files with a single call
- 🎨 URL transformations generated synchronously, including in Edge templates
- 🔐 Signed/expiring URLs with
Dateor Unix-timestamp expiration - 🗄️ Optional FlyDrive bridge to use Cloudinary as a Drive disk
- 🧩 Zero-config Edge helper that registers itself when Edge is installed (and stays out of the way in API-only apps)
- 📦 Standalone helpers for scripts, workers, and non-AdonisJS projects
- 🩺
cloudinary:pingace command to verify your credentials right after install
Documentation
| Topic | Document |
| --- | --- |
| Install, configure, first upload | Getting Started |
| defineConfig, config types, container binding | Configuration |
| Full CloudinaryService method reference | Service API |
| The cloudinaryUrl Edge global | Edge Helpers |
| Using Cloudinary as a FlyDrive disk | Drive Bridge |
| Scripts, workers, non-AdonisJS usage | Standalone Helpers |
| Raw SDK, Admin/Search API, presets, multi-account | Advanced |
| Avatars, galleries, signed URLs, cleanup | Recipes |
Installation
node ace add @rikology/adonisjs-cloudinaryThat single command installs the package, publishes config/cloudinary.ts, registers the
CloudinaryProvider and the cloudinary:* ace commands in adonisrc.ts, and wires env
validation for your CLOUDINARY_* keys.
Prefer the manual route?
# install
npm install @rikology/adonisjs-cloudinary # or: pnpm/yarn/bun add
# then configure
node ace configure @rikology/adonisjs-cloudinaryVerify your credentials are wired up:
node ace cloudinary:pingEnvironment variables
Add these to your .env (values from the Cloudinary console):
| Variable | Required | Description |
| ----------------------- | -------- | -------------------------- |
| CLOUDINARY_CLOUD_NAME | Yes | Your Cloudinary cloud name |
| CLOUDINARY_API_KEY | Yes | Your Cloudinary API key |
| CLOUDINARY_API_SECRET | Yes | Your Cloudinary API secret |
node ace add / configure auto-registers these in your env validator (start/env.ts). The entry it adds looks like:
import { Env } from '@adonisjs/core/env'
export default await Env.create(new URL('../', import.meta.url), {
CLOUDINARY_CLOUD_NAME: Env.schema.string(),
CLOUDINARY_API_KEY: Env.schema.string(),
CLOUDINARY_API_SECRET: Env.schema.string(),
})Quick start
Inject the service and upload:
import { inject } from '@adonisjs/core'
import type { CloudinaryService } from '@rikology/adonisjs-cloudinary'
@inject()
export default class UploadsController {
constructor(protected cloudinary: CloudinaryService) {}
async store({ request, response }) {
const file = request.file('avatar')!
const result = await this.cloudinary.uploadImage(file.tmpPath!, {
folder: 'avatars',
transformation: { width: 200, height: 200, crop: 'fill' },
})
return response.ok({ url: result.secure_url, publicId: result.public_id })
}
}Render a transformed URL in Edge (the cloudinaryUrl global registers itself automatically):
<img
src="{{ cloudinaryUrl('avatars/photo', { width: 200, height: 200, crop: 'fill' }) }}"
alt="Avatar"
/>Service API (summary)
The CloudinaryService is bound to the container as cloudinary. Full reference: Service API.
| Method | Description |
| ------------------------------------------ | ----------------------------------------------------- |
| sdk | Raw cloudinary v2 object for direct SDK calls |
| uploadImage(file, options?) | Upload with resource_type: 'image' |
| uploadVideo(file, options?) | Upload with resource_type: 'video' |
| uploadFile(file, options?) | Upload with resource_type: 'raw' |
| transformUrl(publicId, transformations?) | Generate a delivery URL with optional transformations |
| signedUrl(publicId, options?) | Generate a signed URL, optionally with expiresAt |
| uploadStream(options?) | Return a cloudinary.uploader.upload_stream() stream |
| destroy(publicId, options?) | Delete an asset (defaults resource_type: 'image') |
// Signed URL expiring in 1 hour
const url = cloudinary.signedUrl('private-doc', {
expiresAt: Date.now() / 1000 + 3600,
})For scripts and workers without the container:
import { createCloudinaryService, transformUrl } from '@rikology/adonisjs-cloudinary'
const cloudinary = createCloudinaryService({
cloudName: 'demo',
apiKey: '123456',
apiSecret: 'secret',
})
const url = transformUrl('my-image', { cloudName: 'demo', apiKey: 'k', apiSecret: 's' }, { width: 200 })See Standalone Helpers.
Drive bridge
An optional FlyDrive driver maps Drive operations onto Cloudinary API calls. It's imported from the /drive subpath so it never loads unless you use it:
import { CloudinaryDrive } from '@rikology/adonisjs-cloudinary/drive'
import { createCloudinaryService } from '@rikology/adonisjs-cloudinary'
const cloudinary = createCloudinaryService({
cloudName: env.get('CLOUDINARY_CLOUD_NAME'),
apiKey: env.get('CLOUDINARY_API_KEY'),
apiSecret: env.get('CLOUDINARY_API_SECRET'),
})
const driveConfig = defineConfig({
default: 'cloudinary',
services: {
cloudinary: () => new CloudinaryDrive(cloudinary),
},
})Requires the optional
flydrive(or@adonisjs/drive) peer dependency. Cloudinary is a media platform, not a byte-level object store — see the Drive Bridge guide for supported/unsupported operations.
Advanced
Need something the wrappers don't cover? The raw SDK is one property away:
const cloudinary = await app.container.make('cloudinary')
// Admin API
await cloudinary.sdk.api.resources({ type: 'upload', prefix: 'avatars/', max_results: 50 })
// Search API
await cloudinary.sdk.search
.expression('resource_type:image AND tags=featured')
.sort_by('created_at', 'desc')
.max_results(30)
.execute()
// Upload presets
await cloudinary.sdk.uploader.upload(file, { upload_preset: 'my-preset' })See Advanced for Admin/Search API patterns, upload presets, multi-account strategies, and testing.
Contributing
See CONTRIBUTING.md. Contributions, issues, and feature requests are welcome.
License
MIT © Riko Riswandha Fahmi Prasetyo
