Meefik's Blog

Freedom and Open Source

URI parser in JavaScript

20 Oct 2024 | javascript

In this post, I show a lightweight JavaScript approach to parse a connection string URI like MongoDB connection string. The code breaks down the URI into its components, including the scheme, credentials, hosts, endpoint, and options.

Input:

mongodb://user:pass@host1:27017,host2:27017/db?option1=value1&option2=value2

Output:

{
  "scheme": "mongodb",
  "username": "user",
  "password": "pass",
  "hosts": [ { "host": "host1", "port": 27017 }, { "host": "host2", "port": 27017 } ],
  "endpoint": "db",
  "options": { "option1": "value1", "option2": "value2" }
}

I like to use simple and useful own code instead of using external models. So I prepared the URI parser code on pure JavaScript, here it is.

So here is the source code:

/**
 * Parses URI address.
 *
 * @param {string} addresses The address(es) to process.
 * @returns {Array<Object>} Parsed addresses.
 */
function parseAddress(addresses) {
  return addresses.split(',').map((address) => {
    const i = address.indexOf(':');
    return i >= 0
      ? {
          host: decodeURIComponent(address.substring(0, i)),
          port: +address.substring(i + 1),
        }
      : { host: decodeURIComponent(address) };
  });
}

/**
 * Parses URI options.
 *
 * @param {string} options The options to process.
 * @returns {Object} Parsed options.
 */
function parseOptions(options) {
  const result = {};
  options.split('&').forEach((option) => {
    const i = option.indexOf('=');
    if (i >= 0) {
      result[decodeURIComponent(option.substring(0, i))] = decodeURIComponent(option.substring(i + 1));
    }
  });
  return result;
}

/**
 * Takes a connection string URI of form:
 *
 *   scheme://[username[:password]@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[endpoint]][?options]
 *
 * and returns an object of form:
 *
 *   {
 *     scheme: string,
 *     username?: string,
 *     password?: string,
 *     hosts: [ { host: string, port?: number }, ... ],
 *     endpoint?: string,
 *     options?: object
 *   }
 *
 * @param {string} uri The connection string URI.
 * @returns {Object} Parsed URI object.
 */
function parseURI(uri) {
  if (!uri) return;
  const connectionStringParser = new RegExp(
    '^\\s*' +                  // Optional whitespace at the beginning
    '([^:]+)://' +             // Scheme (Group 1)
    '(?:([^:@,/?&]+)(?::([^:@,/?&]+))?@)?' + // Username (Group 2) and Password (Group 3)
    '([^@/?&]+)' +             // Host address(es) (Group 4)
    '(?:/([^:@,/?&]+)?)?' +     // Endpoint (Group 5)
    '(?:\\?([^:@,/?]+)?)?' +    // Options (Group 6)
    '\\s*$',                   // Optional whitespace at the end
    'gi'
  );
  const connectionStringObject = {};
  const tokens = connectionStringParser.exec(uri);
  if (Array.isArray(tokens)) {
    connectionStringObject.scheme = tokens[1];
    connectionStringObject.username = tokens[2] ? decodeURIComponent(tokens[2]) : tokens[2];
    connectionStringObject.password = tokens[3] ? decodeURIComponent(tokens[3]) : tokens[3];
    connectionStringObject.hosts = parseAddress(tokens[4]);
    connectionStringObject.endpoint = tokens[5] ? decodeURIComponent(tokens[5]) : tokens[5];
    connectionStringObject.options = tokens[6] ? parseOptions(tokens[6]) : tokens[6];
  }
  return connectionStringObject;
}

Use the parseURI() function to parse URI string. Look at the code example:

// MongoDB connection string as example
const uri = 'mongodb://user:pass@host1:27017,host2:27017/db?option1=value1&option2=value2';
// parse this string
const params = parseURI(uri);
// show parsed parameters
console.log(params);
// {
//   scheme: 'mongodb',
//   username: 'user',
//   password: 'pass',
//   hosts: [ { host: 'host1', port: 27017 }, { host: 'host2', port: 27017 } ],
//   endpoint: 'db',
//   options: { option1: 'value1', option2: 'value2' }
// }

This simple yet powerful parser efficiently breaks down a URI connection string into usable components. It’s especially useful when dealing with multiple hosts or various options in connection strings. Experiment with different URIs to see its in action.

Comments