Open-source project
zaeleus/noodles avatar
zaeleus/noodles

noodles: a Rust workspace for bioinformatics file formats

Bioinformatics I/O libraries in Rust

730 stars87 forksRustMIT

At a glance

What is it?
noodles is a Cargo workspace of per-format Rust crates for reading and writing BAM, CRAM, VCF, FASTA and related bioinformatics files, with a meta crate and feature flags for optional async I/O. The API is explicitly experimental and no GitHub releases exist, so adoption is a bet on a moving target.
Who is it for?
Adopt noodles if you are writing Rust, need specification-compliant readers and writers for these formats rather than a one-off command line conversion, and can absorb a breaking API while the project is still experimental. Do not adopt it if your pipeline needs a finished CLI tool, if you need a pinned release to audit, or if your toolchain cannot meet the edition 2024 and rust-version 1.91.0 requirements the workspace declares.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 8 days ago.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A Cargo workspace with one crate per file format

noodles is a Rust workspace whose Cargo.toml lists eighteen members, one per format or shared concern, plus a top-level meta crate. The members are noodles-bam, noodles-bcf, noodles-bed, noodles-bgzf, noodles-core, noodles-cram, noodles-csi, noodles-fasta, noodles-fastq, noodles-gff, noodles-gtf, noodles-htsget, noodles-refget, noodles-sam, noodles-tabix, noodles-util, noodles-vcf, and noodles itself.

That structure is the project's central design decision, and it is a good one for a library that touches this many adjacent specifications. BAM sits next to BGZF and CSI, CRAM sits next to both, and VCF and BCF sit on the same tabix and CSI index machinery. Pulling one format in does not drag the entire ecosystem with it, and the shared crates such as noodles-core and noodles-util exist so the format crates do not each reinvent the same primitives.

The workspace sets resolver = "3", edition 2024 and rust-version 1.91.0, and enables the missing_docs lint as a warning across the workspace. That last point is a small signal about how the project treats its public surface: every public item is expected to carry documentation, and the lint is enforced at the workspace level rather than crate by crate.

The format list names specification versions, not just extensions

The README states the goal plainly, providing specification-compliant implementations where applicable, and then lists what is currently supported: BAM 1.6, BCF 2.2, BED, BGZF, CRAM 3.0/3.1, CSI, FASTA, FASTQ, GFF3, GTF 2.2, htsget 1.3, refget 2.0, SAM 1.6, tabix, and VCF 4.3/4.4.

Naming the specification version is the detail that separates this from a file-extension wrapper. A BAM file at version 1.6 is not the same as one at 1.0, and CRAM 3.0 and 3.1 differ in ways that affect what a reader must tolerate. For a tool that has to interoperate with data written by other software, knowing which revision of the specification an implementation targets is a real answer, and here it is given.

The list also shows the two directions of the problem. Some entries are read and write formats such as BAM, FASTQ and VCF. Others are plumbing: BGZF is the block compression layer, CSI and tabix are index formats, and htsget and refget are protocols for fetching genomic data over HTTP rather than local files. A library that only handled the first kind would be less useful than one that handles both, and noodles attempts both.

A meta crate with feature flags, not one big dependency

noodles is published on crates.io, and the README is explicit that early versions can be used in projects while the API is still considered experimental. The dependency story is designed to keep that usable. Instead of depending on a single crate with everything compiled in, you add the top-level meta crate and enable the features you need:

sh
cargo add noodles --features bam

Cargo writes the dependency and the feature selection into your manifest, and the enabled format is then imported through its re-exported name:

rust
use noodles::bam;

The re-export pattern matters more than it looks. The code above does not point at a noodles-bam path, and if the crate split ever changes underneath you, the import line stays the same. The alternative, depending on noodles-bam directly, also works and gives a smaller dependency tree, but you then track the crate split yourself and lose the stable re-export surface.

One caveat applies to both routes. Because the API is flagged as experimental, a routine cargo update can change method names or module paths in a way that breaks compilation. Treat version bumps as code changes, not as routine maintenance.

Async I/O is opt-in per crate, and not every format offers it

The individual crates carry optional features of their own, and the one that matters most in practice is async, which enables asynchronous I/O with Tokio. The README lists exactly which formats provide it: BAM, BCF, BGZF, CRAM, CSI, FASTA, FASTQ, GFF, SAM, tabix, and VCF.

Read that list carefully, because it is not a list of every crate in the workspace. BED, GTF, htsget, refget and the utility crates are absent from the async feature list, so a builder that assumes every format is available in an async flavour will hit a missing feature. The workspace dependency list already shows why the split exists. Some crates depend on tokio and pin-project-lite, while others depend on futures without tokio, and htsget and refget lean on reqwest, url and serde for network access.

That is a sensible design. Async is a different I/O model, and retrofitting it into every reader would force the runtime choice on users who only need to parse a local file. The cost is that your application code will be shaped differently depending on which formats you use, and a codebase that mixes an async BAM reader with a synchronous BED reader ends up with two styles of code.

libdeflate changes the DEFLATE path for BGZF and CRAM

The second optional feature is libdeflate, which switches DEFLATE stream encoding and decoding to the libdeflate implementation. The README scopes it to BGZF and CRAM, the two crates that actually handle DEFLATE-compressed data, and the workspace lists libdeflater at version 1.19.0 as a dependency.

The default path is flate2, configured in the workspace with default-features = false and the zlib-rs feature. That detail is easy to miss and it is the one to know: the default is not a system zlib but the Rust implementation of zlib, and the libdeflate feature is an opt-in switch to a different backend for the same stream format.

