Skip to main content
FileSystem provides cross-platform file system operations from @effect/platform.

Overview

import { FileSystem } from "@effect/platform"

interface FileSystem {
  readonly access: (path: string, options?: AccessFileOptions) => Effect<void, PlatformError>
  readonly copy: (fromPath: string, toPath: string, options?: CopyOptions) => Effect<void, PlatformError>
  readonly exists: (path: string) => Effect<boolean, PlatformError>
  readonly makeDirectory: (path: string, options?: MakeDirectoryOptions) => Effect<void, PlatformError>
  readonly readDirectory: (path: string, options?: ReadDirectoryOptions) => Effect<Array<string>, PlatformError>
  readonly readFile: (path: string) => Effect<Uint8Array, PlatformError>
  readonly readFileString: (path: string, encoding?: string) => Effect<string, PlatformError>
  readonly remove: (path: string, options?: RemoveOptions) => Effect<void, PlatformError>
  readonly rename: (oldPath: string, newPath: string) => Effect<void, PlatformError>
  readonly stat: (path: string) => Effect<File.Info, PlatformError>
  readonly writeFile: (path: string, data: Uint8Array, options?: WriteFileOptions) => Effect<void, PlatformError>
  readonly writeFileString: (path: string, data: string, options?: WriteFileStringOptions) => Effect<void, PlatformError>
  // ... and more
}

Reading Files

readFile

Read file as bytes.
const readFile: (path: string) => Effect<Uint8Array, PlatformError>
import { FileSystem } from "@effect/platform"
import { Effect } from "effect"

const program = Effect.gen(function* () {
  const fs = yield* FileSystem.FileSystem
  const data = yield* fs.readFile("./data.bin")
  return data
})

readFileString

Read file as string.
const readFileString: (
  path: string,
  encoding?: string
) => Effect<string, PlatformError>
import { FileSystem } from "@effect/platform"
import { Effect } from "effect"

const program = Effect.gen(function* () {
  const fs = yield* FileSystem.FileSystem
  const content = yield* fs.readFileString("./config.json", "utf-8")
  return JSON.parse(content)
})

stream

Read file as a stream.
const stream: (
  path: string,
  options?: StreamOptions
) => Stream<Uint8Array, PlatformError>
import { FileSystem } from "@effect/platform"
import { Effect, Stream } from "effect"

const program = Effect.gen(function* () {
  const fs = yield* FileSystem.FileSystem
  const stream = fs.stream("./large-file.txt", {
    chunkSize: FileSystem.Size(64 * 1024) // 64 KB chunks
  })

  yield* Stream.runForEach(stream, (chunk) =>
    Effect.sync(() => console.log(`Read ${chunk.length} bytes`))
  )
})

Writing Files

writeFile

Write bytes to file.
const writeFile: (
  path: string,
  data: Uint8Array,
  options?: WriteFileOptions
) => Effect<void, PlatformError>
import { FileSystem } from "@effect/platform"
import { Effect } from "effect"

const program = Effect.gen(function* () {
  const fs = yield* FileSystem.FileSystem
  const data = new TextEncoder().encode("Hello, World!")
  yield* fs.writeFile("./hello.txt", data)
})

writeFileString

Write string to file.
const writeFileString: (
  path: string,
  data: string,
  options?: WriteFileStringOptions
) => Effect<string, PlatformError>
import { FileSystem } from "@effect/platform"
import { Effect } from "effect"

const program = Effect.gen(function* () {
  const fs = yield* FileSystem.FileSystem
  yield* fs.writeFileString(
    "./config.json",
    JSON.stringify({ name: "app", version: "1.0" }, null, 2)
  )
})

sink

Create a writable sink for a file.
const sink: (
  path: string,
  options?: SinkOptions
) => Sink<void, Uint8Array, never, PlatformError>
import { FileSystem } from "@effect/platform"
import { Effect, Stream } from "effect"

const program = Effect.gen(function* () {
  const fs = yield* FileSystem.FileSystem

  const dataStream = Stream.make(
    new TextEncoder().encode("Line 1\n"),
    new TextEncoder().encode("Line 2\n")
  )

  yield* Stream.run(dataStream, fs.sink("./output.txt"))
})

Directories

makeDirectory

Create a directory.
const makeDirectory: (
  path: string,
  options?: MakeDirectoryOptions
) => Effect<void, PlatformError>

interface MakeDirectoryOptions {
  readonly recursive?: boolean
  readonly mode?: number
}
import { FileSystem } from "@effect/platform"
import { Effect } from "effect"

const program = Effect.gen(function* () {
  const fs = yield* FileSystem.FileSystem
  yield* fs.makeDirectory("./data/nested/dir", { recursive: true })
})

readDirectory

List directory contents.
const readDirectory: (
  path: string,
  options?: ReadDirectoryOptions
) => Effect<Array<string>, PlatformError>

interface ReadDirectoryOptions {
  readonly recursive?: boolean
}
import { FileSystem } from "@effect/platform"
import { Effect } from "effect"

