Security News
GitHub Removes Malicious Pull Requests Targeting Open Source Repositories
GitHub removed 27 malicious pull requests attempting to inject harmful code across multiple open source repositories, in another round of low-effort attacks.
adonis-responsive-attachment
Advanced tools
Turn any field on your Lucid models to an image attachment data type while automatically generating responsive formats for image files.
The Adonis Responsive Attachment package allows you to generate and persist optimised responsive images from uploaded images. It integrates with AdonisJS Lucid by converting any column on your Lucid model to an image attachment data type via the @responsiveAttachment
decorator.
Adonis Responsive Attachment generates very detailed metadata of the original file and generated responsive images and persists the metadata to the decorated
column within the database. It does not require any additional database tables and stores the file metadata as JSON within the same specified/decorated column.
This add-on only accepts image files and is a fork of the Attachment Lite add-on. The main difference between the Adonis Responsive Attachment
and Attachment Lite
is that Attachment Lite
accepts all file types while Adonis Responsive Attachment
only accepts image files. Also, Attachment Lite
only persists the original uploaded file plus its metadata while Adonis Responsive Attachment
persists the uploaded image and generated responsive images to disk and their metadata to the database.
The ability of your application/website to serve different sizes of the same image across different devices is an important factor for improving the performance of your application/website. If your visitor is accessing your website with a mobile device whose screen width is less than 500px, it is performant and data-friendly to serve that device a banner which isn't wider than 500px. On the other hand, if a visitor is accessing your website with a laptop with a minimum screen size of 1400px, it makes sense not to serve that device a banner whose width is less than 1200px so that the image does not appear pixelated.
The Adonis Responsive Attachment add-on provides the ability to generate unlimited number of responsive sizes from an uploaded image and utilise the srcset
and sizes
attributes to serve and render different sizes of the same image to a visitor based on the size of their screen. You should get familiar with this concept by studying the Responsive Images topic on MDN.
Let us assume you are developing a blog. On the article page, you need to upload a cover image. You also need to generate responsive sizes of the uploaded cover image so that you can serve different sizes to different devices based on their screen sizes. This add-on will optimise and persist the original cover image saving you up to 50% reduction is the file size. It will also generate and persist optimised responsive cover images at various breakpoints which you can customise. Additionally, for the original and responsive cover images, the add-on will generate detailed metadata of the images and persist the metadata as the value for the column within the database.
On the frontend of your blog, you can use the srcset
attribute of the img
element to define and serve the different cover image sizes. You can also use the picture
wrapper element with the source
element to define and serve the responsive cover images.
name
, width
, height
, size
, format
, mimetype
, hash
, extname
, and url
.jpeg
, png
, webp
, tiff
, and avif
.The attachment-lite
package requires @adonisjs/lucid >= v16.3.1
and @adonisjs/core >= 5.3.4
.
It relies on AdonisJS drive for writing files on the disk.
It also relies heavily on the Sharp image manipulation library for performing image optimisations and generation of responsive images.
Install the package from the npm registry as follows.
yarn add adonis-responsive-attachment
Next, configure the package by running the following ace command.
node ace configure adonis-responsive-attachment
First and very importantly, this addon generates a large metadata for the original and generated images which will persisted to the database. So, the column for storing the metadata must be a JSON data type.
If you are creating the column for the first time, make sure that you use the JSON data type. Example:
protected tableName = 'posts'
public async up() {
this.schema.createTable(this.tableName, (table) => {
table.increments()
table.json('cover_image') // <-- Use a JSON data type
})
}
If you already have a column for storing image paths/URLs, you need to create a new migration and alter the column definition to a JSON data type. Example:
node ace make:migration change_cover_image_column_to_json --table=posts
protected tableName = 'posts'
public async up() {
this.schema.alterTable(this.tableName, (table) => {
table.json('cover_image').alter() // <-- Alter the column definition
})
}
The next step is to import the responsiveAttachment
decorator and the ResponsiveAttachmentContract
interface from the adonis-responsive-attachment
package.
Make sure NOT to use the
@column
decorator when using the@responsiveAttachment
decorator.
import { BaseModel } from '@ioc:Adonis/Lucid/Orm'
import {
responsiveAttachment,
ResponsiveAttachmentContract
} from '@ioc:Adonis/Addons/ResponsiveAttachment'
class Post extends BaseModel {
@responsiveAttachment()
public coverImage: ResponsiveAttachmentContract
}
There are two ways to create responsive attachments with the Adonis Responsive Attachment
add-on:
The fromFile
static method:
The fromFile
method allows you to create responsive images from images upload via HTTP requests. It takes one parameter which is the file
output of the request.file()
method
The fromBuffer
static method:
The fromBuffer
method creates responsive images from (image) buffers. These images buffers can come from any source you prefer as long as they are of type Buffer
. This allows you to create responsive images from outside the HTTP life-cycle. The fromBuffer
method accepts one parameter which must be a Buffer
.
The example below shows the use of the fromFile
static method.
import { ResponsiveAttachment } from '@ioc:Adonis/Addons/ResponsiveAttachment'
class PostsController {
public store({ request }: HttpContextContract) {
const coverImage = request.file('coverImage')!
const post = new Post()
post.coverImage = coverImage ? await ResponsiveAttachment.fromFile(coverImage) : null
await post.save()
}
}
The example below shows the use of the fromBuffer
static method.
import { ResponsiveAttachment } from '@ioc:Adonis/Addons/ResponsiveAttachment'
import { readFile } from 'fs/promises'
class UsersController {
public store() {
const buffer = await readFile(join(__dirname, '../me.jpeg'))
const user = new User()
user.avatar = await ResponsiveAttachment.fromBuffer(buffer)
await user.save()
}
}
NOTE: You should
await
the operationResponsiveAttachment.fromFile(coverImage)
as the uploaded image is being temporarily persisted during thefromFile
operation. This is a bit different from the approach of theattachment-lite
add-on. In order to offer a uniform syntax you are required to also await the methodResponsiveAttachment.fromBuffer
.
The ResponsiveAttachment.fromFile
or ResponsiveAttachment.fromBuffer
static method creates an instance of the ResponsiveAttachment
class from the uploaded image or provider buffer. When you persist the model to the database, the adonis-responsive-attachment
add-on will write the file or buffer to the disk and generate optimised responsive images and thumbnails from the original image.
You can update the property with a newly image, and the package will take care of removing the old images and generating and persisting new responsive images.
import { ResponsiveAttachment } from '@ioc:Adonis/Addons/ResponsiveAttachment'
class PostsController {
public update({ request }: HttpContextContract) {
const post = await Post.firstOrFail()
const coverImage = request.file('coverImage')!
post.coverImage = coverImage ? await ResponsiveAttachment.fromFile(coverImage) : null
// Old file will be removed from the disk as well.
await post.save()
}
}
Or using the fromBuffer
method:
import { ResponsiveAttachment } from '@ioc:Adonis/Addons/ResponsiveAttachment'
import { readFile } from 'fs/promises'
class UsersController {
public store() {
const buffer = await readFile(join(__dirname, '../me.jpeg'))
const user = await User.firstOrFail()
user.avatar = buffer ? await ResponsiveAttachment.fromBuffer(buffer) : null
// Old file will be removed from the disk as well.
await user.save()
}
}
Similarly, assign null
value to the model property to delete the file without assigning a new file.
Also, make sure you update the property type on the model to be null
as well.
class Post extends BaseModel {
@responsiveAttachment()
public coverImage: ResponsiveAttachmentContract | null
}
const post = await Post.first()
post.coverImage = null
// Removes the original and responsive images from the disk
await post.save()
Upon deleting the model instance, all the related original and responsive images will be removed from the disk.
Do note: For attachment lite to delete files, you will have to use the
modelInstance.delete
method. Usingdelete
on the query builder will not work.
const post = await Post.first()
// Removes any image attachments related to this post
await post.delete()
responsiveAttachment
Decorator OptionsThe responsiveAttachment
decorator accepts the following options:
disk
- string,folder
- string,breakpoints
- object,forceFormat
- "jpeg" | "png" | "webp" | "tiff" | "avif",optimizeSize
- boolean,optimizeOrientation
- boolean,responsiveDimensions
- boolean,preComputeUrls
- boolean,disableThumbnail
- boolean.keepOriginal
- boolean.Let's discuss these options
disk
optionBy default, all images are written/deleted from the default disk. However, you can specify a custom disk at the time of using the responsiveAttachment
decorator.
The
disk
property value is never persisted to the database. It means, if you first define the disk ass3
, upload a few files and then change the disk value togcs
, the package will look for files using thegcs
disk.
class Post extends BaseModel {
@responsiveAttachment({ disk: 's3' })
public coverImage: ResponsiveAttachmentContract
}
folder
optionYou can also store files inside the subfolder by defining the folder
property as follows.
class Page extends BaseModel {
@responsiveAttachment({ folder: 'cover-images/pages' })
public coverImage: ResponsiveAttachmentContract
}
// or
class Post extends BaseModel {
@responsiveAttachment({ folder: 'cover-images/posts' })
public coverImage: ResponsiveAttachmentContract
}
breakpoints
The breakpoints
option accepts an object which contains the definition for the breakpoints for the generation of responsive images. By default, it has the following value:
{
large: 1000,
medium: 750,
small: 500,
}
With the above default values, the adonis-responsive-attachment
add-on will generate three (3) responsive images whose widths are exactly 1000px
, 750px
, and 500px
.
In addition, the adonis-responsive-attachment
add-on will generate a thumbnail width the following resize options:
{
width: 245,
height: 156,
fit: 'inside' as sharp.FitEnum['inside'],
}
This means that if the width of the original image is greater than 245px
or the height of th original image is greater than 156px
, a thumbnail will be generated with the inside
fit type. Learn more here.
If you need to customise the breakpoints
options, you need to overwrite the default properties large
, medium
, and small
with your own values. You can also add new properties to the default ones.
class Post extends BaseModel {
@responsiveAttachment(
{
breakpoints: {
xlarge: 1400, // This is a custom/extra breakpoint
large: 1050, // Make sure you overwrite `large`
medium: 800, // Make sure you overwrite `medium`
small: 550, // Make sure you overwrite `small`
}
}
)
public coverImage: ResponsiveAttachmentContract
}
const post = await Post.findOrFail(1)
post.coverImage.name // exists
post.coverImage.breakpoints.thumbnail.name // exists
post.coverImage.breakpoints.small.name // exists
post.coverImage.breakpoints.medium.name // exists
post.coverImage.breakpoints.large.name // exists
post.coverImage.breakpoints.xlarge.name // extra breakpoint exists
You can also choose to cherry-pick which breakpoint image you want to generate
class Post extends BaseModel {
@responsiveAttachment(
{
breakpoints: {
large: 'off', // Disable the `large` breakpoint
medium: 'off', // Disable the `medium` breakpoint
small: 550, // Make you overwrite `small`
}
}
)
public coverImage: ResponsiveAttachmentContract
}
const post = await Post.findOrFail(1)
post.coverImage.name // exists
post.coverImage.breakpoints.thumbnail.name // exists
post.coverImage.breakpoints.small.name // exists
post.coverImage.breakpoints.medium // does not exist
post.coverImage.breakpoints.large // does not exist
forceFormat
OptionThe forceFormat
option is used to change the image from one format to another. By default, the adonis-responsive-attachment
addon will maintain the format of the uploaded image when persisting the original image and generating the responsive images. However, assuming you want to force the conversion of all supported formats to the webp
format, you can do:
class Post extends BaseModel {
@responsiveAttachment({forcedFormat: 'webp'})
public coverImage: ResponsiveAttachmentContract
}
This will persist the original image and generated responsive images in the webp
format.
{
name: 'original_ckw5lpv7v0002egvobe1b0oav.webp',
size: 291.69,
hash: 'ckw5lpv7v0002egvobe1b0oav',
width: 1500,
format: 'webp',
height: 1000,
extname: 'webp',
mimeType: 'image/webp',
url: null,
breakpoints: {
thumbnail: {
name: 'thumbnail_ckw5lpv7v0002egvobe1b0oav.webp',
hash: 'ckw5lpv7v0002egvobe1b0oav',
extname: 'webp',
mimeType: 'image/webp',
width: 234,
height: 156,
size: 7.96,
},
large: {
name: 'large_ckw5lpv7v0002egvobe1b0oav.webp',
hash: 'ckw5lpv7v0002egvobe1b0oav',
extname: 'webp',
mimeType: 'image/webp',
width: 1000,
height: 667,
size: 129.15,
},
medium: {
name: 'medium_ckw5lpv7v0002egvobe1b0oav.webp',
hash: 'ckw5lpv7v0002egvobe1b0oav',
extname: 'webp',
mimeType: 'image/webp',
width: 750,
height: 500,
size: 71.65,
},
small: {
name: 'small_ckw5lpv7v0002egvobe1b0oav.webp',
hash: 'ckw5lpv7v0002egvobe1b0oav',
extname: 'webp',
mimeType: 'image/webp',
width: 500,
height: 333,
size: 32.21,
},
},
}
optimizeSize
OptionThe optimizeSize
option enables the optimisation of the uploaded image and then use the optimised version of the uploaded image to persist the original image and generate the responsive images. By default, this is set to true
. However, you can disable this behaviour by setting optimizeSize
to false
:
class Post extends BaseModel {
@responsiveAttachment({optimizeSize: false})
public coverImage: ResponsiveAttachmentContract
}
optimizeOrientation
OptionThe optimizeOrientation
option ensures that the orientation of the uploaded image is corrected through auto-rotation
if the add-on detects that the orientation is not correct. This option is set to true
by default but you can disable this behaviour by setting optimizeOrientation
to false
:
class Post extends BaseModel {
@responsiveAttachment({optimizeOrientation: false})
public coverImage: ResponsiveAttachmentContract
}
const post = await Post.findOrFail(1)
post.coverImage.name // exists
post.coverImage.breakpoints // undefined
responsiveDimensions
OptionThe responsiveDimensions
option allows the generation of the thumbnail and responsive images from the uploaded image. This option is set to true
by default but if you do not need to generate responsive images, you can disable this behaviour by setting responsiveDimensions
to false
:
class Post extends BaseModel {
@responsiveAttachment({responsiveDimensions: false})
public coverImage: ResponsiveAttachmentContract
}
preComputeUrls
OptionRead more about this option in this section: Using the preComputeUrls Option.
disableThumbnail
OptionThe disableThumbnail
option, if set to true
, allows you to disable the generation of the thumbnail without affecting the generation of other breakpoint images.
class Post extends BaseModel {
@responsiveAttachment({disableThumbnail: true})
public coverImage: ResponsiveAttachmentContract
}
const post = await Post.findOrFail(1)
post.coverImage.name // exists
post.coverImage.breakpoints.small.name // exists
post.coverImage.breakpoints.medium.name // exists
post.coverImage.breakpoints.large.name // exists
post.coverImage.breakpoints.thumbnail // does not exist
keepOriginal
OptionThe keepOriginal
option allows you to decide whether to keep the original uploaded image or not. If you do not have any need for the original image in the future, there should be no need to keep it. By default keepOriginal
is true
but you can disable it by setting it to false
.
class Post extends BaseModel {
@responsiveAttachment({keepOriginal: false})
public coverImage: ResponsiveAttachmentContract
}
const post = await Post.findOrFail(1)
post.coverImage.name // does not exist
post.coverImage.hash // does not exist
post.coverImage.width // does not exist
post.coverImage.format // does not exist
post.coverImage.height // does not exist
post.coverImage.extname // does not exist
post.coverImage.mimeType // does not exist
post.coverImage.breakpoints.small.name // exists
post.coverImage.breakpoints.medium.name // exists
post.coverImage.breakpoints.large.name // exists
post.coverImage.breakpoints.thumbnail.name // exists
By default, the adonis-responsive-attachment
, will not generate the URLs of the original and responsive images to the JSON metadata which is persisted to the database. This helps reduce the size of the JSON. The same of the JSON will look as shown below. Notice that the root url
property (which is the URL of the original image) is null while the url
property is missing in the metadata of the breakpoint images.
{
name: 'original_ckw5lpv7v0002egvobe1b0oav.jpg',
size: 291.69,
hash: 'ckw5lpv7v0002egvobe1b0oav',
width: 1500,
format: 'jpeg',
height: 1000,
extname: 'jpg',
mimeType: 'image/jpeg',
url: null,
breakpoints: {
thumbnail: {
name: 'thumbnail_ckw5lpv7v0002egvobe1b0oav.jpg',
hash: 'ckw5lpv7v0002egvobe1b0oav',
extname: 'jpg',
mimeType: 'image/jpeg',
width: 234,
height: 156,
size: 7.96,
},
large: {
name: 'large_ckw5lpv7v0002egvobe1b0oav.jpg',
hash: 'ckw5lpv7v0002egvobe1b0oav',
extname: 'jpg',
mimeType: 'image/jpeg',
width: 1000,
height: 667,
size: 129.15,
},
medium: {
name: 'medium_ckw5lpv7v0002egvobe1b0oav.jpg',
hash: 'ckw5lpv7v0002egvobe1b0oav',
extname: 'jpg',
mimeType: 'image/jpeg',
width: 750,
height: 500,
size: 71.65,
},
small: {
name: 'small_ckw5lpv7v0002egvobe1b0oav.jpg',
hash: 'ckw5lpv7v0002egvobe1b0oav',
extname: 'jpg',
mimeType: 'image/jpeg',
width: 500,
height: 333,
size: 32.21,
},
},
}
If you want to enable the automatic generation of the URLs of the original and responsive images, you have two options:
preComputeUrls
option to true
in the responsiveAttachment
decorator,ResponsiveAttachment.getUrls
method.preComputeUrls
OptionThe preComputeUrls
option when enabled (i.e. set to true
) will pre-compute the URLs of the original and responsive images when you find
, fetch
, or paginate
the model which the responsive attachment is defined within. For example:
class Post extends BaseModel {
@responsiveAttachment({ preComputeUrls: true })
public coverImage: ResponsiveAttachmentContract
}
Fetch
resultconst posts = await Post.all()
posts[0].coverImage.url // pre-computed
posts[0].coverImage.breakpoints.thumbnail.url // pre-computed
posts[0].coverImage.breakpoints.small.url // pre-computed
posts[0].coverImage.breakpoints.medium.url // pre-computed
posts[0].coverImage.breakpoints.large.url // pre-computed
posts[0].coverImage.urls // pre-computed
Find
resultconst post = await Post.findOrFail(1)
post.coverImage.url // pre-computed
post.coverImage.breakpoints.thumbnail.url // pre-computed
post.coverImage.breakpoints.small.url // pre-computed
post.coverImage.breakpoints.medium.url // pre-computed
post.coverImage.breakpoints.large.url // pre-computed
posts.coverImage.urls // pre-computed
Pagination
resultconst posts = await Post.query.paginate(1)
posts[0].coverImage.url // pre-computed
posts[0].coverImage.breakpoints.thumbnail.url // pre-computed
posts[0].coverImage.breakpoints.small.url // pre-computed
posts[0].coverImage.breakpoints.medium.url // pre-computed
posts[0].coverImage.breakpoints.large.url // pre-computed
posts[0].coverImage.urls // pre-computed
The preComputeUrl
property will generate the URLs and set it on the ResponsiveAttachment class instance. Also, a signed URL is generated when the disk is private, and a normal URL is generated when the disk is public.
Pre-computation stores a JSON with url
properties for the original and responsive images to the database. Typically, the JSON will look like this:
{
name: 'original_ckw5lpv7v0002egvobe1b0oav.jpg',
size: 291.69,
hash: 'ckw5lpv7v0002egvobe1b0oav',
width: 1500,
format: 'jpeg',
height: 1000,
extname: 'jpg',
mimeType: 'image/jpeg',
url: '/uploads/original_ckw5lpv7v0002egvobe1b0oav.jpg?signature=eyJtZXNzYWdlIjoiL3VwbG9hZHMvb3JpZ2luYWxfY2t3NWxwdjd2MDAwMmVndm9iZTFiMG9hdi5qcGcifQ.ieXMlaRb8izlREvJ0E9iMY0I3iedalmv-pvOUIrfEZc',
breakpoints: {
thumbnail: {
name: 'thumbnail_ckw5lpv7v0002egvobe1b0oav.jpg',
hash: 'ckw5lpv7v0002egvobe1b0oav',
extname: 'jpg',
mimeType: 'image/jpeg',
width: 234,
height: 156,
size: 7.96,
url: '/uploads/thumbnail_ckw5lpv7v0002egvobe1b0oav.jpg?signature=eyJtZXNzYWdlIjoiL3VwbG9hZHMvdGh1bWJuYWlsX2NrdzVscHY3djAwMDJlZ3ZvYmUxYjBvYXYuanBnIn0.RGGimHh6NuyPrB2ZgmudE7rH4RRCT3NL7kex9EmSyIo',
},
large: {
name: 'large_ckw5lpv7v0002egvobe1b0oav.jpg',
hash: 'ckw5lpv7v0002egvobe1b0oav',
extname: 'jpg',
mimeType: 'image/jpeg',
width: 1000,
height: 667,
size: 129.15,
url: '/uploads/large_ckw5lpv7v0002egvobe1b0oav.jpg?signature=eyJtZXNzYWdlIjoiL3VwbG9hZHMvbGFyZ2VfY2t3NWxwdjd2MDAwMmVndm9iZTFiMG9hdi5qcGcifQ.eNC8DaqYCYd4khKhqS7DKI66SsLpD-vyVIaP8rzMmAA',
},
medium: {
name: 'medium_ckw5lpv7v0002egvobe1b0oav.jpg',
hash: 'ckw5lpv7v0002egvobe1b0oav',
extname: 'jpg',
mimeType: 'image/jpeg',
width: 750,
height: 500,
size: 71.65,
url: '/uploads/medium_ckw5lpv7v0002egvobe1b0oav.jpg?signature=eyJtZXNzYWdlIjoiL3VwbG9hZHMvbWVkaXVtX2NrdzVscHY3djAwMDJlZ3ZvYmUxYjBvYXYuanBnIn0.2ADmssxFC0vxmq4gJEgjb9Fxo1qcQ6tMVeKBqZ1ENkM',
},
small: {
name: 'small_ckw5lpv7v0002egvobe1b0oav.jpg',
hash: 'ckw5lpv7v0002egvobe1b0oav',
extname: 'jpg',
mimeType: 'image/jpeg',
width: 500,
height: 333,
size: 32.21,
url: '/uploads/small_ckw5lpv7v0002egvobe1b0oav.jpg?signature=eyJtZXNzYWdlIjoiL3VwbG9hZHMvc21hbGxfY2t3NWxwdjd2MDAwMmVndm9iZTFiMG9hdi5qcGcifQ.I8fwMRwY5azvlS_8B0K40BWKQNLuS-HqCB_3RXryOok',
},
},
}
ResponsiveAttachment.getUrls
MethodIf you manually generate signed or un-signed URLs for a given image attachment using the getUrls
method. This method calls the ResponsiveAttachment.preComputeUrls
method internally to compute the URLs of original and responsive images and returns the result as an object containing the various URLs. It also adds the URLs to the this.url
and this.breakpoints
properties so that you can access the URLs normally when you serialise the result as JSON.
// For unsigned URLs, do not pass in any options
await post.coverImage.getUrls()
// After
post.coverImage.url // computed
post.coverImage.breakpoints.thumbnail.url // computed
post.coverImage.breakpoints.small.url // computed
post.coverImage.breakpoints.medium.url // computed
post.coverImage.breakpoints.large.url // computed
posts.coverImage.urls // computed
// For signed URLs, you can pass in signing options.
// See the options at: https://docs.adonisjs.com/guides/drive#getsignedurl
await post.coverImage.getUrls({ expiresIn: '30mins' })
// or
await post.coverImage.getUrls({
contentType: 'application/json',
contentDisposition: 'attachment',
})
// After
post.coverImage.url // computed as a signed URL
post.coverImage.breakpoints.thumbnail.url // computed as a signed URL
post.coverImage.breakpoints.small.url // computed as a signed URL
post.coverImage.breakpoints.medium.url // computed as a signed URL
post.coverImage.breakpoints.large.url // computed as a signed URL
posts.coverImage.urls // computed as an object containing all signed URLs
The Drive API methods for generating URLs are asynchronous, whereas serializing a model to JSON is synchronous. Therefore, it is not to create URLs at the time of serializing a model.
// ❌ Does not work
const users = await Post.all()
users.map((post) => {
post.coverImage.url = await post.coverImage.getUrls()
return post
})
To address this use case, you can opt for pre-computing URLs
We recommend not enabling the preComputeUrls
option when you need the URLs for just one or two queries and not within the rest of your application.
For those couple of queries, you can manually compute the URLs within the controller. Here's a small helper method that you can drop on the model directly.
class Post extends BaseModel {
public static async preComputeUrls(models: Post | Post[]) {
if (Array.isArray(models)) {
await Promise.all(models.map((model) => this.preComputeUrls(model)))
return
}
await models.avatar?.computeUrls()
await models.coverImage?.computeUrls()
}
}
And now use it as follows.
const posts = await Post.all()
await Post.preComputeUrls(posts)
return posts
Or for a single post
const post = await Post.findOrFail(1)
await Post.preComputeUrls(post)
return post
FAQs
Generate and persist optimised and responsive breakpoint images on the fly in your AdonisJS application.
The npm package adonis-responsive-attachment receives a total of 9 weekly downloads. As such, adonis-responsive-attachment popularity was classified as not popular.
We found that adonis-responsive-attachment demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 0 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
GitHub removed 27 malicious pull requests attempting to inject harmful code across multiple open source repositories, in another round of low-effort attacks.
Security News
RubyGems.org has added a new "maintainer" role that allows for publishing new versions of gems. This new permission type is aimed at improving security for gem owners and the service overall.
Security News
Node.js will be enforcing stricter semver-major PR policies a month before major releases to enhance stability and ensure reliable release candidates.