Whether the change is worth enabling depends on your workload, and the README does not give benchmarks, so the honest answer is that you need to measure it on your own data. What is clear is the scope: it affects BGZF and CRAM, and it is a compile-time choice rather than a runtime setting. If you benchmark on one platform and deploy on another, verify the feature is enabled consistently in both, since a build that silently uses flate2 will produce different timings rather than different output.

Runnable examples are the fastest way to see a real BAM file

Each crate may have its own examples directory, and every example is runnable as an application. After cloning the repository, cargo run --release --example lists the available examples, and you then pass an example name as the option argument plus any program arguments. The README shows the pattern with two BAM examples:

bash
cargo run --release --example bam_write > sample.bam
cargo run --release --example bam_read_header sample.bam

The first command writes a BAM file and redirects the binary output into sample.bam. The second reads that file back and prints its header. Together they form a useful smoke test, because a file written by the library and read by the same library can agree while still being wrong against the rest of the world, and only the round trip through a real file shows that the writer and reader agree with the format.

Notice the release flag. These examples do real compression and index work, and building them in debug mode makes the timings meaningless while the binaries remain correct. Running the list command first is worth the time even when you know the format you want, because the example names follow the crate names and the list tells you which formats actually have examples today.

A library, not a tool, and the gap that leaves

noodles provides readers and writers. It does not ship a command line program that converts SAM to BAM, sorts records, or prints statistics. If that is what you need, this project is the wrong shape, and the honest description of it is a building block for tools rather than a tool itself.

That boundary also removes a lot of the cost that usually makes a bioinformatics format library painful. There is no command line surface to keep stable, no output format to design, and no question about whether a subcommand should be added. The surface a consumer sees is the Rust API, which the README already labels experimental.

The alternative approach worth naming is the opposite one: call existing command line tools from your program and parse their output. That approach avoids writing any format code and inherits each tool's format coverage, but it forces you through files or pipes, loses type information at the boundary, and ties the portability of your program to whichever binaries you can ship. The trade is a real dependency boundary against a real loss of control. The reason to look at noodles is that the format handling is the part you usually do not want to own.

Experimental API, no releases, and a fresh push

Maintenance signals need reading in pairs here. The repository is not archived and the last push was on 2026-10-15, so the code is being worked on. At the same time, the repository has no GitHub releases, so there is no tagged version you can pin, and the README asks users to keep in mind that the API is still considered experimental.

Together those two facts define the upgrade cost. There is no stable release cadence to schedule against, and the only versions on crates.io are early ones whose APIs may differ from what the current branch exposes. A dependency on noodles today is a dependency on a moving target, and the missing_docs lint plus the CI workflow referenced in the .github directory suggest the project cares about internal quality, but neither of those is a compatibility promise to your code.

Licensing is straightforward. The workspace package table declares license = "MIT" and the repository root holds a LICENSE.txt, so redistribution inside a closed-source product is a normal commercial use of an MIT grant. That is a statement about the licence text, not legal advice. What to confirm for yourself is which version of the crates your build actually resolved, since with no releases the dependency lockfile becomes the only record of what you shipped.

Editorial conclusion

Adopt noodles if you are writing Rust, need specification-compliant readers and writers for these formats rather than a one-off command line conversion, and can absorb a breaking API while the project is still experimental. Do not adopt it if your pipeline needs a finished CLI tool, if you need a pinned release to audit, or if your toolchain cannot meet the edition 2024 and rust-version 1.91.0 requirements the workspace declares. Verify three things first: add the exact format crate you need with cargo add noodles --features bam and confirm the API you plan to use matches the current docs, read the LICENSE.txt MIT terms before shipping binaries built on it, and check the CI workflow in .github before depending on a format for a production pipeline, because the newest tagged release does not exist and the last push was on 2026-10-15.

Frequently asked questions

How do I add noodles to a Rust project for a specific format?

Add the meta crate and enable the format feature, for example cargo add noodles --features bam, then import the format through its re-exported name with use noodles::bam. Individual crates such as noodles-bam can also be depended on directly if you want a smaller dependency tree.

Which file formats does noodles support?

The README lists BAM 1.6, BCF 2.2, BED, BGZF, CRAM 3.0/3.1, CSI, FASTA, FASTQ, GFF3, GTF 2.2, htsget 1.3, refget 2.0, SAM 1.6, tabix, and VCF 4.3/4.4. Each format has its own crate in the workspace.

Does noodles support async I/O?

The async feature enables asynchronous I/O with Tokio and is offered by the BAM, BCF, BGZF, CRAM, CSI, FASTA, FASTQ, GFF, SAM, tabix, and VCF crates. The README does not list async for BED, GTF, htsget, refget, or the utility crates.

Is the noodles API stable enough to depend on?

The README states that early versions can be used in projects but that the API is still considered experimental, and the repository has no GitHub releases. The last push was on 2026-10-15, so plan for API changes and pin exact versions in your lockfile.

What Rust toolchain does the noodles workspace require?

The workspace Cargo.toml sets edition 2024, rust-version 1.91.0 and resolver = "3", and it turns on the missing_docs lint as a warning across the workspace. The licence is MIT, recorded in the workspace package table and in a LICENSE.txt file at the repository root.

Official sources

  1. Issues
  2. License: MIT
  3. README
  4. zaeleus/noodles on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/zaeleus-noodles.svg)](https://hysenlabs.com/projects/zaeleus-noodles)

Was this useful?