file-type and mime-types both handle file identification in JavaScript, but they solve different problems. file-type inspects the actual content of a file (magic numbers) to determine its true type, making it secure against extension spoofing. mime-types maps file extensions to MIME type strings, which is useful for setting HTTP headers but relies on the filename being correct. Choose file-type for security validation and mime-types for routing or content negotiation based on known file structures.
Both file-type and mime-types help developers work with file formats, but they operate at different layers of the stack. file-type looks inside the file to see what it really is, while mime-types looks at the filename to guess what it should be. Understanding this difference is critical for building secure and reliable applications.
file-type reads the actual bytes of a file.
// file-type: Reads buffer content
import { fileTypeFromBuffer } from 'file-type';
const buffer = await fs.readFile('upload.dat');
const result = await fileTypeFromBuffer(buffer);
console.log(result);
// Output: { ext: 'png', mime: 'image/png' }
// Even if the file was named 'upload.dat'
mime-types reads the file path or extension string.
false if the extension is unknown.// mime-types: Reads path string
import mime from 'mime-types';
const type = mime.lookup('upload.dat');
console.log(type);
// Output: false or 'application/octet-stream'
// It cannot see inside the file
file-type protects against extension spoofing.
virus.exe renamed to image.jpg, this package detects it is an executable.// file-type: Catches spoofed files
const buffer = getUploadedFileBuffer(); // Contains EXE data
const result = await fileTypeFromBuffer(buffer);
if (result?.mime !== 'image/jpeg') {
throw new Error('Invalid file content'); // Blocks the EXE
}
mime-types trusts the filename completely.
virus.exe renamed to image.jpg, it will return image/jpeg.// mime-types: Vulnerable to spoofing
const type = mime.lookup('virus.jpg'); // File is actually EXE
console.log(type);
// Output: 'image/jpeg'
// This incorrectly validates the malicious file
file-type is ESM only in recent versions (v18+).
import syntax or dynamic import().// file-type: Requires ESM
import { fileTypeFromFile } from 'file-type';
// Cannot use require() directly in modern versions
const result = await fileTypeFromFile('path/to/file');
mime-types supports CommonJS and ESM.
require() out of the box.// mime-types: Works with CommonJS
const mime = require('mime-types');
// Also works with import
import mime from 'mime-types';
const type = mime.lookup('file.txt');
file-type returns a detailed object.
undefined if the type is not recognized.// file-type: Rich return value
const result = await fileTypeFromBuffer(buffer);
if (result) {
console.log(result.ext); // 'png'
console.log(result.mime); // 'image/png'
}
mime-types returns a simple string or boolean.
false.// mime-types: Simple return value
const type = mime.lookup('file.png'); // 'image/png'
const ext = mime.extension('image/png'); // 'png'
if (!type) {
console.log('Unknown type');
}
While their methods differ, both packages aim to standardize file handling in Node.js. Here are key overlaps:
image/png or application/pdf string.Content-Type headers.// file-type: Get MIME from content
const { mime } = await fileTypeFromBuffer(buffer);
res.setHeader('Content-Type', mime);
// mime-types: Get MIME from extension
const type = mime.lookup('image.png');
res.setHeader('Content-Type', type);
file-type also supports browsers and Deno, while mime-types is mostly Node-focused.// Both work in standard Node.js scripts
import { fileTypeFromBuffer } from 'file-type';
import mime from 'mime-types';
// Usage in server logic
file-type supports more obscure binary formats via magic numbers.mime-types supports web-standard extensions via IANA registry.// file-type: Detects binary signatures
await fileTypeFromBuffer(pdfBuffer); // { ext: 'pdf', mime: 'application/pdf' }
// mime-types: Maps known extensions
mime.lookup('doc.pdf'); // 'application/pdf'
| Feature | Shared by file-type and mime-types |
|---|---|
| Core Output | π MIME type strings |
| Environment | βοΈ Node.js support |
| Use Case | π File handling logic |
| Formats | β Images, audio, video, docs |
| Feature | file-type | mime-types |
|---|---|---|
| Detection | π Reads file content (bytes) | π·οΈ Reads file extension (string) |
| Security | π‘οΈ Safe against spoofing | β οΈ Vulnerable to spoofing |
| Module System | π¦ ESM Only (v18+) | π¦ CommonJS + ESM |
| Return Value | π Object { ext, mime } | π String or false |
| Input Requirement | ποΈ Needs Buffer/Stream/File | π Needs Path/Extension string |
file-type is like a security guard π‘οΈ β it checks the ID inside the packet, not just the label on the outside. Use this for uploads, processing pipelines, and any situation where trust is low.
mime-types is like a postal sorter π¬ β it looks at the address label to decide where the packet goes. Use this for setting headers, routing, and serving static assets where extensions are controlled.
Final Thought: Never use mime-types to validate user uploads. The small convenience of checking a string is not worth the risk of executing malicious code. For validation, always inspect the content with file-type.
Choose mime-types when you need to map known file extensions to MIME types for HTTP responses or static file serving. It is lightweight and perfect for setting Content-Type headers in Express or Koa when you already trust the file extension. Do not use this for security validation, as it cannot detect if a file's content matches its extension.
Choose file-type when security is critical, such as validating user uploads where a malicious actor might rename an executable to .jpg. It reads the file content directly, so it works even if the extension is missing or wrong. This is the right choice for backend validation pipelines, image processing services, and any scenario where trusting the filename is unsafe.
The ultimate javascript content-type utility.
Similar to the [email protected] 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.