Fastify natively supports 'application/json'
and 'text/plain'
content types
with a default charset of utf-8
. These default parsers can be changed or
removed.
Unsupported content types will throw an FST_ERR_CTP_INVALID_MEDIA_TYPE
error.
To support other content types, use the addContentTypeParser
API or an
existing plugin.
As with other APIs, addContentTypeParser
is encapsulated in the scope in which
it is declared. If declared in the root scope, it is available everywhere; if
declared in a plugin, it is available only in that scope and its children.
Fastify automatically adds the parsed request payload to the Fastify
request object, accessible via request.body
.
Note that for GET
and HEAD
requests, the payload is never parsed. For
OPTIONS
and DELETE
requests, the payload is parsed only if a valid
content-type
header is provided. Unlike POST
, PUT
, and PATCH
, the
catch-all parser is not executed, and the payload is simply not
parsed.
When using regular expressions to detect
Content-Type
, it is important to ensure proper detection. For example, to matchapplication/*
, use/^application\/([\w-]+);?/
to match the essence MIME type only.
fastify.addContentTypeParser('application/jsoff', function (request, payload, done) {
jsoffParser(payload, function (err, body) {
done(err, body)
})
})
// Handle multiple content types with the same function
fastify.addContentTypeParser(['text/xml', 'application/xml'], function (request, payload, done) {
xmlParser(payload, function (err, body) {
done(err, body)
})
})
// Async is also supported in Node versions >= 8.0.0
fastify.addContentTypeParser('application/jsoff', async function (request, payload) {
const res = await jsoffParserAsync(payload)
return res
})
// Handle all content types that matches RegExp
fastify.addContentTypeParser(/^image\/([\w-]+);?/, function (request, payload, done) {
imageParser(payload, function (err, body) {
done(err, body)
})
})
// Can use default JSON/Text parser for different content Types
fastify.addContentTypeParser('text/json', { parseAs: 'string' }, fastify.getDefaultJsonParser('ignore', 'ignore'))
Fastify first tries to match a content-type parser with a string
value before
trying to find a matching RegExp
. For overlapping content types, it starts
with the last one configured and ends with the first (last in, first out).
To specify a general content type more precisely, first specify the general
type, then the specific one, as shown below.
// Here only the second content type parser is called because its value also matches the first one
fastify.addContentTypeParser('application/vnd.custom+xml', (request, body, done) => {} )
fastify.addContentTypeParser('application/vnd.custom', (request, body, done) => {} )
// Here the desired behavior is achieved because fastify first tries to match the
// `application/vnd.custom+xml` content type parser
fastify.addContentTypeParser('application/vnd.custom', (request, body, done) => {} )
fastify.addContentTypeParser('application/vnd.custom+xml', (request, body, done) => {} )
When using addContentTypeParser
with fastify.register
, avoid await
when registering routes. Using await
makes route registration asynchronous,
potentially registering routes before addContentTypeParser
is set.
const fastify = require('fastify')();
fastify.register((fastify, opts) => {
fastify.addContentTypeParser('application/json', function (request, payload, done) {
jsonParser(payload, function (err, body) {
done(err, body)
})
})
fastify.get('/hello', async (req, res) => {});
});
In addition to addContentTypeParser
, the hasContentTypeParser
,
removeContentTypeParser
, and removeAllContentTypeParsers
APIs are available.
Use the hasContentTypeParser
API to check if a specific content type parser
exists.
if (!fastify.hasContentTypeParser('application/jsoff')){
fastify.addContentTypeParser('application/jsoff', function (request, payload, done) {
jsoffParser(payload, function (err, body) {
done(err, body)
})
})
}
removeContentTypeParser
can remove a single content type or an array of
content types, supporting both string
and RegExp
.
fastify.addContentTypeParser('text/xml', function (request, payload, done) {
xmlParser(payload, function (err, body) {
done(err, body)
})
})
// Removes the both built-in content type parsers so that only the content type parser for text/html is available
fastify.removeContentTypeParser(['application/json', 'text/plain'])
The removeAllContentTypeParsers
API removes all existing content type parsers
eliminating the need to specify each one individually. This API supports
encapsulation and is useful for registering a
catch-all content type parser that should be executed for every
content type, ignoring built-in parsers.
fastify.removeAllContentTypeParsers()
fastify.addContentTypeParser('text/xml', function (request, payload, done) {
xmlParser(payload, function (err, body) {
done(err, body)
})
})
Notice: function(req, done)
and async function(req)
are
still supported but deprecated.
The request body can be parsed in two ways. First, add a custom content type
parser and handle the request stream. Or second, use the parseAs
option in the
addContentTypeParser
API, specifying 'string'
or 'buffer'
. Fastify will
handle the stream, check the maximum size of
the body, and the content length. If the limit is exceeded, the custom parser
will not be invoked.
fastify.addContentTypeParser('application/json', { parseAs: 'string' }, function (req, body, done) {
try {
const json = JSON.parse(body)
done(null, json)
} catch (err) {
err.statusCode = 400
done(err, undefined)
}
})
See
example/parser.js
for an example.
parseAs
(string):'string'
or'buffer'
to designate how the incoming data should be collected. Default:'buffer'
.bodyLimit
(number): The maximum payload size, in bytes, that the custom parser will accept. Defaults to the global body limit passed to theFastify factory function
.
To catch all requests regardless of content type, use the '*'
content type:
fastify.addContentTypeParser('*', function (request, payload, done) {
let data = ''
payload.on('data', chunk => { data += chunk })
payload.on('end', () => {
done(null, data)
})
})
All requests without a corresponding content type parser will be handled by this function.
This is also useful for piping the request stream. Define a content parser like:
fastify.addContentTypeParser('*', function (request, payload, done) {
done()
})
And then access the core HTTP request directly for piping:
app.post('/hello', (request, reply) => {
reply.send(request.raw)
})
Here is a complete example that logs incoming json line objects:
const split2 = require('split2')
const pump = require('pump')
fastify.addContentTypeParser('*', (request, payload, done) => {
done(null, pump(payload, split2(JSON.parse)))
})
fastify.route({
method: 'POST',
url: '/api/log/jsons',
handler: (req, res) => {
req.body.on('data', d => console.log(d)) // log every incoming object
}
})
For piping file uploads, check out
@fastify/multipart
.
To execute the content type parser on all content types, call
removeAllContentTypeParsers
first.
// Without this call, the request body with the content type application/json would be processed by the built-in JSON parser
fastify.removeAllContentTypeParsers()
fastify.addContentTypeParser('*', function (request, payload, done) {
const data = ''
payload.on('data', chunk => { data += chunk })
payload.on('end', () => {
done(null, data)
})
})