The media plugin for Tensei. How to handle file uploads
When building production-ready applications, efficiently handling file uploads is critical. Tensei provides a plugin to abstract this functionality for you.
This plugin provides a file upload REST API endpoint and GraphQL query, and a way to associate file uploads to any application resources.
To install this plugin, run the following command:
yarn add @tensei/media
# Or using npm
npm install --save @tensei/media
Once installed, you may register this plugin with your tensei application using the .plugins()
method on the Tensei instance:
import { tensei } from '@tensei/core'
import { media } from '@tensei/media'
export default tensei()
.plugins([
media()
.plugin()
])
Once you register the media plugin, you may need to associate existing resources to your application to uploaded files. To do this, the media plugin exports a file()
and files()
field. These fields create a hasOne()
and hasMany()
relationship between the resource and the media plugin resource respectively.
Since Mikro ORM does not support polymorphic relationships at the moment, the media plugin uses a Single-Table Inheritance approach to associating resources with file uploads.
Let's take an example social networking application. We want users to have multiple profile pictures, and a single avatar:
import { media, files, file } from '@tensei/media'
import { tensei, resource, text } from '@tensei/core'
export default tensei()
.resources([
resource('User')
.fields([
text('Full Name'),
file('Avatar'),
files('Profile Pictures'),
])
])
.plugins([
media()
.plugin()
])
You may upload files to the REST or GraphQL APIs. Here's an example on how to upload files using Axios to the Rest API:
import Axios from 'axios'
async function upload(event) {
import { files } = event.target
const form = new FormData()
files.forEach(file => form.append('files', file))
Axios.post('http://localhost:8810/files', form, {
headers: {
'Content-Type': 'multipart/form-data'
}
}).then((uploaded_files) => {
// console.log(uploaded_files)
})
}
The data returned from the API would contain File
instances, with all information about the uploaded files. Here's sample data from a file upload:
{
"data": [
{
"id": 6,
"size": 24765,
"original_filename": "avatar.jpeg",
"extension": "jpeg",
"mime_type": "image/jpeg",
"hash": "c8b0019021cfbc19b44e0cffb56296115ca0b387dca19de9b64db1b420d933e2bb632af1",
"path": "/",
"disk": "local",
"transformations": []
},
{
"id": 7,
"size": 16685,
"original_filename": "4X4.JPG",
"extension": "JPG",
"mime_type": "image/jpeg",
"hash": "724cca2d3a8a1d040ccd92a05b16b7bcacd497dd00231467e5984bf8aca15b6a78cc7f63",
"path": "/",
"disk": "local",
"transformations": []
}
]
}
After uploading files, you may now associate model instances with the ID of the returned file instances. For example, you may make a PATCH request to update a user's profile with the uploaded files:
PATCH /api/users/1?populate=avatar,profile_pictures HTTP/1.1
Host: localhost:5000
Content-Type: application/json
{
"avatar": 5, // ID of uploaded file
"profile_pictures": [6, 7] // ID of uploaded files
}