Framework
tus/tus-node-server avatar
tus/tus-node-server

tus-node-server: resumable uploads for Node with pluggable stores

Node.js tus server, standalone or integrable in any framework, with disk, S3, Azure, and GGC stores.

1,104 stars228 forksTypeScriptMIT

At a glance

What is it?
tus-node-server is the official Node implementation of the tus resumable upload protocol, a set of packages that lets an upload be paused or interrupted and then continued without sending the earlier bytes again. The server package has no dependencies, so it can be dropped into an existing framework, and the storage is a separate package with implementations for disk, S3, Google Cloud Storage and Azure.
Who is it for?
Adopt tus-node-server if your users upload large files over connections that break, because resumable uploads are the only answer to a mobile client that loses signal halfway through, and if you want the storage layer to be a package you swap rather than a rewrite.
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 4 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

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

Editorial analysis

The protocol, and why resume matters

tus is a protocol based on HTTP for resumable file uploads. Resumable means that an upload can be interrupted at any moment and can be resumed without re-uploading the previous data again.

The README is careful about the two kinds of interruption, and the distinction matters when you are designing the client side of this. An interruption may happen willingly, if the user wants to pause, or by accident in case of a network issue or a server outage. A resumable protocol has to handle both, and the accidental case is the one that motivates the whole design: a user on a train uploading a two gigabyte file should not start again when the tunnel changes.

tus-node-server is an official implementation of the tus resumable upload protocol. The protocol specifies a flexible method to upload files to remote servers using HTTP, and the special feature is the ability to pause and resume uploads at any moment, allowing a seamless continuation after, for example, network interruptions.

The server accepts uploads of arbitrary sizes and stores them locally on disk, on Google Cloud Storage or on AWS S3, or on any other S3-compatible storage system. And because of the modularization and extensibility, support for nearly any other cloud provider could easily be added.

That last sentence is the architectural statement, and it is why the packages are split the way they are.

Four packages and a dependency-free core

The package list is the design.

@tus/server is the tus server itself, usable standalone or integrated into your existing Node.js server. @tus/file-store stores files on disk. @tus/s3-store stores files on AWS S3. @tus/gcs-store stores files on Google Cloud Storage. And @tus/azure-store stores files on Azure.

The property that makes this arrangement useful is stated in the quick start: @tus/server has no dependencies, so it can be integrated in any server-side framework. A resumable upload protocol needs to be reachable at a path, it needs to understand the tus headers, and it needs to talk to a store. Everything else a framework brings is unnecessary, and a dependency-free server means you can put it behind Fastify, Express, Koa or a meta-framework without an argument about who owns the request body parser.

The stores are separate for the same reason. Storage is where the cloud provider differences live, and a user on Google Cloud Storage should not have to take an S3 dependency to run a server.

All packages are fully typed with TypeScript, and all packages require Node.js 20.19.0 or later.

The standalone server in eleven lines

The quick start is a standalone server which stores files on disk, and it is short enough to read at a glance.

js
import { Server } from "@tus/server";
import { FileStore } from "@tus/file-store";

const host = "127.0.0.1";
const port = 1080;
const server = new Server({
  path: "/files",
  datastore: new FileStore({ directory: "./files" }),
});

server.listen({ host, port });

The two configuration points are the path, which is where the tus endpoints live, and the datastore, which is the storage implementation. The host and port are passed separately to listen, so the server can bind to a different interface from the one the application serves on, which matters when the application is behind a proxy.

The README also points at a StackBlitz project so you can try the whole thing in the browser without installing anything, which is a good way to see the protocol in action before you wire it into your own application.

Integrating into an existing server

The second example integrates the tus server into an existing Node.js server, using Fastify, and the interesting part is the three lines in the middle.

js
const app = fastify({ logger: true });
const tusServer = new Server({
  path: "/files",
  datastore: new FileStore({ directory: "./files" }),
});

app.addContentTypeParser(
  "application/offset+octet-stream",
  (request, payload, done) => done(null)
);
app.all("/files", (req, res) => {
  tusServer.handle(req.raw, res.raw);
});

The content type parser is the piece that trips people up. A tus upload sends its body as application/offset+octet-stream, and a framework that has a parser for every content type will try to buffer and decode that stream, which defeats the point of a streaming upload. Registering a parser that hands the raw payload straight through, discarding it, lets the tus server own the body.

