--- displayed_sidebar: core --- import TypeList from "@site/src/components/TypeList" # IFileService ## Overview A file service class is defined in a TypeScript or JavaScript file that’s created in the `src/services` directory. The class must extend the `AbstractFileService` class imported from the `@medusajs/medusa` package. Based on services’ naming conventions, the file’s name should be the slug version of the file service’s name without `service`, and the class’s name should be the pascal case of the file service’s name following by `Service`. For example, create the file `src/services/local-file.ts` with the following content: ```ts title="src/services/local-file.ts" import { AbstractFileService } from "@medusajs/medusa" import { DeleteFileType, FileServiceGetUploadStreamResult, FileServiceUploadResult, GetUploadedFileType, UploadStreamDescriptorType, } from "@medusajs/types" class LocalFileService extends AbstractFileService { async upload( fileData: Express.Multer.File ): Promise { throw new Error("Method not implemented.") } async uploadProtected( fileData: Express.Multer.File ): Promise { throw new Error("Method not implemented.") } async delete(fileData: DeleteFileType): Promise { throw new Error("Method not implemented.") } async getUploadStreamDescriptor( fileData: UploadStreamDescriptorType ): Promise { throw new Error("Method not implemented.") } async getDownloadStream( fileData: GetUploadedFileType ): Promise { throw new Error("Method not implemented.") } async getPresignedDownloadUrl( fileData: GetUploadedFileType ): Promise { throw new Error("Method not implemented.") } } export default LocalFileService ``` :::note[Multer Typing] The examples implement a file service supporting local uploads. If you’re using TypeScript and you're following along with the implementation, you should install the Multer types package in the root of your Medusa backend to resolve errors within your file service types: ```bash npm2yarn npm install @types/multer ``` ::: --- ## Properties `","description":"","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"__moduleDeclaration__","type":"`Record`","description":"","optional":true,"defaultValue":"","expandable":false,"children":[]}]} sectionTitle="IFileService"/> ___ ## Accessors ### activeManager\_ #### Returns ___ ## Methods ### upload This method is used to upload a file to the Medusa backend. #### Example ```ts class LocalFileService extends AbstractFileService { // ... async upload( fileData: Express.Multer.File ): Promise { const filePath = `${this.publicPath}/${fileData.originalname}` fs.copyFileSync(fileData.path, filePath) return { url: `${this.serverUrl}/${filePath}`, key: filePath, } } // ... } ``` :::tip This example does not account for duplicate names to maintain simplicity in this guide. So, an uploaded file can replace another existing file that has the same name. ::: #### Parameters #### Returns ### uploadProtected This method is used to upload a file to the Medusa backend, but to a protected storage. Typically, this would be used to store files that shouldn’t be accessible by using the file’s URL or should only be accessible by authenticated users. For example, exported or imported CSV files. #### Example ```ts class LocalFileService extends AbstractFileService { // ... async uploadProtected( fileData: Express.Multer.File ): Promise { const filePath = `${this.protectedPath}/${fileData.originalname}` fs.copyFileSync(fileData.path, filePath) return { url: `${this.serverUrl}/${filePath}`, key: filePath } } // ... } ``` :::tip This example does not account for duplicate names to maintain simplicity in this guide. So, an uploaded file can replace another existing file that has the same name. ::: #### Parameters #### Returns ### delete This method is used to delete a file from storage. #### Example ```ts class LocalFileService extends AbstractFileService { async delete( fileData: DeleteFileType ): Promise { fs.rmSync(fileData.fileKey) } // ... } ``` #### Parameters #### Returns ### getUploadStreamDescriptor This method is used to retrieve a write stream to be used to upload a file. #### Example ```ts // ... import { Stream } from "stream" class LocalFileService extends AbstractFileService { // ... async getUploadStreamDescriptor({ name, ext, isPrivate = true, }: UploadStreamDescriptorType ): Promise { const filePath = `${isPrivate ? this.publicPath : this.protectedPath }/${name}.${ext}` const pass = new Stream.PassThrough() const writeStream = fs.createWriteStream(filePath) pass.pipe(writeStream) return { writeStream: pass, promise: Promise.resolve(), url: `${this.serverUrl}/${filePath}`, fileKey: filePath, } } // ... } ``` #### Parameters #### Returns ### getDownloadStream This method is used to retrieve a read stream for a file, which can then be used to download the file. #### Example ```ts class LocalFileService extends AbstractFileService { async getDownloadStream({ fileKey, isPrivate = true, }: GetUploadedFileType ): Promise { const filePath = `${isPrivate ? this.publicPath : this.protectedPath }/${fileKey}` const readStream = fs.createReadStream(filePath) return readStream } // ... } ``` #### Parameters #### Returns ### getPresignedDownloadUrl This method is used to retrieve a download URL of the file. For some file services, such as S3, a presigned URL indicates a temporary URL to get access to a file. If your file service doesn’t perform or offer a similar functionality, you can just return the URL to download the file. #### Example ```ts class LocalFileService extends AbstractFileService { async getPresignedDownloadUrl({ fileKey, isPrivate = true, }: GetUploadedFileType ): Promise { // Local upload doesn't provide // support for presigned URLs, // so just return the file's URL. const filePath = `${isPrivate ? this.publicPath : this.protectedPath }/${fileKey}` return `${this.serverUrl}/${filePath}` } // ... } ``` #### Parameters #### Returns ### withTransaction #### Parameters #### Returns ### shouldRetryTransaction\_ #### Parameters ` \\| `object`","description":"","optional":false,"defaultValue":"","expandable":false,"children":[]}]} sectionTitle="shouldRetryTransaction_"/> #### Returns ### atomicPhase\_ Wraps some work within a transactional block. If the service already has a transaction manager attached this will be reused, otherwise a new transaction manager is created. #### Type Parameters #### Parameters Promise<TResult>","description":"the transactional work to be done","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"isolationOrErrorHandler","type":"`IsolationLevel` \\| (`error`: TError) => Promise<void \\| TResult>","description":"the isolation level to be used for the work.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"maybeErrorHandlerOrDontFail","type":"(`error`: TError) => Promise<void \\| TResult>","description":"Potential error handler","optional":true,"defaultValue":"","expandable":false,"children":[]}]} sectionTitle="atomicPhase_"/> #### Returns