### Implement Custom GET Handler Source: https://github.com/tus/tus-node-server/blob/main/packages/server/README.md Example of how to implement a custom GET handler to retrieve all files. Ensure the datastore directory is accessible. ```javascript import fs from "node:fs/promises"; import { Server } from "@tus/server"; import { FileStore } from "@tus/file-store"; const server = new Server({ path: "/files", datastore: new FileStore({ directory: "./files" }), }); server.get("/uploads", async (req) => { const files = await fs.readdir(server.datastore.directory); // Format and return }); ``` -------------------------------- ### Install @tus/azure-store Source: https://github.com/tus/tus-node-server/blob/main/packages/azure-store/README.md Install the @tus/azure-store package using npm. Requires Node.js >=20.19.0. ```bash npm install @tus/azure-store ``` -------------------------------- ### Install @tus/file-store Source: https://github.com/tus/tus-node-server/blob/main/packages/file-store/README.md Install the package using npm. Ensure you are using Node.js version 20.19.0 or higher. ```bash npm install @tus/file-store ``` -------------------------------- ### Initialize S3Store with ECS Credentials Source: https://github.com/tus/tus-node-server/blob/main/packages/s3-store/README.md Example of initializing the S3Store with AWS ECS credentials. This configuration is passed directly to the AWS SDK. ```javascript import aws from "aws-sdk"; import { Server } from "@tus/server"; import { S3Store } from "@tus/s3-store"; const s3Store = new S3Store({ partSize: 8 * 1024 * 1024, s3ClientConfig: { bucket: process.env.AWS_BUCKET, region: process.env.AWS_REGION, credentials: new aws.ECSCredentials({ httpOptions: { timeout: 5000 }, maxRetries: 10, }), }, }); const server = new Server({ path: "/files", datastore: s3Store }); // ... ``` -------------------------------- ### Using Custom ConfigStore with FileStore Source: https://github.com/tus/tus-node-server/blob/main/packages/file-store/README.md Instantiate FileStore with a custom configuration store, such as the MemoryConfigstore example. ```javascript import {MemoryConfigstore} from './MemoryConfigstore' const store = new FileStore({directory: './some/path', configstore: MemoryConfigstore}), ``` -------------------------------- ### Install @tus/server Source: https://github.com/tus/tus-node-server/blob/main/packages/server/README.md Install the @tus/server package using npm. Ensure you are using Node.js version 20.19.0 or higher. ```bash npm install @tus/server ``` -------------------------------- ### Server Lifecycle Methods Source: https://github.com/tus/tus-node-server/blob/main/packages/server/README.md Methods for starting and managing the tus server. ```APIDOC #### `server.listen()` Start the tus server. Supported arguments are the same as [`server.listen()`](https://nodejs.org/api/net.html#serverlisten) from `node:net`. #### `server.cleanUpExpiredUploads()` Clean up expired uploads. Your chosen datastore must support the [expiration][] extension for this to work. ``` -------------------------------- ### Install @tus/gcs-store Source: https://github.com/tus/tus-node-server/blob/main/packages/gcs-store/README.md Install the package using npm. Requires Node.js >=20.19.0. ```bash npm install @tus/gcs-store ``` -------------------------------- ### Install @tus/s3-store Source: https://github.com/tus/tus-node-server/blob/main/packages/s3-store/README.md Install the @tus/s3-store package using npm. Ensure you are using Node.js version 20.19.0 or higher. ```bash npm install @tus/s3-store ``` -------------------------------- ### Configure S3Store for Cloudflare R2 Source: https://github.com/tus/tus-node-server/blob/main/packages/s3-store/README.md Example of configuring S3Store for Cloudflare R2, which requires `partSize` and `minPartSize` to be identical to ensure all non-trailing parts are the same size. ```typescript // ... const s3Store = new S3Store({ partSize: 8 * 1024 * 1024, minPartSize: 8 * 1024 * 1024, // ... }); ``` -------------------------------- ### Custom GET Handlers Source: https://github.com/tus/tus-node-server/blob/main/packages/server/README.md Allows implementing custom GET handlers for specific routes. ```APIDOC #### `server.get(path, handler)` You can implement your own `GET` handlers. For instance, to return all files. ```js import fs from "node:fs/promises"; import { Server } from "@tus/server"; import { FileStore } from "@tus/file-store"; const server = new Server({ path: "/files", datastore: new FileStore({ directory: "./files" }), }); server.get("/uploads", async (req) => { const files = await fs.readdir(server.datastore.directory); // Format and return }); ``` ``` -------------------------------- ### Integrate tus-node-server with Koa Source: https://context7.com/tus/tus-node-server/llms.txt This example shows how to integrate tus-node-server into a Koa application, handling tus requests separately from the main Koa app. ```typescript import http from "node:http"; import url from "node:url"; import Koa from "koa"; import { Server } from "@tus/server"; import { FileStore } from "@tus/file-store"; const app = new Koa(); const appCallback = app.callback(); const tusServer = new Server({ path: "/files", datastore: new FileStore({ directory: "./uploads" }), }); const httpServer = http.createServer((req, res) => { const pathname = url.parse(req.url!).pathname!; if (/^/files(/.*)?$/.test(pathname)) { return tusServer.handle(req, res); } appCallback(req, res); }); httpServer.listen(1080); ``` -------------------------------- ### Implement Access Control with JWT Source: https://github.com/tus/tus-node-server/blob/main/packages/server/README.md Secure tus uploads by implementing access control using JSON Web Tokens. This example demonstrates verifying tokens and checking user roles. ```javascript import { Server } from "@tus/server"; // ... const server = new Server({ // .. async onIncomingRequest(req) { const token = req.headers.authorization; if (!token) { throw { status_code: 401, body: "Unauthorized" }; } try { const decodedToken = await jwt.verify(token, "your_secret_key"); req.user = decodedToken; } catch (error) { throw { status_code: 401, body: "Invalid token" }; } if (req.user.role !== "admin") { throw { status_code: 403, body: "Access denied" }; } }, }); ``` -------------------------------- ### Implement Custom Configstore for FileStore Metadata Source: https://context7.com/tus/tus-node-server/llms.txt Shows how to replace the default file-based metadata storage in FileStore with a custom in-memory implementation. This custom store must implement `get`, `set`, `delete`, and `list` methods. ```typescript import type { Upload } from "@tus/server"; import { Server } from "@tus/server"; import { FileStore } from "@tus/file-store"; // Example: in-memory metadata store (not suitable for production) class MemoryConfigstore { private data = new Map(); async get(key: string): Promise { return this.data.get(key); } async set(key: string, value: Upload): Promise { this.data.set(key, value); } async delete(key: string): Promise { this.data.delete(key); } async list(): Promise { return [...this.data.keys()]; } } const server = new Server({ path: "/files", datastore: new FileStore({ directory: "./uploads", configstore: new MemoryConfigstore(), }), }); server.listen({ port: 1080 }); ``` -------------------------------- ### FileStore - Custom configstore for metadata storage Source: https://context7.com/tus/tus-node-server/llms.txt Allows replacing the default file-based metadata store in `FileStore` with any object implementing `get`, `set`, `delete`, and `list` methods. ```APIDOC ### Custom `configstore` for `FileStore` — Pluggable metadata storage Replace the default file-based metadata store with any object implementing `get`, `set`, `delete`, and `list`. ```ts import type { Upload } from "@tus/server"; import { Server } from "@tus/server"; import { FileStore } from "@tus/file-store"; // Example: in-memory metadata store (not suitable for production) class MemoryConfigstore { private data = new Map(); async get(key: string): Promise { return this.data.get(key); } async set(key: string, value: Upload): Promise { this.data.set(key, value); } async delete(key: string): Promise { this.data.delete(key); } async list(): Promise { return [...this.data.keys()]; } } const server = new Server({ path: "/files", datastore: new FileStore({ directory: "./uploads", configstore: new MemoryConfigstore(), }), }); server.listen({ port: 1080 }); ``` ``` -------------------------------- ### Configure S3Store for Scaleway Object Storage Source: https://github.com/tus/tus-node-server/blob/main/packages/s3-store/README.md Example of configuring S3Store for Scaleway Object Storage, which has a limit of 1,000 parts per multipart upload. Set `maxMultipartParts` to 1000. ```typescript const s3Store = new S3Store({ maxMultipartParts: 1000, // ... }); ``` -------------------------------- ### S3 Lifecycle Policy for Incomplete Uploads Source: https://github.com/tus/tus-node-server/blob/main/packages/s3-store/README.md Configure an S3 Lifecycle policy to automatically abort and clean up incomplete multipart uploads. This example sets a 2-day expiration for uploads not marked as completed. ```json { "Rules": [ { "Filter": { "Tag": { "Key": "Tus-Completed", "Value": "false" } }, "Expiration": { "Days": 2 } } ] } ``` -------------------------------- ### server.get(path, handler) Source: https://context7.com/tus/tus-node-server/llms.txt Registers a custom handler for a specific GET path. This is useful for implementing features like serving a file list or custom download endpoints alongside the standard tus uploads. ```APIDOC ## server.get(path, handler) — Register a custom GET route Registers a handler for a specific `GET` path, useful for serving a file list or custom download endpoints alongside the tus endpoints. ### Example Usage ```ts import fs from "node:fs/promises"; import { Server } from "@tus/server"; import { FileStore } from "@tus/file-store"; const server = new Server({ path: "/files", datastore: new FileStore({ directory: "./uploads" }), }); server.get("/files", async (req) => { const files = await fs.readdir("./uploads"); return new Response(JSON.stringify({ files }), { headers: { "Content-Type": "application/json" }, }); }); server.listen({ port: 1080 }); ``` ``` -------------------------------- ### Integrate tus Server into Fastify Source: https://github.com/tus/tus-node-server/blob/main/README.md Integrates the tus server into an existing Fastify Node.js application. This example handles uploads and stores them using FileStore. It requires adding a custom content type parser for tus uploads. ```javascript import fastify from "fastify"; import { Server } from "@tus/server"; import { FileStore } from "@tus/file-store"; 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); }); app.all("/files/*", (req, res) => { tusServer.handle(req.raw, res.raw); }); app.listen(3000, (err) => { if (err) { app.log.error(err); process.exit(1); } }); ``` -------------------------------- ### Configure AzureStore for Azure Blob Storage Source: https://context7.com/tus/tus-node-server/llms.txt Appends upload chunks using the Azure Append Blob API. Supports Creation and Creation-With-Upload extensions. An optional in-memory cache can be swapped for a shared cache in multi-instance setups. ```typescript import { Server } from "@tus/server"; import { AzureStore } from "@tus/azure-store"; import { MemoryKvStore } from "@tus/server"; import type { MetadataValue } from "@tus/azure-store"; const server = new Server({ path: "/files", datastore: new AzureStore({ account: process.env.AZURE_ACCOUNT_ID!, accountKey: process.env.AZURE_ACCOUNT_KEY!, containerName: process.env.AZURE_CONTAINER_NAME!, // Optional: swap in-memory cache for a shared cache in multi-instance setups cache: new MemoryKvStore(), }), }); server.listen({ port: 1080 }); ``` -------------------------------- ### new Server(options) Source: https://context7.com/tus/tus-node-server/llms.txt Instantiates the tus server with specified options for path, datastore, and various lifecycle hooks and configurations. ```APIDOC ## new Server(options) — Create a tus server Instantiates the tus server. The two required options are `path` (the URL prefix the server owns) and `datastore` (a storage back-end). All lifecycle hooks, CORS settings, custom naming, and locking strategies are configured here. The server extends `EventEmitter`, so upload lifecycle events can be subscribed to with `.on()`. ### Options * **path** (string) - Required - The URL prefix the server owns. * **datastore** (Datastore) - Required - A storage back-end instance. * **maxSize** (number) - Optional - Caps the upload size. * **postReceiveInterval** (number) - Optional - Emits POST_RECEIVE events at a specified interval. * **allowedOrigins** (string[]) - Optional - Allows only specified origins. * **allowedCredentials** (boolean) - Optional - Allows credentials for CORS. * **onUploadCreate** (function) - Optional - Validates and modifies upload metadata before creation. * **onUploadFinish** (function) - Optional - Handles post-processing after an upload completes. * **onIncomingRequest** (function) - Optional - Middleware for access control and authentication. * **onResponseError** (function) - Optional - Maps errors to a tus-compatible shape. ### Events * **EVENTS.POST_CREATE**: Emitted when an upload is created. * **EVENTS.POST_RECEIVE**: Emitted periodically while an upload is in progress. * **EVENTS.POST_FINISH**: Emitted when an upload is successfully finished. * **EVENTS.POST_TERMINATE**: Emitted when an upload is terminated. ### Example ```ts import { Server, EVENTS } from "@tus/server"; import { FileStore } from "@tus/file-store"; const server = new Server({ path: "/files", datastore: new FileStore({ directory: "./uploads" }), maxSize: 1 * 1024 * 1024 * 1024, postReceiveInterval: 2000, allowedOrigins: ["https://example.com"], allowedCredentials: true, async onUploadCreate(req, upload) { if (!upload.metadata?.filename) { throw { status_code: 400, body: "filename metadata is required" }; } return { metadata: { ...upload.metadata, uploadedBy: "server" } }; }, async onUploadFinish(req, upload) { console.log("Upload complete:", upload.id, upload.size, "bytes"); return { status_code: 200, body: JSON.stringify({ id: upload.id }) }; }, async onIncomingRequest(req, uploadId) { const token = req.headers.get("authorization"); if (!token) throw { status_code: 401, body: "Unauthorized" }; }, async onResponseError(req, err) { console.error(err); }, }); server.on(EVENTS.POST_CREATE, (req, upload, url) => { console.log("Upload created:", upload.id, "URL:", url); }); server.on(EVENTS.POST_RECEIVE, (req, upload) => { const pct = upload.size ? Math.round((upload.offset / upload.size) * 100) : "?"; console.log(`Upload ${upload.id} progress: ${upload.offset} / ${upload.size} (${pct}%)"); }); server.on(EVENTS.POST_FINISH, (req, res, upload) => { console.log("Upload finished:", upload.id); }); server.on(EVENTS.POST_TERMINATE, (req, res, id) => { console.log("Upload terminated:", id); }); server.listen({ host: "127.0.0.1", port: 1080 }, () => { console.log("tus server listening on http://127.0.0.1:1080"); }); ``` ``` -------------------------------- ### AzureStore Constructor Source: https://github.com/tus/tus-node-server/blob/main/packages/azure-store/README.md Initializes a new AzureStore instance with the provided configuration options for connecting to Azure Blob Storage. ```APIDOC ## new AzureStore(options) Creates a new azure store with options. ### Parameters #### `options.account` Azure account ID (`string`). #### `options.accountKey` Azure account key (`string`). #### `options.containerName` Azure storage container name (`string`). #### `options.cache` Provide your own cache solution for the metadata of uploads ([`KvStore`][]) to reduce the calls to storage server. Default is ([`MemoryKvStore`][]) which stores the data in memory. ``` -------------------------------- ### Custom MemoryConfigstore Implementation Source: https://github.com/tus/tus-node-server/blob/main/packages/file-store/README.md Demonstrates creating a custom in-memory configuration store for upload metadata. This is for demonstration purposes only and not recommended for production. ```typescript import type { Upload } from "@tus/server"; export class MemoryConfigstore { data: Map = new Map(); get(key: string): Upload | undefined { return this.data.get(key); } set(key: string, value: Upload) { this.data.set(key, value); } delete(key: string) { return this.data.delete(key); } get list(): Record { return Object.fromEntries(this.data.entries()); } } ``` -------------------------------- ### Register Custom GET Route with `server.get` Source: https://context7.com/tus/tus-node-server/llms.txt Register a custom handler for a specific GET path. This is useful for serving file lists or custom download endpoints alongside tus uploads. ```typescript import fs from "node:fs/promises"; import { Server } from "@tus/server"; import { FileStore } from "@tus/file-store"; const server = new Server({ path: "/files", datastore: new FileStore({ directory: "./uploads" }), }); server.get("/files", async (req) => { const files = await fs.readdir("./uploads"); return new Response(JSON.stringify({ files }), { headers: { "Content-Type": "application/json" }, }); }); server.listen({ port: 1080 }); ``` -------------------------------- ### FileStore Constructor Source: https://github.com/tus/tus-node-server/blob/main/packages/file-store/README.md Creates a new FileStore instance with specified options. ```APIDOC ## new FileStore(options) ### Description Creates a new file store with options. ### Parameters #### `options.directory` - **directory** (string) - Required - The directory to store the files on disk. #### `options.configstore` - **configstore** (KvStore) - Optional - Provide your own storage solution for the metadata of uploads. Defaults to `FileKvStore`. #### `options.expirationPeriodInMilliseconds` - **expirationPeriodInMilliseconds** (number) - Optional - The time in milliseconds before an ongoing upload is considered expired. This is calculated from the time of creation. ``` -------------------------------- ### Create and Configure tus Server Source: https://context7.com/tus/tus-node-server/llms.txt Instantiates the tus server with required path and datastore options. Lifecycle hooks, CORS settings, and other configurations can be set here. Subscribe to upload lifecycle events using `.on()`. ```typescript import { Server, EVENTS } from "@tus/server"; import { FileStore } from "@tus/file-store"; const server = new Server({ path: "/files", datastore: new FileStore({ directory: "./uploads" }), // Optional: cap upload size to 1 GiB (can also be an async function) maxSize: 1 * 1024 * 1024 * 1024, // Optional: emit POST_RECEIVE every 2 s while a PATCH is in flight postReceiveInterval: 2000, // Optional: allow only these origins in Access-Control-Allow-Origin allowedOrigins: ["https://example.com"], allowedCredentials: true, // Optional: validate metadata before the upload record is created async onUploadCreate(req, upload) { if (!upload.metadata?.filename) { throw { status_code: 400, body: "filename metadata is required" }; } // Merge extra server-side metadata into the upload record return { metadata: { ...upload.metadata, uploadedBy: "server" } }; }, // Optional: post-processing after the last byte is received async onUploadFinish(req, upload) { console.log("Upload complete:", upload.id, upload.size, "bytes"); // Returning a non-204 status is non-standard but most clients accept it return { status_code: 200, body: JSON.stringify({ id: upload.id }) }; }, // Optional: access control / auth middleware async onIncomingRequest(req, uploadId) { const token = req.headers.get("authorization"); if (!token) throw { status_code: 401, body: "Unauthorized" }; // verify JWT, etc. }, // Optional: map any error to a tus-compatible error shape async onResponseError(req, err) { console.error(err); // Return undefined to let the default error response pass through }, }); // Subscribe to lifecycle events server.on(EVENTS.POST_CREATE, (req, upload, url) => { console.log("Upload created:", upload.id, "URL:", url); }); server.on(EVENTS.POST_RECEIVE, (req, upload) => { const pct = upload.size ? Math.round((upload.offset / upload.size) * 100) : "?"; console.log(`Upload ${upload.id} progress: ${upload.offset} / ${upload.size} (${pct}%)`); }); server.on(EVENTS.POST_FINISH, (req, res, upload) => { console.log("Upload finished:", upload.id); }); server.on(EVENTS.POST_TERMINATE, (req, res, id) => { console.log("Upload terminated:", id); }); // Start as a standalone HTTP server server.listen({ host: "127.0.0.1", port: 1080 }, () => { console.log("tus server listening on http://127.0.0.1:1080"); }); ``` -------------------------------- ### Initialize and Listen with tus-node-server Source: https://context7.com/tus/tus-node-server/llms.txt Sets up a tus server with a file store and defines an event listener for upload creation. Ensure the directory for uploads exists. ```typescript import { Server, EVENTS, Upload } from "@tus/server"; import { FileStore } from "@tus/file-store"; const server = new Server({ path: "/files", datastore: new FileStore({ directory: "./uploads" }), async onUploadCreate(req, upload: Upload) { console.log({ id: upload.id, size: upload.size, deferred: upload.sizeIsDeferred, filename: upload.metadata?.filename, createdAt: upload.creation_date, }); return {}; }, }); server.listen({ port: 1080 }); ``` -------------------------------- ### Server Configuration Options Source: https://github.com/tus/tus-node-server/blob/main/packages/server/README.md Configuration options for initializing the tus Node.js server. ```APIDOC #### `options.locker` The locker interface to manage locks for exclusive access control over resources ([`Locker`][] or `(req: Request) => Promise`). By default it uses an in-memory locker ([`MemoryLocker`][]) for safe concurrent access to uploads using a single server. When running multiple instances of the server, you need to provide a locker implementation that is shared between all instances (such as a `RedisLocker`). #### `options.disableTerminationForFinishedUploads` Disallow the [termination extension](https://tus.io/protocols/resumable-upload#termination) for finished uploads. (`boolean`) #### `options.onUploadCreate` `onUploadCreate` will be invoked before a new upload is created. (`(req, upload) => Promise<{ metadata?: Record}>`). - If the function returns the (modified) response the upload will be created. - You can optionally return `metadata` which will merge `upload.metadata`. - You can `throw` an Object and the HTTP request will be aborted with the provided `body` and `status_code` (or their fallbacks). This can be used to implement validation of upload metadata or add headers. #### `options.onUploadFinish` `onUploadFinish` will be invoked after an upload is completed but before a response is returned to the client (`(req, res, upload) => Promise<{ status_code?: number, headers?: Record, body?: string }>`). - You can optionally return `status_code`, `headers` and `body` to modify the response. Note that the tus specification does not allow sending response body nor status code other than 204, but most clients support it. Use at your own risk. - You can `throw` an Object and the HTTP request will be aborted with the provided `body` and `status_code` (or their fallbacks). This can be used to implement post-processing validation. #### `options.onIncomingRequest` `onIncomingRequest` is a middleware function invoked before all handlers (`(req: Request, uploadId: string) => Promise`) This can be used for things like access control. You can `throw` an Object and the HTTP request will be aborted with the provided `body` and `status_code` (or their fallbacks). #### `options.onResponseError` `onResponseError` will be invoked when an error response is about to be sent by the server. you use this function to map custom errors to tus errors or for custom observability. (`(req: Request, err: Error) => Promise<{status_code: number; body: string} | void>`) ``` -------------------------------- ### Integrate tus-node-server with Fastify Source: https://context7.com/tus/tus-node-server/llms.txt Fastify requires an explicit content-type parser for `application/offset+octet-stream` and raw Node.js request/response objects. This setup tells Fastify not to parse the binary body. ```typescript import fastify from "fastify"; import { Server } from "@tus/server"; import { FileStore } from "@tus/file-store"; const app = fastify({ logger: true }); const tusServer = new Server({ path: "/files", datastore: new FileStore({ directory: "./uploads" }), }); // Tell Fastify not to parse the binary body app.addContentTypeParser( "application/offset+octet-stream", (request, payload, done) => done(null) ); app.all("/files", (req, res) => tusServer.handle(req.raw, res.raw)); app.all("/files/*", (req, res) => tusServer.handle(req.raw, res.raw)); app.listen({ port: 3000 }, (err) => { if (err) { app.log.error(err); process.exit(1); } }); ``` -------------------------------- ### Next.js App Router API Route Source: https://github.com/tus/tus-node-server/blob/main/packages/server/README.md Configure a Next.js API route for tus uploads using the App router. This setup uses `handleWeb` for all HTTP methods. ```typescript import { Server } from "@tus/server"; import { FileStore } from "@tus/file-store"; const server = new Server({ // `path` needs to match the route declared by the next file router // ie /api/upload path: "/api/upload", datastore: new FileStore({ directory: "./files" }), }); export const GET = server.handleWeb; export const POST = server.handleWeb; export const PATCH = server.handleWeb; export const DELETE = server.handleWeb; export const OPTIONS = server.handleWeb; export const HEAD = server.handleWeb; ``` -------------------------------- ### Standalone tus Server with FileStore Source: https://github.com/tus/tus-node-server/blob/main/README.md Sets up a standalone tus server that stores uploaded files on the local disk. Ensure the './files' directory exists. ```javascript 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 }); ``` -------------------------------- ### Initialize S3Store with tus-node-server Source: https://github.com/tus/tus-node-server/blob/main/packages/s3-store/README.md Initialize the S3Store with custom part size and AWS S3 client configuration. This store is then used to configure the tus-node-server. ```javascript import { Server } from "@tus/server"; import { S3Store } from "@tus/s3-store"; const s3Store = new S3Store({ partSize: 8 * 1024 * 1024, // Each uploaded part will have ~8MiB, s3ClientConfig: { bucket: process.env.AWS_BUCKET, region: process.env.AWS_REGION, credentials: { accessKeyId: process.env.AWS_ACCESS_KEY_ID, secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY, }, }, }); const server = new Server({ path: "/files", datastore: s3Store }); // ... ``` -------------------------------- ### S3Store Constructor Source: https://github.com/tus/tus-node-server/blob/main/packages/s3-store/README.md Initializes a new S3Store instance with various configuration options for connecting to and managing uploads on AWS S3. ```APIDOC ## new S3Store(options) Creates a new AWS S3 store with options. ### Parameters #### `options.bucket` - **Type**: string - **Description**: The bucket name. #### `options.partSize` - **Type**: number - **Description**: The preferred part size for parts sent to S3. Can not be lower than 5MiB or more than 5GiB. The server calculates the optimal part size, which takes this size into account, but may increase it to not exceed the S3 10K parts limit. #### `options.minPartSize` - **Type**: number - **Description**: The minimal part size for parts. Can be used to ensure that all non-trailing parts are exactly the same size by setting `partSize` and `minPartSize` to the same value. Can not be lower than 5MiB or more than 5GiB. The server calculates the optimal part size, which takes this size into account, but may increase it to not exceed the `options.maxMultipartParts` parts limit. #### `options.maxMultipartParts` - **Type**: number - **Description**: The maximum number of parts allowed in a multipart upload. Defaults to 10,000. Some S3 providers have non-standard restrictions on the number of parts in a multipart upload. For example, AWS S3 has a limit of 10,000 parts, but some S3 compatible providers have a limit of 1,000 parts. #### `options.s3ClientConfig` - **Type**: object - **Description**: Options to pass to the AWS S3 SDK. Checkout the [`S3ClientConfig`](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-s3/interfaces/s3clientconfig.html) docs for the supported options. You need to at least set the `region`, `bucket` name, and your preferred method of authentication. #### `options.expirationPeriodInMilliseconds` - **Type**: number - **Description**: Enables the expiration extension and sets the expiration period of an upload url in milliseconds. Once the expiration period has passed, the upload url will return a 410 Gone status code. #### `options.useTags` - **Type**: boolean - **Description**: Some S3 providers don't support tagging objects. If you are using certain features like the expiration extension and your provider doesn't support tagging, you can set this option to `false` to disable tagging. #### `options.cache` - **Type**: object - **Description**: An optional cache implementation ([`KvStore`][]). Default uses an in-memory cache (`MemoryKvStore`). When running multiple instances of the server, you need to provide a cache implementation that is shared between all instances like the `RedisKvStore`. See the exported [KV stores][kvstores] from `@tus/server` for more information. ``` -------------------------------- ### Basic @tus/server Usage Source: https://github.com/tus/tus-node-server/blob/main/packages/server/README.md Set up a standalone tus server that stores files on disk. Configure the server path and the directory for storing uploads. ```javascript 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 }); ``` -------------------------------- ### Custom Nested Directory File Storage Source: https://github.com/tus/tus-node-server/blob/main/packages/server/README.md Configure tus to store files in custom nested directories using `namingFunction`, `generateUrl`, and `getFileIdFromRequest`. This example uses a user-specific folder and base64url encoding for URLs. ```javascript import crypto from "node:crypto"; import { Server } from "@tus/server"; import { FileStore } from "@tus/file-store"; const path = "/files"; const server = new Server({ path, datastore: new FileStore({ directory: "./test/output" }), namingFunction(req) { const id = crypto.randomBytes(16).toString("hex"); const folder = getFolderForUser(req); // your custom logic return `users/${folder}/${id}`; }, generateUrl(req, { proto, host, path, id }) { id = Buffer.from(id, "utf-8").toString("base64url"); return `${proto}://${host}${path}/${id}`; }, getFileIdFromRequest(req, lastPath) { // lastPath is everything after the last `/ // If your custom URL is different, this might be undefined // and you need to extract the ID yourself return Buffer.from(lastPath, "base64url").toString("utf-8"); }, }); ``` -------------------------------- ### Server Constructor Source: https://github.com/tus/tus-node-server/blob/main/packages/server/README.md Instantiates a new tus server with the provided options. The server handles file uploads according to the tus protocol. ```APIDOC ## `new Server(options)` Creates a new tus server with options. ### Parameters #### `options.path` The route to accept requests (`string`). #### `options.maxSize` Max file size (in bytes) allowed when uploading (`number` | `(req: Request, id: string | null) => Promise | number`). When providing a function during the OPTIONS request the id will be `null`. #### `options.allowedCredentials` Sets `Access-Control-Allow-Credentials` (`boolean`, default: `false`). #### `options.allowedOrigins` Trusted origins (`string[]`). Sends the client's origin back in `Access-Control-Allow-Origin` if it matches. #### `options.postReceiveInterval` Interval in milliseconds for sending progress of an upload over [`POST_RECEIVE`](#eventspost_receive) (`number`). #### `options.relativeLocation` Return a relative URL as the `Location` header to the client (`boolean`). #### `options.respectForwardedHeaders` Allow `Forwarded`, `X-Forwarded-Proto`, and `X-Forwarded-Host` headers to override the `Location` header returned by the server (`boolean`). #### `options.allowedHeaders` Additional headers sent in `Access-Control-Allow-Headers` (`string[]`). #### `options.exposedHeaders` Additional headers sent in `Access-Control-Expose-Headers` (`string[]`). #### `options.generateUrl` Control how the upload URL is generated (`(req, { proto, host, path, id }) => string)`. This only changes the upload URL (`Location` header). If you also want to change the file name in storage use `namingFunction`. Returning `prefix-1234` in `namingFunction` means the `id` argument in `generateUrl` is `prefix-1234`. `@tus/server` expects everything in the path after the last `/` to be the upload id. If you change that you have to use `getFileIdFromRequest` as well. A common use case of this function and `getFileIdFromRequest` is to base65 encode a complex id into the URL. Checkout the example how to [store files in custom nested directories](#example-store-files-in-custom-nested-directories). #### `options.getFileIdFromRequest` Control how the Upload-ID is extracted from the request (`(req: Request, lastPath?: string) => string | void`) By default, it expects everything in the path after the last `/` to be the upload id. `lastPath` is everything after the last `/`. Checkout the example how to [store files in custom nested directories](#example-store-files-in-custom-nested-directories). #### `options.namingFunction` Control how you want to name files (`(req: Request, metadata: Record) => string | Promise`) In `@tus/server`, the upload ID in the URL is the same as the file name. This means using a custom `namingFunction` will return a different `Location` header for uploading and result in a different file name in storage. It is important to make these unique to prevent data loss. Only use it if you need to. Default uses `crypto.randomBytes(16).toString('hex')`. Checkout the example how to [store files in custom nested directories](#example-store-files-in-custom-nested-directories). ``` -------------------------------- ### Upload Locking with `MemoryLocker` Source: https://context7.com/tus/tus-node-server/llms.txt Guarantees exclusive access to upload resources during concurrent PATCH requests. The default `MemoryLocker` is suitable for single-process deployments; use a shared locker for multi-instance setups. Custom timeouts can be configured. ```typescript import { Server, MemoryLocker } from "@tus/server"; import { FileStore } from "@tus/file-store"; const server = new Server({ path: "/files", datastore: new FileStore({ directory: "./uploads" }), // Custom timeout: give up waiting for a lock after 60 s instead of 30 s locker: new MemoryLocker({ acquireLockTimeout: 60_000 }), // Delay before forcibly cancelling an in-flight request that holds the lock lockDrainTimeout: 5000, }); server.listen({ port: 1080 }); ``` -------------------------------- ### GCSStore Constructor Source: https://github.com/tus/tus-node-server/blob/main/packages/gcs-store/README.md Initializes a new GCSStore instance. This store is used to manage tus uploads using Google Cloud Storage. ```APIDOC ## `new GCSStore(options)` Creates a new Google Cloud Storage store by passing a GCS bucket instance. ### Parameters #### `options.bucket` - **bucket** (object) - Required - The Google Cloud Storage bucket instance to use for storing uploads. ``` -------------------------------- ### Configure KV Stores for Metadata Caching with S3Store Source: https://context7.com/tus/tus-node-server/llms.txt Demonstrates configuring various KV stores (MemoryKvStore, FileKvStore, RedisKvStore, IoRedisKvStore) for caching upload metadata when using S3Store. Ensure Redis is running if using Redis-backed stores. ```typescript import { Server, MemoryKvStore, FileKvStore, RedisKvStore, IoRedisKvStore } from "@tus/server"; import { S3Store, type MetadataValue } from "@tus/s3-store"; import { createClient } from "@redis/client"; import Redis from "ioredis"; // In-memory cache (default for S3Store; lost on restart) const memCache = new MemoryKvStore(); // File-backed cache (survives restarts) const fileCache = new FileKvStore("./upload-cache"); // @redis/client-backed cache (for multi-instance deployments) const redisClient = await createClient({ url: "redis://localhost:6379" }).connect(); const redisCache = new RedisKvStore(redisClient, "tus:"); // ioredis-backed cache const ioRedisClient = new Redis({ host: "localhost", port: 6379 }); const ioRedisCache = new IoRedisKvStore(ioRedisClient, "tus:"); const server = new Server({ path: "/files", datastore: new S3Store({ partSize: 8 * 1024 * 1024, s3ClientConfig: { bucket: process.env.AWS_BUCKET!, region: process.env.AWS_REGION!, }, cache: redisCache, // swap to any of the above }), }); server.listen({ port: 1080 }); ``` -------------------------------- ### Basic FileStore Usage Source: https://github.com/tus/tus-node-server/blob/main/packages/file-store/README.md Integrate FileStore with the tus server by specifying the upload path and the directory for storing files. ```javascript import { Server } from "@tus/server"; import { FileStore } from "@tus/file-store"; const server = new Server({ path: "/files", datastore: new FileStore({ directory: "./some/path" }), }); // ... ``` -------------------------------- ### Configure S3Store for Cloudflare R2 with Fixed Part Size Source: https://context7.com/tus/tus-node-server/llms.txt For Cloudflare R2, ensure all non-trailing multipart parts are the same size by setting `partSize` and `minPartSize` to the same value. ```typescript import { Server } from "@tus/server"; import { S3Store } from "@tus/s3-store"; const r2Store = new S3Store({ partSize: 8 * 1024 * 1024, minPartSize: 8 * 1024 * 1024, // enforce uniform part size for R2 s3ClientConfig: { bucket: process.env.R2_BUCKET!, region: "auto", endpoint: `https://${process.env.CF_ACCOUNT_ID}.r2.cloudflarestorage.com`, credentials: { accessKeyId: process.env.R2_ACCESS_KEY_ID!, secretAccessKey: process.env.R2_SECRET_ACCESS_KEY!, }, }, }); const server = new Server({ path: "/files", datastore: r2Store }); server.listen({ port: 1080 }); ``` -------------------------------- ### Use AzureStore with tus-node-server Source: https://github.com/tus/tus-node-server/blob/main/packages/azure-store/README.md Integrate AzureStore into your tus-node-server instance by providing Azure credentials and container name. Ensure environment variables are set. ```javascript import { Server } from "@tus/server"; import { AzureStore } from "@tus/azure-store"; const server = new Server({ path: "/files", datastore: new AzureStore({ account: process.env.AZURE_ACCOUNT_ID, accountKey: process.env.AZURE_ACCOUNT_KEY, containerName: process.env.AZURE_CONTAINER_NAME, }), }); // ... ``` -------------------------------- ### Configure S3Store with IoRedisKvStore Cache Source: https://github.com/tus/tus-node-server/blob/main/packages/server/README.md Use IoRedisKvStore as a cache for S3Store, connecting to an ioredis client and specifying a key prefix. ```typescript import { IoRedisKvStore } from "@tus/server"; import S3Store, { type MetadataValue } from "@tus/s3-store"; import Redis from "ioredis"; const client = new Redis(); const prefix = "foo"; // prefix for the key (foo${id}) new S3Store({ // ... cache: new IoRedisKvStore(client, prefix), }); ``` -------------------------------- ### Configure S3Store with FileKvStore Cache Source: https://github.com/tus/tus-node-server/blob/main/packages/server/README.md Use FileKvStore as a cache for S3Store to persist upload info in a specified directory. ```typescript import { FileKvStore } from "@tus/server"; import S3Store, { type MetadataValue } from "@tus/s3-store"; const path = "./uploads"; new S3Store({ // ... cache: new FileKvStore(path), }); ``` -------------------------------- ### Use GCSStore with tus-node-server Source: https://github.com/tus/tus-node-server/blob/main/packages/gcs-store/README.md Integrate GCSStore into your tus-node-server instance. Ensure you have authenticated Google Cloud Storage access. ```javascript import { Server } from "@tus/server"; import { GCSStore } from "@tus/gcs-store"; import { Storage } from "@google-cloud/storage"; const storage = new Storage({ keyFilename: "key.json" }); const server = new Server({ path: "/files", datastore: new GCSStore({ bucket: storage.bucket("tus-node-server-ci"), }), }); // ... ``` -------------------------------- ### Configure S3Store with MemoryKvStore Cache Source: https://github.com/tus/tus-node-server/blob/main/packages/server/README.md Use MemoryKvStore as a cache for S3Store to persist upload info in memory. ```typescript import { MemoryKvStore } from "@tus/server"; import S3Store, { type MetadataValue } from "@tus/s3-store"; new S3Store({ // ... cache: new MemoryKvStore(), }); ``` -------------------------------- ### server.handleWeb(req) Source: https://context7.com/tus/tus-node-server/llms.txt Handles incoming Web API Requests and returns a Response. This is suitable for modern meta-frameworks like Next.js App Router, SvelteKit, Nuxt, Cloudflare Workers, and Bun. ```APIDOC ## server.handleWeb(req) — Web API / meta-framework handler Takes a standard Web API `Request` and returns a `Response`. Use this for Next.js App Router, SvelteKit, Nuxt, Cloudflare Workers, Bun, and similar environments. ### Example Usage (Next.js App Router) ```ts import { Server } from "@tus/server"; import { FileStore } from "@tus/file-store"; const server = new Server({ path: "/api/upload", datastore: new FileStore({ directory: "./uploads" }), }); export const GET = server.handleWeb.bind(server); export const POST = server.handleWeb.bind(server); export const PATCH = server.handleWeb.bind(server); export const DELETE = server.handleWeb.bind(server); export const OPTIONS = server.handleWeb.bind(server); export const HEAD = server.handleWeb.bind(server); ``` ``` -------------------------------- ### Configure FileStore for Local Disk Storage Source: https://context7.com/tus/tus-node-server/llms.txt Sets up the Tus Node.js server to use FileStore for storing uploads on the local disk. Configure the directory for uploads and optionally set an expiration period for incomplete uploads. ```typescript import { Server } from "@tus/server"; import { FileStore } from "@tus/file-store"; const server = new Server({ path: "/files", datastore: new FileStore({ // Required: directory where files are written directory: "./uploads", // Optional: mark incomplete uploads as expired after 48 h expirationPeriodInMilliseconds: 48 * 60 * 60 * 1000, // Optional: provide a custom KV store for upload metadata // configstore: new MyCustomKvStore(), }), }); server.listen({ host: "0.0.0.0", port: 1080 }); ``` -------------------------------- ### Handle Web API Requests with `server.handleWeb` Source: https://context7.com/tus/tus-node-server/llms.txt Use this handler for meta-frameworks like Next.js App Router, SvelteKit, and Cloudflare Workers. It takes a standard Web API Request and returns a Response. ```typescript // Next.js App Router — /app/api/upload/[[...slug]]/route.ts import { Server } from "@tus/server"; import { FileStore } from "@tus/file-store"; const server = new Server({ path: "/api/upload", datastore: new FileStore({ directory: "./uploads" }), }); export const GET = server.handleWeb.bind(server); export const POST = server.handleWeb.bind(server); export const PATCH = server.handleWeb.bind(server); export const DELETE = server.handleWeb.bind(server); export const OPTIONS = server.handleWeb.bind(server); export const HEAD = server.handleWeb.bind(server); ```