The routing then delegates to the server with handle on the raw request and the raw response, which is the whole integration surface. The example registers both /files and /files/* because the protocol addresses individual uploads by path, so you need a wildcard route as well as the collection route.

The server object is created separately from the framework, and the framework is told about it rather than the other way round. That is the same property that makes @tus/server dependency free, and it is why the same code works in a meta-framework.

The extension table, and where the gaps are

The tus protocol supports optional extensions, and the README publishes a table of which store implements which. It is the most practically useful table in the document, because it tells you that the feature set is not uniform across the stores.

Creation and Creation With Upload are supported by all four stores, which is what you would expect, since a tus server cannot function without them.

Expiration and Termination are supported only by the file store and the S3 store. The GCS store and the Azure store are marked as not supporting them, which matters if you were planning a lifecycle policy that removes abandoned uploads or a way to delete a stored object through the tus API.

Checksum and Concatenation are not supported by any of the stores. Concatenation is the extension that stitches several uploads together into one file, so a client that relies on it has to find another approach.

The table is worth reading before you commit to a store, rather than after, because the differences are structural. A disk-backed server can expire an upload and terminate it, because it owns the filesystem. An object store can do it if the SDK offers the operation, and apparently for two of them it does not.

For a fuller picture of what the protocol offers, the extension links in the README point at the specification sections for each one.

Meta-frameworks, runtimes and the other implementations

The 2.0.0 release is the one the README tells you to read about, and the announcement post is titled integrating in all meta-frameworks and JavaScript runtimes.

The point is that tus Node makes sense if you want to host a Node.js server or integrate it into your existing one, and you can also run it in all meta-frameworks such as Next.js, Nuxt, React Router and SvelteKit, and in other Node.js compatible runtime environments such as AWS Lambda, Cloudflare, Bun and Deno Deploy.

The reason this became possible is the no-dependency property of the server package. A framework that already parses request bodies, owns a router and runs on a serverless runtime can host a tus endpoint without a second HTTP stack, and the content type parser is the one integration step that each framework needs.

The README is also candid that this is not the only implementation, listing other mature servers: tusd, tusdotnet, rustus, and many others at the tus implementations page.

The comparison that matters is deployment shape. A separate server process is a different operational model from a library inside your application. The Node implementation is for the case where the upload endpoint has to live in the same process, or the same function, as the code that owns the file afterwards.

The repository, the release process and the current state

The repository is an npm workspace with packages/* and a test workspace, built with tsc, linted and formatted with Biome, and versioned with changesets.

The scripts in the root package.json describe the workflow. The build is tsc --build, the lint and format steps are Biome with a write flag, and there is a format:check variant that treats warnings as errors. The pretest script builds the types before the tests run, and the test script runs the package tests. For releases, version runs changeset version followed by npm install, and the release script triggers a GitHub workflow with gh workflow run release, with a local variant that builds and runs changeset publish.

The repository also has the pieces a modern JavaScript project tends to accumulate, with an.agents/ directory, AGENTS.md and CLAUDE.md for agent-driven work, a.changeset/ directory for the change fragments, a skills-lock.json, a.well-known/ directory and a keyfile.json.gpg file, which are the pieces a tus server needs for its key and for the client discovery path.

The release history shows the packages moving independently, which is the point of the split. @tus/server was tagged 2.4.4 on 2026-08-14 and 2.4.5 on 2026-08-31, and @tus/s3-store was at 2.0.7 on 2026-09-22. The last push was on 2026-10-11, and the repository is not archived.

The licence is MIT, and the project site is tus.io, which is where the protocol specification itself lives, and that is the right place to start if you are implementing a client rather than a server.

Editorial conclusion

Adopt tus-node-server if your users upload large files over connections that break, because resumable uploads are the only answer to a mobile client that loses signal halfway through, and if you want the storage layer to be a package you swap rather than a rewrite. Do not adopt it if your uploads are small and reliable, since a plain multipart POST is less to run and to reason about, and check the extension table before you promise features, because termination and expiration are not implemented by the cloud stores. Before you start, decide whether you want the standalone server or the integrated one, and read the 2.0.0 announcement, since that release is the one that made the server package free of dependencies and usable in any meta-framework.

Frequently asked questions

What does the tus protocol give me?

It is an HTTP based protocol for resumable file uploads. An upload can be interrupted at any moment, whether willingly because the user paused or accidentally because of a network issue or a server outage, and it can be resumed without re-uploading the previous data.

How do I run a standalone tus server in Node?

Create a Server from @tus/server with a path and a datastore, in this case a FileStore pointed at a directory, then call listen with the host and port. The full example is: import { Server } from "@tus/server"; import { FileStore } from "@tus/file-store"; and construct the server with path "/files".

Which tus extensions does each storage store implement?

Creation and Creation With Upload are supported by the file, S3, GCS and Azure stores. Expiration and Termination are supported by the file and S3 stores only, and Checksum and Concatenation are supported by none of them.

Can I use tus-node-server inside a Next.js or Lambda function?

Yes. The 2.0.0 announcement is about integrating in all meta-frameworks and JavaScript runtimes, including Next.js, Nuxt, React Router, SvelteKit, AWS Lambda, Cloudflare, Bun and Deno Deploy. The reason is that @tus/server has no dependencies, and the only integration step a framework needs is a content type parser for application/offset+octet-stream that passes the raw body through.

What are the requirements for tus-node-server?

All packages require Node.js 20.19.0 or later, and all packages are fully typed with TypeScript. The project is MIT licensed, the packages are versioned independently with changesets, and the last push was on 2026-10-11.

Official sources

  1. License: MIT
  2. Project website
  3. README
  4. Releases
  5. tus/tus-node-server 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/tus-tus-node-server.svg)](https://hysenlabs.com/projects/tus-tus-node-server)

Was this useful?