Static Files
Shokupan provides built-in support for serving static files with optional directory listing.
Basic Usage
Section titled “Basic Usage”Serve files from a directory:
import { Shokupan } from 'shokupan';
const app = new Shokupan();
// Serve static files from ./publicapp.static('/public', { root: './public'});
app.listen();Now files in ./public are accessible:
./public/style.css→http://localhost:3000/public/style.css./public/script.js→http://localhost:3000/public/script.js./public/images/logo.png→http://localhost:3000/public/images/logo.png
Directory Listing
Section titled “Directory Listing”Enable directory browsing:
app.static('/files', { root: './files', listDirectory: true // Enable directory listing});When you visit http://localhost:3000/files/, you’ll see a list of files and subdirectories.
Multiple Static Directories
Section titled “Multiple Static Directories”Serve from multiple directories:
// Public assetsapp.static('/public', { root: './public', listDirectory: true});
// Imagesapp.static('/images', { root: './assets/images', listDirectory: false});
// JavaScript bundlesapp.static('/js', { root: './dist/js', listDirectory: false});
// CSS stylesheetsapp.static('/css', { root: './dist/css', listDirectory: false});Root Path
Section titled “Root Path”Serve files at the root path:
// Serve from rootapp.static('/', { root: './public'});
// Now accessible at:// ./public/index.html → http://localhost:3000/index.htmlTypical Project Structure
Section titled “Typical Project Structure”my-app/├── public/│ ├── index.html│ ├── favicon.ico│ ├── css/│ │ └── style.css│ ├── js/│ │ └── app.js│ └── images/│ └── logo.png└── src/ └── index.tsConfiguration:
app.static('/public', { root: './public', listDirectory: true});
// Or serve at root for SPAapp.static('/', { root: './public'});SPA (Single Page Application)
Section titled “SPA (Single Page Application)”For SPAs, serve your build directory and handle client-side routing:
import { Shokupan } from 'shokupan';
const app = new Shokupan();
// API routesapp.get('/api/users', (ctx) => ({ users: [] }));app.get('/api/posts', (ctx) => ({ posts: [] }));
// Serve static filesapp.static('/', { root: './dist'});
// Fallback to index.html for client-side routingapp.get('/*', (ctx) => { return ctx.file('./dist/index.html', { type: 'text/html' });});
app.listen();File Types
Section titled “File Types”Shokupan automatically sets the correct Content-Type header based on file extension:
.html→text/html.css→text/css.js→application/javascript.json→application/json.png→image/png.jpg,.jpeg→image/jpeg.svg→image/svg+xml.pdf→application/pdf- And many more…
Security Considerations
Section titled “Security Considerations”// ✅ Safe - specific public directoryapp.static('/public', { root: './public', listDirectory: true});
// ❌ Dangerous - entire projectapp.static('/', { root: '.', listDirectory: true});Caching Headers
Section titled “Caching Headers”Add cache control headers:
const staticMiddleware = async (ctx, next) => { const result = await next();
// Add cache headers for static assets if (ctx.path.startsWith('/public')) { ctx.set('Cache-Control', 'public, max-age=31536000'); }
return result;};
app.use(staticMiddleware);
app.static('/public', { root: './public'});Custom 404
Section titled “Custom 404”Handle missing static files:
app.static('/files', { root: './files', listDirectory: true});
// Fallback for 404app.get('/files/*', (ctx) => { return ctx.html('<h1>File Not Found</h1>', 404);});Development vs Production
Section titled “Development vs Production”Different configurations for environments:
const isDev = process.env.NODE_ENV !== 'production';
app.static('/public', { root: './public', listDirectory: isDev // Only in development});
if (isDev) { // Development-specific static routes app.static('/docs', { root: './docs', listDirectory: true });}Next Steps
Section titled “Next Steps”- Routing - Learn about dynamic routing
- Middleware - Add caching and compression
- Compression Plugin - Compress static assets