Gathering detailed insights and metrics for fastify-xml-server
Gathering detailed insights and metrics for fastify-xml-server
Gathering detailed insights and metrics for fastify-xml-server
Gathering detailed insights and metrics for fastify-xml-server
Fastify plugin which integrates xml2js and modifies your Fastify server to receive XML requests and send XML responses while still working with JSON data internally.
npm install fastify-xml-server
Typescript
Module System
Node Version
NPM Version
Skip reply serialization for string payload
Updated on Jan 10, 2025
Add max xml tree depth to plugin config
Updated on Jan 09, 2025
Fix on demand parser/builder closure override
Updated on Dec 06, 2024
Correct assignOneElementArrays to work with no arrays on top level
Updated on Dec 04, 2024
Fix on demand builder options
Updated on Dec 04, 2024
Add xml2js options to on demand functions
Updated on Dec 04, 2024
TypeScript (100%)
Total Downloads
0
Last Day
0
Last Week
0
Last Month
0
Last Year
0
1 Stars
37 Commits
1 Watchers
8 Branches
1 Contributors
Updated on Jan 12, 2025
Latest Version
1.5.8
Package Id
fastify-xml-server@1.5.8
Unpacked Size
27.36 kB
Size
8.02 kB
File Count
8
NPM Version
10.8.2
Node Version
20.18.1
Published on
Jan 10, 2025
Cumulative downloads
Total Downloads
Last Day
0%
NaN
Compared to previous day
Last Week
0%
NaN
Compared to previous week
Last Month
0%
NaN
Compared to previous month
Last Year
0%
NaN
Compared to previous year
5
Fastify plugin which integrates xml2js and modifies your Fastify server to receive XML requests and send XML responses while still working with JSON data internally.
Contents:
Even though this plugin allows to completely switch the communication data format of a server and handle errors with XML responses (and is definitely useful for majority of use cases where you need SOAP) it is missing one important feature of a complete SOAP server - which is the routing. Unlike REST it is not uncommon for SOAP to have a single URL path responsible for multiple operations and the decision on what operation to perform is made based on the payload of a request. So if you are facing a use case like that you would have to implement this decision logic in a single request handler or maybe find additional libraries for this purpose. Otherwise this plugin should satisfy the vast majorty of SOAP use cases, but this detail does not allow to call it SOAP server.
1import fastify from 'fastify'; 2import { fastifyXmlServer } from 'fastify-xml-server'; 3 4const server = fastify({ logger: true }); 5 6server.register(fastifyXmlServer, { contentType: ['application/xml'] }); 7 8server.listen({ port: 3000 }, (err, address) => { 9 server.log.info(`server listening on ${address}`); 10});
The plugin is configured with options object of XmlServerOptions
interface:
1export interface XmlServerOptions extends FastifyPluginOptions { 2 parserOptions?: ParserOptions; 3 serializerOptions?: BuilderOptions; 4 errorTranslator?: (error: any) => Record<string, any>; 5 wrapper?: Record<string, any>; 6 contentType?: string[]; 7 maxXmlTreeDepth?: number; 8 assignOneElementArrays?: boolean; 9 propagateRawXml?: boolean; 10 dropNamespacePrefixes?: boolean; 11}
parserOptions
- config of xml2js parser (responsible for parsing xml requests to json) using xml2js ParserOptions
interface without changes, this config is passed through directly to xml2js.
default:
1{ 2 explicitRoot: false, // removes root wrapper element to avoid unnecessary property chaning in Javascript 3 ignoreAttrs: true // removes xml tag attributes from resulting JSON object leaving only tag values 4}
serializerOptions
- config of xml2js builder (responsible for serializing responses to xml string) using xml2js BuilderOptions
interface without changes, this config is passed through directly to xml2js.
default:
1{ 2 renderOpts: { 3 pretty: false; // skips additional processing for linebreaks and indentation to improve performance and reduce response size 4 } 5}
errorTranslator
- a function responsible for translating errors into input objects for xml serializer. This function replaces standard Fastify error handler and allows to send xml even for some automatic error responses of Fastify.
By default errorTranslator
maps Node.js Error
into SOAP 1.2 Fault
schema (setting only Code
and Reason
tags).
wrapper
- Node.js object describing top level xml wrapper tags. The plugin automatically wraps all of your responses into the specified wrapper object before proceeding to serialization to help you avoid creating boilerplate code and complicating response objects in your implementation.
By default a wrapper object is the standard SOAP 1.2 Envelope
and Body
tags specified in standard SOAP 1.2 env
namespace.
default:
1{ 2 'env:Envelope': { 3 $: { 4 'xmlns:env': 'https://www.w3.org/2003/05/soap-envelope/', 5 }, 6 'env:Body': '', 7 }, 8}
Note: wrapper object is serialized using the same xml2js builder therefore you should use xml2js syntax for specifing attributes etc.
contentType
- list of supported HTTP content types.
default: ['application/xml', 'text/xml']
Note: first element from the list is used to set
Content-Type
header on responses, all values represent supported request content types.
It is possible that some clients might pass additional parameters in the Content-Type
header such as charset
. The behavior of the plugin in such cases in the following:
Content-Type
header (ignoring parameters) is in the contentType
list it is considered supported;contentType
list contains the exact match with all parameters.In other words if specific parameters are not of much importance (which should be true for most use cases) - specifing a content type without parameters in the list would automatically support all of the parametrized variations of that type.
For example contentType
set to ['application/xml']
would implicitly support application/xml;charset=utf-8
, application/xml;charset="utf-8"
, application/xml;charset=utf-16
etc.
However if you need to accept only certain parametrized content types - just specify those explicitly in the contentType
array.
maxXmlTreeDepth
- sets the limit for recursion depth over the XML tree during parsing.
default: 500
assignOneElementArrays
- xml2js parser always outputs all child elements as arrays, even in cases there the child of an element is a string value or there is just one child, this flag allows to improve this output by assigning all arrays with one element and all arrays with element values as values to their parent keys dropping the array syntax (which makes it much more operable, see example)
default: true
Example:
Input xml:
1<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"> 2 <soapenv:Header/> 3 <soapenv:Body> 4 <soapenv:detail> 5 <soapenv:Value>1</soapenv:Value> 6 </soapenv:detail> 7 </soapenv:Body> 8</soapenv:Envelope>
Standard xml2js parser output:
1{ 2 "soapenv:Header": [""], 3 "soapenv:Body": [ 4 { 5 "soapenv:detail": [ 6 { 7 "soapenv:Value": ["1"] 8 } 9 ] 10 } 11 ] 12}
Standard xml2js parser output with assignOneElementArrays
enabled:
1{ 2 "soapenv:Header": "", 3 "soapenv:Body": { 4 "soapenv:detail": { 5 "soapenv:Value": "1" 6 } 7 } 8}
Note: because
assignOneElementArrays
executes a recursive function on a JSON tree it is limited in depth bymaxXmlTreeDepth
field in the config to avoid long running operations caused by too deep recusrion.
propagateRawXml
- if enabled adds rawXml
property containing origninal XML string to Fastify request object making it available in a handler function.
default: false
dropNamespacePrefixes
- if enabled all XML namespace prefixes in tag names (keys of the parsed JSON) will be removed.
default: false
XML being an extensible format allows to specify any namespaces a user needs and then use those namespaces in tag names in order to distinguish some of them to a higher degree (see XML namespaces).
This means that theoretically different consumers of your API might be using different namespace prefixes for the same namespaces.
For example there is a namespace for domain schemas of your API defined at http://xsd.org/domain and one consumer specifies it as xmlns:domain="http://xsd.org/domain"
whereas another consumer specifies it as xmlns:dmn="http://xsd.org/domain"
which means that tags of that namespace for those two consumers will look like <domain:entity>
and <dmn:entity>
respectively. From the XML perspective everything is consistent but when parsed to JSON this means that the keys of resulting JSON objects will be different for different requests since there is no concept of "key namespace" in JSON format. This fact may pose various problems like creating request schemas (in case of Fastify) or having some deterministic logic to process such request payloads at all.
Therefore if your API uses some custom namespaces where there is a chance of different consumers using different namespace prefixes it is better to enable this option and the plugin will omit all of the namespace prefixes from JSON keys for you during request parsing allowing to have a static schema of JSON objects regardless of XML namespaces usage.
In order to get access to the original XML request string in your request handler or custom hook functions set propagateRawXml
property in options object to true
.
After that is done the plugin will add the original XML into rawXml
property of a Fastify request object.
For usage with Typescript the plugin uses declaration merging of FastifyRequest
interface inside of the d.ts file, which allows to reference rawXml
property on a request.
1import fastify from 'fastify'; 2import { fastifyXmlServer } from 'fastify-xml-server'; 3 4const server = fastify({ logger: true }); 5 6server.register(fastifyXmlServer, { propagateRawXml: true }); 7 8server.route({ 9 method: 'POST', 10 url: '/call', 11 handler: async (req, rep) => { 12 console.log(req.rawXml); 13 return rep.status(200).send(req.body['soapenv:Body']['soapenv:detail']); 14 }, 15}); 16 17server.listen({ port: 3000 }, (err, address) => { 18 server.log.info(`server listening on ${address}`); 19});
Note: keep in mind that the original XML string might take up a considerable amount of memory.
You can invoke the same xml parsing or building logic which is configured for your Fastify instance on demand at any point in code using parseXml
and buildXml
functions respectively.
parseXml
completely mimics the parsing logic you have configured for the Fastify plugin.
However it is also possible to override some of the parsing logic by passing an optional configuration object of interface XmlParserOptions
to parseXml
which consists of a subset of XmlServerOptions
parameters related to parsing logic.
1export interface XmlParserOptions { 2 parserOptions?: ParserOptions; 3 wrapper?: Record<string, any>; 4 assignOneElementArrays?: boolean; 5 dropNamespacePrefixes?: boolean; 6}
You can use this function even before the plugin is initialized, in this case it would behave according to the default configuration.
The function takes one argument which is an xml string and one type argument which specifies a return type of the function, by default it is Record<string, any>
.
1async function parseXml<T = Record<string, any>>(xml: string, options?: XmlParserOptions): Promise<T>;
Example usage:
1import { parseXml } from 'fastify-xml-server'; 2 3const xml = '<env:Envelope><value>1</value></env:Envelope>'; 4 5(async () => { 6 const json = await parseXml(xml); 7})();
buildXml
completely mimics the XML building logic which used to serialize XML responses in the Fastify plugin.
It is similarly possible to override some of the parameters by passing an optional configuration object of interface XmlBuilderOptions
which of a subset of XmlServerOptions
parameters related to serialization logic:
1export interface XmlBuilderOptions { 2 serializerOptions?: BuilderOptions; 3 wrapper?: Record<string, any>; 4}
You can use this function even before the plugin is initialized, in this case it would behave according to the default configuration.
The function takes one argument which is a json object and one optional type argument which specifies the type of that json, by default it is Record<string, any>
. It returns an xml string.
1export function buildXml<T = Record<string, any>>(json: T, options?: XmlBuilderOptions): string;
If you are familiar with Fastify this section should be interesting and informative for you, but also shuold help to understand what is heppening under the hood of the plugin and potentially contribute improvements or debug problems.
The plugin makes 6 modifications of your Fastify server instance:
setNotFoundHandler
method of a Fastify instance, which generates error using errorTranslator
passing not found error in it.onRequest
hook which executes request Content-Type
check and rejects unsupported media types (in case no Content-Type
header is present the check passes).addContentTypeParser
method of a Fastify instance.setErrorHandler
method of a Fastify instance, which uses the specified errorTranslator
in order to generate object representation of XML error response and serializes it into XML.setReplySerializer
method of a Fastify instance, which uses xml2js Builder in order to serialize response object.onSend
hook which adds the specified Content-Type
header to every response.No vulnerabilities found.
No security vulnerabilities found.