These packages provide focused utilities for working with HTTP headers, MIME types, and error responses in Node.js applications. content-disposition and content-type parse and format specific HTTP headers. http-errors creates standard HTTP error objects with appropriate status codes and messages. mime and mime-types handle MIME type lookups and extensions, while type-is checks request content types against expected values. Together, they form a foundational toolkit for building robust HTTP servers and middleware.
When building HTTP servers or middleware in Node.js, you'll frequently encounter tasks like parsing headers, validating content types, generating proper error responses, and mapping file extensions to MIME types. The six packages in this comparison (content-disposition, content-type, http-errors, mime, mime-types, and type-is) each solve specific problems in this space with minimal, focused APIs. Let's explore how they work together and when to use each one.
The content-disposition package handles the Content-Disposition header, which tells browsers how to handle responses — particularly for file downloads.
Why not DIY? Manually constructing this header is risky because filenames can contain characters that break header parsing or enable response splitting attacks.
// Using content-disposition
const contentDisposition = require('content-disposition');
// Safe filename handling with proper encoding
const header = contentDisposition('résumé.pdf');
// Returns: 'inline; filename="resume.pdf"; filename*=UTF-8\'\'r%C3%A9sum%C3%A9.pdf'
// For attachments (downloads)
const attachmentHeader = contentDisposition('report.xlsx', { type: 'attachment' });
Without this package, you'd need to implement RFC 6266 compliance yourself, including UTF-8 encoding for international filenames and proper escaping.
The content-type package parses and formats Content-Type headers according to RFC 7231.
Why not DIY? Content-Type headers can include parameters (like charset=utf-8) and have subtle parsing rules around case sensitivity and whitespace.
// Using content-type
const contentType = require('content-type');
// Parsing a header
const parsed = contentType.parse('text/html; charset=utf-8');
// Returns: { type: 'text/html', parameters: { charset: 'utf-8' } }
// Formatting back to a string
const formatted = contentType.format(parsed);
// Returns: 'text/html; charset=utf-8'
// Works with request objects too
const reqType = contentType.parse(req);
Manual parsing would require handling edge cases like quoted parameters, multiple parameters, and ensuring the main type is normalized to lowercase.
The http-errors package creates HTTP error objects with proper status codes, messages, and properties.
Why not DIY? Creating consistent error objects that work well with frameworks like Express requires setting multiple properties correctly.
// Using http-errors
const createError = require('http-errors');
// Creating specific errors
const notFound = createError(404, 'User not found');
const badRequest = createError.BadRequest('Invalid email format');
const unauthorized = createError.Unauthorized();
// Errors have proper properties
console.log(notFound.status); // 404
console.log(notFound.expose); // true (safe to show to users)
Without this, you'd manually set statusCode, status, expose, and other properties that Express and other middleware expect.
Both mime and mime-types handle MIME type lookups, but with different scopes.
// Using mime
const mime = require('mime');
// Extension to type
const type = mime.getType('file.txt'); // 'text/plain'
// Type to extension
const ext = mime.getExtension('text/html'); // 'html'
// Custom types
mime.define({ 'application/custom': ['custom'] });
// Using mime-types
const mime = require('mime-types');
// Extension to type only
const type = mime.lookup('file.txt'); // 'text/plain'
// No reverse lookup available
// mime.extension('text/html') doesn't exist
// Get charset for a type
const charset = mime.charset('text/html'); // 'UTF-8'
Choose mime if you need bidirectional lookups or custom type definitions. Choose mime-types if you only need extension-to-type mapping and want a smaller dependency.
The type-is package checks if a request's Content-Type matches expected values.
Why not DIY? Content-Type validation needs to handle wildcards, specific types, and arrays of acceptable types while ignoring parameters.
// Using type-is
const typeis = require('type-is');
// Check against single type
if (typeis(req, 'application/json')) {
// Handle JSON
}
// Check against array of types
if (typeis(req, ['json', 'urlencoded'])) {
// Handle form data or JSON
}
// Wildcard support
if (typeis(req, 'application/*')) {
// Handle any application type
}
// Get the actual matching type
const match = typeis(req, ['json', 'html']);
// Returns 'json', 'html', or false
Manual checking would require parsing the Content-Type header first and then implementing matching logic for wildcards and aliases.
These packages are designed to complement each other in typical server scenarios:
// Example: File download endpoint
const express = require('express');
const createError = require('http-errors');
const contentDisposition = require('content-disposition');
const mime = require('mime');
app.get('/download/:filename', (req, res, next) => {
const { filename } = req.params;
// Validate file exists (simplified)
if (!fileExists(filename)) {
return next(createError.NotFound('File not found'));
}
// Set proper Content-Type
const contentType = mime.getType(filename) || 'application/octet-stream';
res.setHeader('Content-Type', contentType);
// Set safe Content-Disposition
res.setHeader('Content-Disposition', contentDisposition(filename));
// Send file
res.sendFile(filename);
});
// Example: API endpoint with validation
const typeis = require('type-is');
const createError = require('http-errors');
app.post('/api/users', (req, res, next) => {
// Validate content type
if (!typeis(req, ['json'])) {
return next(createError.UnsupportedMediaType('Only JSON accepted'));
}
// Process JSON body
// ...
});
If you're already using mime, there's no need to also install mime-types. The mime package includes everything mime-types provides plus additional functionality.
Never construct Content-Disposition headers manually:
// ❌ Dangerous - vulnerable to response splitting
res.setHeader('Content-Disposition', `attachment; filename="${userFilename}"`);
// ✅ Safe - properly escaped and encoded
res.setHeader('Content-Disposition', contentDisposition(userFilename));
Mixing manual error objects with http-errors can lead to inconsistent behavior in error-handling middleware:
// ❌ Inconsistent
if (!user) {
const err = new Error('Not found');
err.statusCode = 404;
next(err);
}
// ✅ Consistent
if (!user) {
next(createError.NotFound('User not found'));
}
| Scenario | Recommended Package(s) |
|---|---|
| Serving file downloads | content-disposition + mime |
| Parsing request Content-Type | content-type + type-is |
| Creating API error responses | http-errors |
| Static file server | mime-types (lighter) or mime (full-featured) |
| Validating request body format | type-is |
| Generating Content-Type headers | mime |
These packages follow the Unix philosophy of doing one thing well. Rather than building monolithic utilities, they provide composable, focused tools that handle the tricky details of HTTP standards correctly.
content-disposition whenever you're setting that header — the security benefits alone justify the dependency.content-type for parsing/formatting Content-Type headers — it handles RFC compliance so you don't have to.http-errors for all error creation — it ensures consistency across your application.mime and mime-types based on whether you need bidirectional lookups.type-is for content type validation — it's much more reliable than manual string matching.By using these specialized packages, you avoid common security vulnerabilities, ensure standards compliance, and write more maintainable code that clearly expresses its intent.
Choose mime-types when you only need extension-to-MIME-type lookups and want a lighter dependency. It's essentially the lookup functionality extracted from the mime package but without extension-to-type support. This is ideal for static file servers that map file extensions to Content-Type headers without needing reverse lookups.
Choose content-disposition when you need to safely generate or parse the Content-Disposition header for file downloads. It properly handles filename encoding for special characters and prevents response splitting attacks by escaping newlines. This is essential for any application serving user-uploaded files or generating downloadable content.
Choose http-errors when you need to create HTTP error objects that follow standard conventions. It provides named constructors for all HTTP status codes (like createError.NotFound()) and ensures errors have proper status codes, message properties, and expose settings. This is crucial for consistent error handling in Express.js applications and custom middleware.
Choose mime when you need a comprehensive MIME type database with both type-to-extension and extension-to-type lookups. It includes a large built-in database and supports custom type definitions. Use this over mime-types if you need extension lookups or want a single package that handles both directions of MIME type resolution.
Choose type-is when you need to check if an incoming request's Content-Type matches expected values. It supports wildcards (like application/*), specific types, and arrays of acceptable types. This is commonly used in middleware to validate request bodies before processing, ensuring your handlers only receive appropriately formatted data.
Choose content-type when you need to parse or format Content-Type headers according to RFC 7231. It correctly handles media types with parameters (like charset) and provides normalized access to type and subtype. Use this instead of manual string parsing to avoid common pitfalls with parameter parsing and case sensitivity.
The ultimate javascript content-type utility.
Similar to the mime@1.x module, except:
mime-types simply returns false, so do
var type = mime.lookup('unrecognized') || 'application/octet-stream'.new Mime() business, so you could do var lookup = require('mime-types').lookup..define() functionality.lookup(path)Otherwise, the API is compatible with mime 1.x.
This is a Node.js module available through the
npm registry. Installation is done using the
npm install command:
$ npm install mime-types
This package considers the programmatic api as the semver compatibility. Additionally, the package which provides the MIME data
for this package (mime-db) also considers it's programmatic api as the semver contract. This means the MIME type resolution is not considered
in the semver bumps.
In the past the version of mime-db was pinned to give two decision points when adopting MIME data changes. This is no longer true. We still update the
mime-db package here as a minor release when necessary, but will use a ^ range going forward. This means that if you want to pin your mime-db data
you will need to do it in your application. While this expectation was not set in docs until now, it is how the pacakge operated, so we do not feel this is
a breaking change.
If you wish to pin your mime-db version you can do that with overrides via your package manager of choice. See their documentation for how to correctly configure that.
All mime types are based on mime-db, so open a PR there if you'd like to add mime types.
var mime = require('mime-types')
All functions return false if input is invalid or not found.
Lookup the content-type associated with a file.
mime.lookup('json') // 'application/json'
mime.lookup('.md') // 'text/markdown'
mime.lookup('file.html') // 'text/html'
mime.lookup('folder/file.js') // 'application/javascript'
mime.lookup('folder/.htaccess') // false
mime.lookup('cats') // false
Create a full content-type header given a content-type or extension.
When given an extension, mime.lookup is used to get the matching
content-type, otherwise the given content-type is used. Then if the
content-type does not already have a charset parameter, mime.charset
is used to get the default charset and add to the returned content-type.
mime.contentType('markdown') // 'text/x-markdown; charset=utf-8'
mime.contentType('file.json') // 'application/json; charset=utf-8'
mime.contentType('text/html') // 'text/html; charset=utf-8'
mime.contentType('text/html; charset=iso-8859-1') // 'text/html; charset=iso-8859-1'
// from a full path
mime.contentType(path.extname('/path/to/file.json')) // 'application/json; charset=utf-8'
Get the default extension for a content-type.
mime.extension('application/octet-stream') // 'bin'
Lookup the implied default charset of a content-type.
mime.charset('text/markdown') // 'UTF-8'
A map of content-types by extension.
A map of extensions by content-type.