const program = Effect.gen(function* () {
  const fs = yield* FileSystem.FileSystem

  const files = yield* fs.readDirectory("./src")
  console.log(files)

  // Recursive listing
  const allFiles = yield* fs.readDirectory("./src", { recursive: true })
  console.log(allFiles)
})

remove

Remove files or directories.
const remove: (
  path: string,
  options?: RemoveOptions
) => Effect<void, PlatformError>

interface RemoveOptions {
  readonly recursive?: boolean
  readonly force?: boolean
}
import { FileSystem } from "@effect/platform"
import { Effect } from "effect"

const program = Effect.gen(function* () {
  const fs = yield* FileSystem.FileSystem

  // Remove file
  yield* fs.remove("./temp.txt")

  // Remove directory recursively
  yield* fs.remove("./temp-dir", { recursive: true })

  // Ignore errors if path doesn't exist
  yield* fs.remove("./maybe-exists.txt", { force: true })
})

Temporary Files

makeTempDirectory

const makeTempDirectory: (
  options?: MakeTempDirectoryOptions
) => Effect<string, PlatformError>

interface MakeTempDirectoryOptions {
  readonly directory?: string
  readonly prefix?: string
}
import { FileSystem } from "@effect/platform"
import { Effect } from "effect"

const program = Effect.gen(function* () {
  const fs = yield* FileSystem.FileSystem

  const tempDir = yield* fs.makeTempDirectory({ prefix: "myapp-" })
  console.log(`Created temp dir: ${tempDir}`)

  // Use the temp directory...
})

makeTempDirectoryScoped

Auto-cleanup on scope exit.
const makeTempDirectoryScoped: (
  options?: MakeTempDirectoryOptions
) => Effect<string, PlatformError, Scope>
import { FileSystem } from "@effect/platform"
import { Effect } from "effect"

const program = Effect.scoped(
  Effect.gen(function* () {
    const fs = yield* FileSystem.FileSystem

    const tempDir = yield* fs.makeTempDirectoryScoped()

    // Directory automatically deleted when scope closes
    yield* fs.writeFileString(`${tempDir}/data.txt`, "temp data")
  })
)

makeTempFile

const makeTempFile: (
  options?: MakeTempFileOptions
) => Effect<string, PlatformError>

interface MakeTempFileOptions {
  readonly directory?: string
  readonly prefix?: string
  readonly suffix?: string
}

makeTempFileScoped

const makeTempFileScoped: (
  options?: MakeTempFileOptions
) => Effect<string, PlatformError, Scope>

File Information

stat

Get file metadata.
const stat: (path: string) => Effect<File.Info, PlatformError>

interface File.Info {
  readonly type: "File" | "Directory" | "SymbolicLink"
  readonly mtime: Option<Date>
  readonly atime: Option<Date>
  readonly birthtime: Option<Date>
  readonly size: bigint
}
import { FileSystem } from "@effect/platform"
import { Effect } from "effect"

const program = Effect.gen(function* () {
  const fs = yield* FileSystem.FileSystem

  const info = yield* fs.stat("./data.txt")

  console.log(`Type: ${info.type}`)
  console.log(`Size: ${info.size} bytes`)
})

exists

Check if path exists.
const exists: (path: string) => Effect<boolean, PlatformError>
import { FileSystem } from "@effect/platform"
import { Effect } from "effect"

const program = Effect.gen(function* () {
  const fs = yield* FileSystem.FileSystem

  const fileExists = yield* fs.exists("./config.json")

  if (fileExists) {
    const content = yield* fs.readFileString("./config.json")
  }
})

File Operations

copy

Copy files or directories.
const copy: (
  fromPath: string,
  toPath: string,
  options?: CopyOptions
) => Effect<void, PlatformError>

interface CopyOptions {
  readonly overwrite?: boolean
  readonly preserveTimestamps?: boolean
}

rename

Rename/move files.
const rename: (
  oldPath: string,
  newPath: string
) => Effect<void, PlatformError>
Create hard links and symbolic links.
const link: (fromPath: string, toPath: string) => Effect<void, PlatformError>
const symlink: (fromPath: string, toPath: string) => Effect<void, PlatformError>
const readLink: (path: string) => Effect<string, PlatformError>

Size Helpers

import { FileSystem } from "@effect/platform"

const size = FileSystem.Size(1024) // 1024 bytes

const kb = FileSystem.KiB(64) // 64 KiB
const mb = FileSystem.MiB(10) // 10 MiB
const gb = FileSystem.GiB(2) // 2 GiB
const tb = FileSystem.TiB(1) // 1 TiB
const pb = FileSystem.PiB(1) // 1 PiB

File Watching

watch

Watch for file system changes.
const watch: (
  path: string,
  options?: WatchOptions
) => Stream<WatchEvent, PlatformError>

interface WatchOptions {
  readonly recursive?: boolean
}
import { FileSystem } from "@effect/platform"
import { Effect, Stream } from "effect"

const program = Effect.gen(function* () {
  const fs = yield* FileSystem.FileSystem

  const watcher = fs.watch("./src", { recursive: true })

  yield* Stream.runForEach(watcher, (event) =>
    Effect.sync(() => console.log(`File changed: ${event}`)
  ))
})

Build docs developers (and LLMs) love