npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@reventlessdev/rescript-fast-csv

v1.2.0-alpha.12

Published

ReScript bindings for fast-csv

Readme

npm License: Apache-2.0 Docs

@reventlessdev/rescript-fast-csv

⚠️ Alpha. APIs and on-disk formats can change without notice between releases. Pin exact versions and expect breaking changes.

ReScript bindings for fast-csv, a fast and flexible CSV parsing library for Node.js.

Install

pnpm add @reventlessdev/rescript-fast-csv

Add it to your rescript.json dependencies:

{
  "dependencies": ["@reventlessdev/rescript-fast-csv"]
}

Core API

Parsing Functions

parseFile

Parse a CSV file from the filesystem.

parseFile(~path: string, ~options: Options.t=?) => csvParserStream

Example:

FastCSV.parseFile(
  ~path="data.csv",
  ~options={
    headers: Bool(true),
    delimiter: ","
  }
)

parseStream

Parse a CSV from a Node.js readable stream.

parseStream(~stream: NodeStreams.Readable.t, ~options: Options.t=?) => csvParserStream

Parser Options

Configure the CSV parser behavior using the Options.t type:

| Option | Type | Default | Description | |--------|------|---------|-------------| | objectMode | bool | true | Parse rows as objects with named fields | | delimiter | string | "," | Character used to separate columns | | quote | Js.Nullable.t<string> | '"' | Character used to quote fields | | escape | string | '"' | Character used to escape quotes | | headers | Bool(bool) | String(array<string>) | false | Use first row as headers or provide custom headers | | renameHeaders | bool | false | Allow headers to be renamed | | ignoreEmpty | bool | false | Skip empty rows | | comment | string | null | Character that starts comment lines (skipped) | | discardUnmappedfolumns | bool | false | Discard columns not in headers | | strictColumnHandling | bool | false | Throw error if row column count differs from headers | | trim | bool | false | Trim whitespace from both ends of fields | | rtrim | bool | false | Trim whitespace from right end of fields | | ltrim | bool | false | Trim whitespace from left end of fields |

Example:

{
  Options.headers: Bool(true),
  delimiter: ";",
  trim: true,
  ignoreEmpty: true,
  comment: "#"
}

Event Handling Pattern

The parser uses a chainable event handler pattern. Register handlers to process rows, handle errors, and track completion:

onData

Called for each valid row parsed from the CSV.

onData(csvParserStream, row => unit) => csvParserStream

Example:

parseFile(~path="users.csv")
->onData(row => {
  Js.log2("Parsed row:", row)
})

onError

Called when the parser encounters an error.

onError(csvParserStream, Js.Exn.t => unit) => csvParserStream

Example:

parseFile(~path="data.csv")
->onError(err => {
  switch Js.Exn.message(err) {
  | Some(msg) => Js.log2("Parse error:", msg)
  | None => Js.log("Unknown parse error")
  }
})

onEnd

Called when parsing completes. Receives the total row count.

onEnd(csvParserStream, rowCount => unit) => csvParserStream

Example:

parseFile(~path="data.csv")
->onEnd(count => {
  Js.log(`Finished parsing ${count->Int.toString} rows`)
})

onInvalid

Called when a row fails validation. Only triggered if validation is registered.

onInvalid(csvParserStream, (row, rowNumber, option<reason>) => unit) => csvParserStream

Example:

parseFile(~path="data.csv")
->validate((row, callback) => {
  // Validation logic
  callback->toInvalid("Missing required field")
})
->onInvalid((row, rowNum, reason) => {
  switch reason {
  | Some(msg) => Js.log2(`Row ${rowNum->Int.toString} invalid:`, msg)
  | None => Js.log2(`Row ${rowNum->Int.toString} has structural errors`, row)
  }
})

Complete Example: Processing User Data

// Parse a CSV file with user data, validate entries, transform them,
// and handle errors gracefully

type user = {
  name: string,
  email: string,
  age: int,
}

let users = ref([])

