This comparison evaluates six critical npm packages used for handling MIME types, content headers, and file signature detection in Node.js environments. While mime-db serves as the foundational database of known types, mime-types and mime provide high-level APIs for looking up extensions and generating headers. content-type focuses strictly on parsing and formatting HTTP Content-Type header strings with parameters. file-type distinguishes itself by detecting file formats via binary magic numbers rather than extensions. Finally, mime-lookup offers a lightweight alternative for extension-to-type mapping. Understanding the specific role of each prevents architectural anti-patterns, such as trusting file extensions for security-critical validation or manually parsing complex header strings.
Handling MIME types correctly is a foundational requirement for any Node.js application that serves static assets, processes file uploads, or negotiates content with clients. A misunderstanding here can lead to broken media playback, incorrect browser rendering, or severe security vulnerabilities where malicious files are executed because their type was guessed incorrectly. The ecosystem offers several packages, each solving a specific slice of this problem. Let's break down how they work, where they overlap, and which one belongs in your architecture.
The most critical architectural decision is how you determine a file's type. Relying on file extensions (like .jpg or .pdf) is fast but insecure. Users can easily rename a malicious script to image.jpg. To solve this, you must inspect the file's actual binary content.
file-type is the only package in this list that performs "magic number" detection. It reads the initial bytes of a file buffer or stream to identify the true format.
import fileType from 'file-type';
import { readFileSync } from 'fs';
const buffer = readFileSync('uploads/user-file.dat');
const result = await fileType.fromBuffer(buffer);
if (result && result.mime === 'image/jpeg') {
console.log('Safe to process as JPEG');
} else {
console.log('Rejected: Not a valid JPEG');
}
In contrast, mime-types, mime, and mime-lookup rely entirely on the file extension. They are useful for setting response headers when you know the file is valid, but never for validating untrusted input.
import mimeTypes from 'mime-types';
// Dangerous if used for validation on untrusted files
const type = mimeTypes.lookup('suspicious-file.exe');
// Returns 'application/x-msdownload' based solely on name
At the bottom of the stack sits mime-db. This package contains no logic; it is simply a large JSON file mapping MIME types to their associated extensions and properties. You should rarely import this directly unless you are building your own MIME library.
import db from 'mime-db';
// Direct access to raw data
const info = db['application/json'];
// Returns: { source: 'iana', compressible: true, extensions: ['json'] }
Most developers should use mime-types, which wraps mime-db with convenient functions. It is the standard choice for looking up a type by extension or finding the default extension for a type.
import mimeTypes from 'mime-types';
// Lookup type by extension
const type = mimeTypes.lookup('style.css');
// Returns 'text/css'
// Lookup extension by type
const ext = mimeTypes.extension('text/html');
// Returns 'html'
The mime package (distinct from mime-types) offers a similar API but includes its own database and allows easier customization of types within the app lifecycle. It is often preferred in build tools or static servers where you might need to add custom types dynamically.
import mime from 'mime';
// Define a custom type on the fly
mime.define({ 'application/x-custom': ['cust'] });
const type = mime.getType('archive.cust');
// Returns 'application/x-custom'
For simpler needs, mime-lookup provides a minimalistic approach. It focuses strictly on the extension-to-type mapping without the extra baggage, making it viable for small utilities.
import mimeLookup from 'mime-lookup';
const type = mimeLookup.lookup('.png');
// Returns 'image/png'
Once you know the MIME type, you often need to construct or parse the full Content-Type HTTP header, which includes parameters like charset or boundary. This is where content-type shines. It does not guess types; it strictly parses and formats header strings.
Use this when writing middleware that needs to read the Content-Type header from an incoming request reliably.
import contentType from 'content-type';
// Parsing an incoming header
const header = 'text/html; charset=utf-8';
const parsed = contentType.parse(header);
console.log(parsed.type);
// 'text/html'
console.log(parsed.parameters.charset);
// 'utf-8'
You can also use it to generate a valid header string from a type and parameters, ensuring correct formatting according to RFC standards.
// Formatting a new header
const header = contentType.format({
type: 'application/json',
parameters: { charset: 'utf-8' }
});
// Returns: 'application/json; charset=utf-8'
Neither mime-types nor mime handles parameter parsing this strictly. They typically return just the base type (e.g., text/html), leaving you to manually append parameters if you don't use content-type.
In a production application, these packages often work together. A typical file upload pipeline might look like this:
file-type to verify the binary content matches the expected format.mime-types or mime to set the Content-Type header for the response if serving the file back.content-type if you need to inspect the multipart boundary of the incoming POST request.Here is how a secure upload handler combines these tools:
import fileType from 'file-type';
import mimeTypes from 'mime-types';
import contentType from 'content-type';
async function handleUpload(req, res) {
// 1. Parse the request header safely
const ct = contentType.parse(req.headers['content-type']);
if (!ct.type.startsWith('multipart/')) {
return res.status(400).send('Invalid content type');
}
// 2. Assume we have the file buffer now
const buffer = req.body.fileBuffer;
const detected = await fileType.fromBuffer(buffer);
// 3. Validate against allowed list
const allowed = ['image/jpeg', 'image/png'];
if (!detected || !allowed.includes(detected.mime)) {
return res.status(415).send('Unsupported file format');
}
// 4. Set response header using the detected type
res.setHeader('Content-Type', detected.mime);
res.send(buffer);
}
If you tried to do step 3 using only mime-types.lookup(req.body.fileName), you would be vulnerable to attacks where a user uploads malware.php renamed as photo.jpg.
| Feature | content-type | file-type | mime | mime-db | mime-lookup | mime-types |
|---|---|---|---|---|---|---|
| Primary Goal | Parse/Format Headers | Binary Detection | Type Lookup & Mapping | Raw Data Source | Simple Lookup | Standard Lookup |
| Input Source | Header String | Buffer/Stream | Filename/Extension | None (Data Only) | Extension | Extension/Type |
| Security Safe? | N/A (Parser) | β Yes (Magic Numbers) | β No (Extension only) | N/A | β No | β No |
| Handles Params | β Yes (charset, etc) | β No | β οΈ Limited | β No | β No | β οΈ Limited |
| Custom Types | β No | β No | β Yes | β No | β No | β No |
| Best For | Middleware Logic | File Validation | Static Servers | Library Authors | Lightweight Scripts | General Backend |
For File Uploads: Always start with file-type. Never trust the client-sent filename or extension for security decisions. If file-type returns undefined, the file is either unsupported or corruptedβreject it immediately.
For Static Asset Servers: Use mime or mime-types. They are optimized for speed and cover the vast majority of web standards. mime is slightly better if your app serves niche file types that require custom definitions not found in the standard database.
For HTTP Middleware: Use content-type to parse incoming headers. It handles edge cases and malformed headers much better than splitting strings manually. It ensures your application adheres to HTTP specifications when negotiating content.
For Data-Heavy Tools: If you are building a linter, a bundler, or a tool that needs to iterate over every known MIME type, reach for mime-db directly. It saves you from bundling the logic of other libraries when you only need the data.
These packages are not competitors; they are specialized tools for different layers of the network stack. mime-db is the dictionary, mime-types and mime are the translators, file-type is the forensic inspector, and content-type is the protocol specialist. Using the right tool for each layer ensures your application is fast, standards-compliant, and secure against common file-based attacks.
Choose content-type when you need to strictly parse or format HTTP Content-Type header strings, including parameters like charset or boundary. It is the industry standard for middleware that validates incoming request headers or constructs response headers without altering the core MIME type logic. Do not use it for file extension lookups or binary detection.
Choose file-type when security and accuracy are paramount, such as validating user uploads where file extensions cannot be trusted. It inspects the actual binary content (magic numbers) of a buffer or stream to determine the true file format. This is the only correct choice for preventing malicious files disguised with wrong extensions.
Choose mime (the mime package) if you need a robust, feature-rich API that handles both MIME type lookups and custom type definitions easily. It is ideal for build tools, static file servers, or applications that need to define non-standard types. Note that it differs from mime-types in API design and default database sourcing.
Choose mime-db only if you are building a custom MIME utility or need direct, raw access to the official IANA and Apache mime-types database JSON. It provides no helper functions; it is purely a data source. Most applications should use mime-types or mime instead of consuming this directly.
Choose mime-lookup for lightweight projects where you only need a simple, synchronous mapping from file extension to MIME type without the extra features of larger libraries. It is suitable for scripts or micro-services where minimizing dependency complexity is a priority, though it lacks the extensive ecosystem support of mime-types.
Choose mime-types for standard Node.js applications requiring fast, reliable MIME type lookups based on the official mime-db. It is the go-to choice for general-purpose extension-to-type mapping and generating basic content-type headers. It pairs perfectly with content-type for full HTTP header management.
Create and parse HTTP Content-Type header.
npm install content-type
import * as contentType from "content-type";
const obj = contentType.parse("image/svg+xml; charset=utf-8");
Parse a Content-Type header. This will return an object with the following properties (examples are shown for the string 'image/svg+xml; charset=utf-8'):
type: The media type. Example: 'image/svg+xml'.parameters: An object of the parameters in the media type (parameter name is always lower case). Example: {charset: 'utf-8'}.The parser is lenient and does not error. You should validate type and parameters before trusting them.
parameters (default: true): Set to false to skip parameters.comma (default: false): Set to true to stop on a comma. This can be used to parse the media range in an Accept header.start (default: 0): Set index to start parsing from.const str = contentType.format({
type: "image/svg+xml",
parameters: { charset: "utf-8" },
});
Format an object into a Content-Type header. This will return a string of the content type for the given object with the following properties (examples are shown that produce the string 'image/svg+xml; charset=utf-8'):
type: The media type. Example: 'image/svg+xml'.parameters: An optional object of the parameters in the media type. Example: {charset: 'utf-8'}.Throws a TypeError if the object contains an invalid type or parameter names.