elysia-static

Serve static files and directories in Elysia apps with automatic caching, ETags, and Bun-optimized performance.

Library
npm
v1.4.10
50stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
42/100Fair
Development Activity36
Maintenance32
Community28
Maturity52
Momentum20

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
74/100Good
Architecture75
Code Quality85
Innovation80
Learning Curve55

@elysiajs/static is the official static-file plugin for the Elysia web framework, adding a single .use(staticPlugin()) call that mounts an entire directory of assets as GET routes. It handles the plumbing a production file server needs — content-type detection via Elysia’s built-in file helper, conditional requests via ETag and If-Modified-Since, configurable Cache-Control directives, and automatic index.html resolution for directory roots — without requiring a separate reverse proxy or CDN for local development or small deployments.

Under the hood it adapts to the runtime: on Bun it can pre-bundle and inline HTML entry points through Bun’s fullstack bundler for zero-overhead delivery, while on Node.js it falls back to fs/promises and an in-memory LRU cache with TTL-based eviction to avoid re-reading files from disk on every request. A configurable staticLimit switches large asset trees from per-file static routes to a single wildcard catch-all route once a threshold is exceeded, keeping route-table size bounded for directories with thousands of files.

What You Get

  • A one-line .use(staticPlugin()) integration that mounts an assets directory as GET routes on your Elysia app
  • Automatic ETag generation and If-Modified-Since / If-None-Match handling for HTTP 304 conditional responses
  • An in-memory LRU file cache with TTL eviction that avoids repeated disk reads on Node.js
  • Bun-specific fast paths, including fullstack HTML bundling and Bun.file()-backed responses
  • Configurable ignore patterns, custom headers, cache-control directives, and URL prefix/decoding options

Common Use Cases

  • Serving a built frontend’s static assets (JS/CSS/images) directly from an Elysia API server
  • Exposing uploaded user files or generated reports from a local directory during development
  • Bundling and serving Bun-native HTML entry points for full-stack Bun applications
  • Adding browser-cacheable static routes to a microservice without introducing a separate CDN or nginx layer

Under The Hood

Architecture The plugin is a single functional entry point (src/index.ts) that returns a configured Elysia instance: it resolves the assets directory, enumerates files via listFiles (Bun’s native glob or a recursive fs/promises walk), sorts HTML files first for Bun bundling priority, then mounts each file as its own GET route via a mountRoute helper, or falls back to one wildcard route once staticLimit is exceeded. Request-time file resolution, ETag generation, and conditional-cache logic (alreadyCachedDownstream) are factored into src/utils.ts, keeping the route-mounting logic in index.ts separate from file I/O and caching concerns; the whole static-serving surface hinges on this single staticPlugin factory, so any change to its request pipeline affects every mounted route at once.

Tech Stack Written in TypeScript against the Elysia framework (peer dependency >= 1.4.0), it targets both Bun and Node.js runtimes by probing process.getBuiltinModule for fs/promises and path at runtime rather than importing them statically. It uses fast-decode-uri-component for URL decoding and Bun’s native Bun.Glob/Bun.file/Bun.CryptoHasher APIs when available. The package is built with a custom build.ts script (esbuild/tsup-based) and published to npm as dual ESM/CJS output with generated type declarations.

Code Quality The test suite (test/index.test.ts, 925 lines) exercises caching headers, ETag conditional requests, wildcard routing, ignore patterns, index.html fallback, and path-traversal rejection, with separate Node CJS/ESM interop tests under test/node/. CI workflows (ci.yml, test.yml, publish.yml, publish-legacy.yml) run the suite and gate releases. Errors are handled explicitly — file-traversal attempts and missing files throw a typed NotFoundError rather than leaking stack traces, and a silent option suppresses non-fatal warnings. ESLint with typescript-eslint is configured, though a formal CONTRIBUTING.md or docs/ directory is not present.

API Design The public API is a single factory function with sensible defaults (public directory, /public prefix, ETag caching on, 86400s max-age) so .use(staticPlugin()) works with zero configuration, while every behavior — ignore patterns, cache directive, URL decoding, Bun HTML bundling, wildcard threshold — is exposed as a documented, JSDoc-commented option on one options object. This keeps the common case to one line while still surfacing the runtime-specific knobs (like bunFullstack) that power users need, at the cost of a fairly large single options interface to read through.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search