FastCSV.parseFile(
  ~path="users.csv",
  ~options={
    headers: Bool(true),
    delimiter: ",",
    trim: true,
    ignoreEmpty: true,
  }
)
->FastCSV.validate((row, callback) => {
  // Check if required fields exist
  switch (Dict.get(row, "name"), Dict.get(row, "email"), Dict.get(row, "age")) {
  | (Some(_), Some(_), Some(_)) => callback->FastCSV_Helpers.toValid
  | _ => callback->FastCSV_Helpers.toInvalid("Missing required fields (name, email, age)")
  }
})
->FastCSV.transform((row, callback) => {
  // Transform row data
  let name = Dict.get(row, "name")->Option.getOr("")
  let email = Dict.get(row, "email")->Option.getOr("")
  let ageStr = Dict.get(row, "age")->Option.getOr("0")

  // Add transformed fields back to row
  Dict.set(row, "email", String.toLowerCase(email))
  Dict.set(row, "name", String.trim(name))

  callback->FastCSV_Helpers.toValidTransformation(row)
})
->FastCSV.onData(row => {
  // Process each valid row
  switch (
    Dict.get(row, "name"),
    Dict.get(row, "email"),
    Dict.get(row, "age")->Option.flatMap(Int.fromString)
  ) {
  | (Some(name), Some(email), Some(age)) => {
      let user = {name, email, age}
      users := Array.concat(users.contents, [user])
    }
  | _ => Js.log2("Skipping invalid row:", row)
  }
})
->FastCSV.onInvalid((row, rowNumber, reason) => {
  let msg = reason->Option.getOr("Structural error")
  Js.log(`Row ${rowNumber->Int.toString}: ${msg}`)
})
->FastCSV.onError(err => {
  switch Js.Exn.message(err) {
  | Some(msg) => Js.log2("Parse error:", msg)
  | None => Js.log("Unknown parsing error occurred")
  }
})
->FastCSV.onEnd(rowCount => {
  Js.log(`Successfully parsed ${rowCount->Int.toString} rows`)
  Js.log2("Total valid users:", Array.length(users.contents))
})

Validation

Simple Validation

Use validate to register a validation function:

parseFile(~path="data.csv")
->validate((row, callback) => {
  let hasRequiredField = Dict.get(row, "id")->Option.isSome
  if hasRequiredField {
    callback->toValid
  } else {
    callback->toInvalid("Missing id field")
  }
})

Result-Based Validation

Use validateResult for cleaner validation with Result types:

parseFile(~path="data.csv")
->validateResult(row => {
  switch Dict.get(row, "email") {
  | Some(email) if String.includes(email, "@") => Ok()
  | Some(_) => Error("Invalid email format")
  | None => Error("Email field is missing")
  }
})

Multiple Validations

Use validateMultiple or validateMultipleResults to chain validations:

let validations = [
  row => Dict.get(row, "name")->Option.isSome ? Ok() : Error("Missing name"),
  row => Dict.get(row, "age")->Option.isSome ? Ok() : Error("Missing age"),
]

parseFile(~path="data.csv")
->validateMultipleResults(validations)

Transformation

Transform row data during parsing:

parseFile(~path="data.csv")
->transform((row, callback) => {
  // Normalize field names by removing empty keys
  Dict.forEach(row, (value, key) => {
    if String.length(key) == 0 {
      Dict.delete(row, key)
    }
  })

  // Convert certain fields to uppercase
  switch Dict.get(row, "code") {
  | Some(code) => Dict.set(row, "code", String.toUpperCase(code))
  | None => ()
  }

  callback->toValidTransformation(row)
})

Helper Functions

  • toValid(callback) - Mark validation as successful
  • toInvalid(callback, reason) - Mark validation as failed with reason
  • toError(callback, reason) - Mark validation as error
  • toValidTransformation(callback, row) - Complete transformation successfully
  • toErrorTransformation(callback, reason) - Mark transformation as failed

Links

License

Apache-2.0