450 lines
22 KiB
Plaintext
450 lines
22 KiB
Plaintext
---
|
||
slug: /references/file-provider-module
|
||
keywords:
|
||
- file
|
||
- storage
|
||
- provider
|
||
- integration
|
||
---
|
||
|
||
import { TypeList } from "docs-ui"
|
||
|
||
# How to Create a File Module Provider
|
||
|
||
In this document, you’ll learn how to create a File Module Provider and the methods you must implement in its main service.
|
||
|
||
---
|
||
|
||
## Implementation Example
|
||
|
||
As you implement your File Module Provider, it can be useful to refer to an existing provider and how it's implemeted.
|
||
|
||
If you need to refer to an existing implementation as an example, check the [S3 File Module Provider in the Medusa repository](https://github.com/medusajs/medusa/tree/develop/packages/modules/providers/file-s3).
|
||
|
||
---
|
||
|
||
## Create Module Provider Directory
|
||
|
||
Start by creating a new directory for your module provider.
|
||
|
||
If you're creating the module provider in a Medusa application, create it under the `src/modules` directory. For example, `src/modules/my-file`.
|
||
|
||
If you're creating the module provider in a plugin, create it under the `src/providers` directory. For example, `src/providers/my-file`.
|
||
|
||
<Note>
|
||
|
||
The rest of this guide always uses the `src/modules/my-file` directory as an example.
|
||
|
||
</Note>
|
||
|
||
---
|
||
|
||
## 2. Create the File Module Provider's Service
|
||
|
||
Create the file `src/modules/my-file/service.ts` that holds the implementation of the module provider's main service. It must extend the `AbstractFileProviderService` class imported from `@medusajs/framework/utils`:
|
||
|
||
```ts title="src/modules/my-file/service.ts"
|
||
import { AbstractFileProviderService } from "@medusajs/framework/utils"
|
||
|
||
class MyFileProviderService extends AbstractFileProviderService {
|
||
// TODO implement methods
|
||
}
|
||
|
||
export default MyFileProviderService
|
||
```
|
||
|
||
### constructor
|
||
|
||
The constructor allows you to access resources from the module's container using the first parameter,
|
||
and the module's options using the second parameter.
|
||
|
||
If you're creating a client or establishing a connection with a third-party service, do it in the constructor.
|
||
|
||
#### Example
|
||
|
||
```ts
|
||
import { Logger } from "@medusajs/framework/types"
|
||
import { AbstractFileProviderService } from "@medusajs/framework/utils"
|
||
|
||
type InjectedDependencies = {
|
||
logger: Logger
|
||
}
|
||
|
||
type Options = {
|
||
apiKey: string
|
||
}
|
||
|
||
class MyFileProviderService extends AbstractFileProviderService {
|
||
protected logger_: Logger
|
||
protected options_: Options
|
||
static identifier = "my-file"
|
||
// assuming you're initializing a client
|
||
protected client
|
||
|
||
constructor (
|
||
{ logger }: InjectedDependencies,
|
||
options: Options
|
||
) {
|
||
super()
|
||
|
||
this.logger_ = logger
|
||
this.options_ = options
|
||
|
||
// assuming you're initializing a client
|
||
this.client = new Client(options)
|
||
}
|
||
}
|
||
|
||
export default MyFileProviderService
|
||
```
|
||
|
||
### identifier
|
||
|
||
Each file provider has a unique ID used to identify it. The provider's ID
|
||
will be stored as `fs_{identifier}_{id}`, where `{id}` is the provider's `id`
|
||
property in the `medusa-config.ts`.
|
||
|
||
#### Example
|
||
|
||
```ts
|
||
class MyFileProviderService extends AbstractFileProviderService {
|
||
static identifier = "my-file"
|
||
// ...
|
||
}
|
||
```
|
||
|
||
### delete
|
||
|
||
This method deletes one or more files from the storage. It's used when an admin user
|
||
deletes a product image, or other custom file deletions.
|
||
|
||
#### Example
|
||
|
||
```ts
|
||
class MyFileProviderService extends AbstractFileProviderService {
|
||
// ...
|
||
async delete(
|
||
files: FileTypes.ProviderDeleteFileDTO | FileTypes.ProviderDeleteFileDTO[]
|
||
): Promise<void> {
|
||
// TODO logic to remove the file from storage
|
||
// Use the `file.fileKey` to delete the file, which is the identifier of the file
|
||
// in the provider's storage.
|
||
// for example:
|
||
const fileArray = Array.isArray(files) ? files : [files]
|
||
for (const file of fileArray) {
|
||
this.client.delete(file.fileKey)
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
#### Parameters
|
||
|
||
<TypeList types={[{"name":"files","type":"[ProviderDeleteFileDTO](../../../types/FileTypes/interfaces/types.FileTypes.ProviderDeleteFileDTO/page.mdx) \\| [ProviderDeleteFileDTO](../../../types/FileTypes/interfaces/types.FileTypes.ProviderDeleteFileDTO/page.mdx)[]","description":"The details of the files to delete.","optional":false,"defaultValue":"","expandable":false,"children":[{"name":"fileKey","type":"`string`","description":"The file's key. When uploading a file, the\nreturned key is used here.","optional":false,"defaultValue":"","expandable":false,"children":[]}]}]} expandUrl="https://docs.medusajs.com/learn/fundamentals/data-models/manage-relationships#retrieve-records-of-relation" sectionTitle="delete"/>
|
||
|
||
#### Returns
|
||
|
||
<TypeList types={[{"name":"Promise","type":"Promise<void>","optional":false,"defaultValue":"","description":"Resolves when the files are deleted.","expandable":false,"children":[]}]} expandUrl="https://docs.medusajs.com/learn/fundamentals/data-models/manage-relationships#retrieve-records-of-relation" sectionTitle="delete"/>
|
||
|
||
### getAsBuffer
|
||
|
||
This method retrieves an uploaded file as a buffer. This is useful when you want to
|
||
process the entire file in memory or send it as a response.
|
||
|
||
:::note
|
||
|
||
This is available starting from [Medusa v2.8.0](https://github.com/medusajs/medusa/releases/tag/v2.8.0).
|
||
|
||
:::
|
||
|
||
#### Example
|
||
|
||
```ts
|
||
class MyFileProviderService extends AbstractFileProviderService {
|
||
// ...
|
||
async getAsBuffer(file: ProviderDeleteFileDTO): Promise<Buffer> {
|
||
// TODO logic to get the file as a buffer
|
||
// Use the `file.fileKey` to get the file, which is the identifier of the file
|
||
// in the provider's storage.
|
||
// for example:
|
||
this.client.getAsBuffer(file.fileKey)
|
||
}
|
||
}
|
||
```
|
||
|
||
#### Parameters
|
||
|
||
<TypeList types={[{"name":"fileData","type":"[ProviderGetFileDTO](../../../types/FileTypes/interfaces/types.FileTypes.ProviderGetFileDTO/page.mdx)","description":"The details of the file to get its buffer.","optional":false,"defaultValue":"","expandable":false,"children":[{"name":"fileKey","type":"`string`","description":"The file's key allowing you to later\nidentify the file in the third-party\nsystem. For example, the S3 Module Provider\nreturns the file's key in S3, whereas the\nLocal File Module Provider returns the file's\npath.","optional":false,"defaultValue":"","expandable":false,"children":[]}]}]} expandUrl="https://docs.medusajs.com/learn/fundamentals/data-models/manage-relationships#retrieve-records-of-relation" sectionTitle="getAsBuffer"/>
|
||
|
||
#### Returns
|
||
|
||
<TypeList types={[{"name":"Promise","type":"Promise<Buffer>","optional":false,"defaultValue":"","description":"The file's buffer.","expandable":false,"children":[],"since":"2.8.0"}]} expandUrl="https://docs.medusajs.com/learn/fundamentals/data-models/manage-relationships#retrieve-records-of-relation" sectionTitle="getAsBuffer"/>
|
||
|
||
### getDownloadStream
|
||
|
||
This method retrieves an uploaded file as a stream. This is useful when streaming
|
||
a file to clients or you want to process the file in chunks.
|
||
|
||
:::note
|
||
|
||
This is available starting from [Medusa v2.8.0](https://github.com/medusajs/medusa/releases/tag/v2.8.0).
|
||
|
||
:::
|
||
|
||
#### Example
|
||
|
||
```ts
|
||
class MyFileProviderService extends AbstractFileProviderService {
|
||
// ...
|
||
async getAsStream(file: ProviderDeleteFileDTO): Promise<Readable> {
|
||
// TODO logic to get the file as a stream
|
||
// Use the `file.fileKey` to get the file, which is the identifier of the file
|
||
// in the provider's storage.
|
||
// for example:
|
||
this.client.getAsStream(file.fileKey)
|
||
}
|
||
}
|
||
```
|
||
|
||
#### Parameters
|
||
|
||
<TypeList types={[{"name":"fileData","type":"[ProviderGetFileDTO](../../../types/FileTypes/interfaces/types.FileTypes.ProviderGetFileDTO/page.mdx)","description":"The details of the file to get its stream.","optional":false,"defaultValue":"","expandable":false,"children":[{"name":"fileKey","type":"`string`","description":"The file's key allowing you to later\nidentify the file in the third-party\nsystem. For example, the S3 Module Provider\nreturns the file's key in S3, whereas the\nLocal File Module Provider returns the file's\npath.","optional":false,"defaultValue":"","expandable":false,"children":[]}]}]} expandUrl="https://docs.medusajs.com/learn/fundamentals/data-models/manage-relationships#retrieve-records-of-relation" sectionTitle="getDownloadStream"/>
|
||
|
||
#### Returns
|
||
|
||
<TypeList types={[{"name":"Promise","type":"Promise<Readable>","optional":false,"defaultValue":"","description":"The file's stream.","expandable":false,"children":[],"since":"2.8.0"}]} expandUrl="https://docs.medusajs.com/learn/fundamentals/data-models/manage-relationships#retrieve-records-of-relation" sectionTitle="getDownloadStream"/>
|
||
|
||
### getPresignedDownloadUrl
|
||
|
||
This method is used to retrieve a download URL of the file. For some providers,
|
||
such as S3, a presigned URL indicates a temporary URL to get access to a file.
|
||
|
||
If your provider doesn’t perform or offer a similar functionality, you can
|
||
return the URL to download the file.
|
||
|
||
#### Example
|
||
|
||
```ts
|
||
class MyFileProviderService extends AbstractFileProviderService {
|
||
// ...
|
||
async getPresignedDownloadUrl(
|
||
fileData: ProviderGetFileDTO
|
||
): Promise<string> {
|
||
// TODO logic to get the presigned URL
|
||
// Use the `file.fileKey` to delete the file, which is the identifier of the file
|
||
// in the provider's storage.
|
||
// for example:
|
||
return this.client.getPresignedUrl(fileData.fileKey)
|
||
}
|
||
}
|
||
```
|
||
|
||
#### Parameters
|
||
|
||
<TypeList types={[{"name":"fileData","type":"[ProviderGetFileDTO](../../../types/FileTypes/interfaces/types.FileTypes.ProviderGetFileDTO/page.mdx)","description":"The details of the file to get its\npresigned URL.","optional":false,"defaultValue":"","expandable":false,"children":[{"name":"fileKey","type":"`string`","description":"The file's key allowing you to later\nidentify the file in the third-party\nsystem. For example, the S3 Module Provider\nreturns the file's key in S3, whereas the\nLocal File Module Provider returns the file's\npath.","optional":false,"defaultValue":"","expandable":false,"children":[]}]}]} expandUrl="https://docs.medusajs.com/learn/fundamentals/data-models/manage-relationships#retrieve-records-of-relation" sectionTitle="getPresignedDownloadUrl"/>
|
||
|
||
#### Returns
|
||
|
||
<TypeList types={[{"name":"Promise","type":"Promise<string>","optional":false,"defaultValue":"","description":"The file's presigned URL.","expandable":false,"children":[{"name":"string","type":"`string`","optional":false,"defaultValue":"","description":"","expandable":false,"children":[]}]}]} expandUrl="https://docs.medusajs.com/learn/fundamentals/data-models/manage-relationships#retrieve-records-of-relation" sectionTitle="getPresignedDownloadUrl"/>
|
||
|
||
### getPresignedUploadUrl
|
||
|
||
<BadgesList badges={[
|
||
{
|
||
"variant": "neutral",
|
||
"children": "optional"
|
||
}
|
||
]} className="mb-1" />
|
||
|
||
This method is used to get a presigned upload URL for a file. For some providers,
|
||
such as S3, a presigned URL indicates a temporary URL to get upload a file.
|
||
|
||
If your provider doesn’t perform or offer a similar functionality, you don't have to
|
||
implement this method. Instead, an error is thrown when the method is called.
|
||
|
||
#### Example
|
||
|
||
```ts
|
||
class MyFileProviderService extends AbstractFileProviderService {
|
||
// ...
|
||
async getPresignedUploadUrl(
|
||
fileData: ProviderGetPresignedUploadUrlDTO
|
||
): Promise<ProviderFileResultDTO> {
|
||
// TODO logic to get the presigned upload URL
|
||
// for example:
|
||
return this.client.getPresignedUploadUrl(fileData.filename, fileData.mimeType)
|
||
}
|
||
}
|
||
```
|
||
|
||
#### Parameters
|
||
|
||
<TypeList types={[{"name":"fileData","type":"[ProviderGetPresignedUploadUrlDTO](../../../types/FileTypes/interfaces/types.FileTypes.ProviderGetPresignedUploadUrlDTO/page.mdx)","description":"The details of the file to get a presigned upload URL for.","optional":false,"defaultValue":"","expandable":false,"children":[{"name":"filename","type":"`string`","description":"The filename of the file to get a presigned upload URL for.","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"mimeType","type":"`string`","description":"The mimetype of the file to get a presigned upload URL for.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"access","type":"[FileAccessPermission](../../../types/FileTypes/types/types.FileTypes.FileAccessPermission/page.mdx)","description":"The access level of the file to get a presigned upload URL for.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"expiresIn","type":"`number`","description":"The validity of the presigned upload URL in seconds.","optional":true,"defaultValue":"","expandable":false,"children":[]}]}]} expandUrl="https://docs.medusajs.com/learn/fundamentals/data-models/manage-relationships#retrieve-records-of-relation" sectionTitle="getPresignedUploadUrl"/>
|
||
|
||
#### Returns
|
||
|
||
<TypeList types={[{"name":"Promise","type":"Promise<[ProviderFileResultDTO](../../../types/FileTypes/interfaces/types.FileTypes.ProviderFileResultDTO/page.mdx)>","optional":false,"defaultValue":"","description":"The presigned URL and file key to upload the file to","expandable":false,"children":[{"name":"url","type":"`string`","description":"The file's URL that users or systems\ncan use to access the file.","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"key","type":"`string`","description":"The file's key allowing you to later\nidentify the file in the third-party\nsystem. For example, the S3 Module Provider\nreturns the file's key in S3, whereas the\nLocal File Module Provider returns the file's\npath.","optional":false,"defaultValue":"","expandable":false,"children":[]}]}]} expandUrl="https://docs.medusajs.com/learn/fundamentals/data-models/manage-relationships#retrieve-records-of-relation" sectionTitle="getPresignedUploadUrl"/>
|
||
|
||
### getUploadStream
|
||
|
||
This method returns a writeable stream to upload a file.
|
||
|
||
:::note
|
||
|
||
This is available starting from [Medusa v2.8.0](https://github.com/medusajs/medusa/releases/tag/v2.8.0).
|
||
|
||
:::
|
||
|
||
#### Example
|
||
|
||
```ts
|
||
class MyFileProviderService extends AbstractFileProviderService {
|
||
// ...
|
||
async getUploadStream(fileData: FileTypes.ProviderUploadStreamDTO): Promise<{
|
||
writeStream: Writable
|
||
promise: Promise<FileTypes.ProviderFileResultDTO>
|
||
url: string
|
||
fileKey: string
|
||
}> {
|
||
// TODO logic to get the writeable stream
|
||
}
|
||
}
|
||
```
|
||
|
||
#### Parameters
|
||
|
||
<TypeList types={[{"name":"fileData","type":"[ProviderUploadStreamDTO](../../../types/FileTypes/interfaces/types.FileTypes.ProviderUploadStreamDTO/page.mdx)","description":"The details of the file to upload.","optional":false,"defaultValue":"","expandable":false,"children":[{"name":"filename","type":"`string`","description":"The filename of the uploaded file","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"mimeType","type":"`string`","description":"The mimetype of the uploaded file","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"access","type":"[FileAccessPermission](../../../types/FileTypes/types/types.FileTypes.FileAccessPermission/page.mdx)","description":"The access level of the file. Defaults to private if not passed","optional":true,"defaultValue":"","expandable":false,"children":[]}]}]} expandUrl="https://docs.medusajs.com/learn/fundamentals/data-models/manage-relationships#retrieve-records-of-relation" sectionTitle="getUploadStream"/>
|
||
|
||
#### Returns
|
||
|
||
<TypeList types={[{"name":"Promise","type":"Promise<object>","optional":false,"defaultValue":"","description":"The writeable stream and upload promise.","expandable":false,"children":[{"name":"writeStream","type":"`Writable`","description":"","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"promise","type":"Promise<[ProviderFileResultDTO](../../../types/FileTypes/interfaces/types.FileTypes.ProviderFileResultDTO/page.mdx)>","description":"","optional":false,"defaultValue":"","expandable":false,"children":[{"name":"url","type":"`string`","description":"The file's URL that users or systems\ncan use to access the file.","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"key","type":"`string`","description":"The file's key allowing you to later\nidentify the file in the third-party\nsystem. For example, the S3 Module Provider\nreturns the file's key in S3, whereas the\nLocal File Module Provider returns the file's\npath.","optional":false,"defaultValue":"","expandable":false,"children":[]}]},{"name":"url","type":"`string`","description":"","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"fileKey","type":"`string`","description":"","optional":false,"defaultValue":"","expandable":false,"children":[]}],"since":"2.8.0"}]} expandUrl="https://docs.medusajs.com/learn/fundamentals/data-models/manage-relationships#retrieve-records-of-relation" sectionTitle="getUploadStream"/>
|
||
|
||
### upload
|
||
|
||
This method uploads a file using your provider's custom logic. In this method, you can upload the file
|
||
into your provider's storage, and return the uploaded file's details.
|
||
|
||
This method will be used when uploading product images, CSV files for imports, or other
|
||
custom file uploads.
|
||
|
||
#### Example
|
||
|
||
```ts
|
||
class MyFileProviderService extends AbstractFileProviderService {
|
||
// ...
|
||
async upload(
|
||
file: ProviderUploadFileDTO
|
||
): Promise<ProviderFileResultDTO> {
|
||
// TODO upload file to third-party provider
|
||
// or using custom logic
|
||
// for example:
|
||
this.client.upload(file)
|
||
|
||
return {
|
||
url: "some-url.com",
|
||
key: "file-name-or-id"
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
#### Parameters
|
||
|
||
<TypeList types={[{"name":"file","type":"[ProviderUploadFileDTO](../../../types/FileTypes/interfaces/types.FileTypes.ProviderUploadFileDTO/page.mdx)","description":"The file to upload.","optional":false,"defaultValue":"","expandable":false,"children":[{"name":"filename","type":"`string`","description":"The filename of the uploaded file","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"mimeType","type":"`string`","description":"The mimetype of the uploaded file","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"content","type":"`string`","description":"The file content as a base64-encoded string","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"access","type":"[FileAccessPermission](../../../types/FileTypes/types/types.FileTypes.FileAccessPermission/page.mdx)","description":"The access level of the file. Defaults to private if not passed","optional":true,"defaultValue":"","expandable":false,"children":[]}]}]} expandUrl="https://docs.medusajs.com/learn/fundamentals/data-models/manage-relationships#retrieve-records-of-relation" sectionTitle="upload"/>
|
||
|
||
#### Returns
|
||
|
||
<TypeList types={[{"name":"Promise","type":"Promise<[ProviderFileResultDTO](../../../types/FileTypes/interfaces/types.FileTypes.ProviderFileResultDTO/page.mdx)>","optional":false,"defaultValue":"","description":"The uploaded file's details.","expandable":false,"children":[{"name":"url","type":"`string`","description":"The file's URL that users or systems\ncan use to access the file.","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"key","type":"`string`","description":"The file's key allowing you to later\nidentify the file in the third-party\nsystem. For example, the S3 Module Provider\nreturns the file's key in S3, whereas the\nLocal File Module Provider returns the file's\npath.","optional":false,"defaultValue":"","expandable":false,"children":[]}]}]} expandUrl="https://docs.medusajs.com/learn/fundamentals/data-models/manage-relationships#retrieve-records-of-relation" sectionTitle="upload"/>
|
||
|
||
### validateOptions
|
||
|
||
This method validates the options of the provider set in `medusa-config.ts`.
|
||
Implementing this method is optional. It's useful if your provider requires custom validation.
|
||
|
||
If the options aren't valid, throw an error.
|
||
|
||
#### Example
|
||
|
||
```ts
|
||
class MyFileProviderService extends AbstractFileProviderService {
|
||
static validateOptions(options: Record<any, any>) {
|
||
if (!options.apiKey) {
|
||
throw new MedusaError(
|
||
MedusaError.Types.INVALID_DATA,
|
||
"API key is required in the provider's options."
|
||
)
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
#### Parameters
|
||
|
||
<TypeList types={[{"name":"options","type":"`Record<any, any>`","description":"The provider's options.","optional":false,"defaultValue":"","expandable":false,"children":[]}]} expandUrl="https://docs.medusajs.com/learn/fundamentals/data-models/manage-relationships#retrieve-records-of-relation" sectionTitle="validateOptions"/>
|
||
|
||
---
|
||
|
||
## 3. Create Module Provider Definition File
|
||
|
||
Create the file `src/modules/my-file/index.ts` with the following content:
|
||
|
||
```ts title="src/modules/my-file/index.ts"
|
||
import MyFileProviderService from "./service"
|
||
import {
|
||
ModuleProvider,
|
||
Modules
|
||
} from "@medusajs/framework/utils"
|
||
|
||
export default ModuleProvider(Modules.FILE, {
|
||
services: [MyFileProviderService],
|
||
})
|
||
```
|
||
|
||
This exports the module provider's definition, indicating that the `MyFileProviderService` is the module provider's service.
|
||
|
||
---
|
||
|
||
## 4. Use Module Provider
|
||
|
||
To use your File Module Provider, add it to the `providers` array of the File Module in `medusa-config.ts`:
|
||
|
||
<Note>
|
||
|
||
The File Module accepts one provider only.
|
||
|
||
</Note>
|
||
|
||
```ts title="medusa-config.ts"
|
||
module.exports = defineConfig({
|
||
// ...
|
||
modules: [
|
||
{
|
||
resolve: "@medusajs/medusa/file",
|
||
options: {
|
||
providers: [
|
||
// default provider
|
||
{
|
||
resolve: "@medusajs/medusa/file-local",
|
||
id: "local",
|
||
},
|
||
{
|
||
// if module provider is in a plugin, use `plugin-name/providers/my-file`
|
||
resolve: "./src/modules/my-file",
|
||
id: "my-file",
|
||
options: {
|
||
// provider options...
|
||
},
|
||
},
|
||
],
|
||
},
|
||
},
|
||
]
|
||
})
|
||
```
|
||
|
||
---
|
||
|
||
## 5. Test it Out
|
||
|
||
To test out your File Module Provider, use the Medusa Admin or the [Upload API route](https://docs.medusajs.com/v2/api/admin#uploads_postuploads) to upload a file.
|