@existdb/node-exist

中文文档
8.0.0发布于 2026/09/13MITES Module构建来源已验证近 30 天下载 3000

promise based interaction with eXist DB's XML-RPC API

原始使用文档
文档目录23 节
  1. Install
  2. Use
  3. REST
  4. XML-RPC
  5. Connection Options
  6. RESTClient with defaults
  7. XmlRpcClient with defaults
  8. Examples
  9. Read options from environment
  10. Components
  11. Queries
  12. Documents
  13. Resources
  14. Collections
  15. App
  16. Indices
  17. Users
  18. server
  19. Command Line Scripts
  20. Test
  21. Roadmap
  22. Compatibility
  23. Disclaimer

node-exist

semantic release status js-standard-style

Mostly a shallow wrapper for eXist's XML-RPC API and eXist's REST API. Attempts to translate terminologies into node world. Uses promises.

Install

npm install @existdb/node-exist

NOTE: If you are looking for a command line client have a look at xst

Use

In addition to eXist-db's XML-RPC API you do now also have the option to leverage the potential of its REST-API. This allows to choose the best tool for any particular task. Both APIs are used in combination in the upload example.

NOTE: This package is now ESM only.

REST

Status: unstable

NOTE: eXist-db's REST-API has its on methodology and available options may differ between instances. Especially, the ability to download the source code of XQuery files is prohibited by default (_source=yes is only availabe if enabled in descriptor.xml). For details of available options for each method please see the REST-API documentation

First, we need an instance of the restClient.

import { getRestClient } from '@existdb/node-exist'
const rc = getRestClient()

For more information see also Connection Options.

The returned HTTP client has 4 methods

  • post(query, path[, options]): execute query in the context of path. The query is expected to be a XQuery main module and will be wrapped in the XML-fragment that exist expects.

    const { bodyText } = await rc.post('count(//p)', '/db')
    console.log(bodyText)
    
  • put(data, path): create resources in the database. If sub-collections in path are missing, they will be created for you. The server will respond with StatusCode 400, Bad Request, for not-well- formed XML resources. In this case, the body contains a detailed description of the validation error.

    await rc.put('<root />', '/db/rest-test/test.xml')
    
  • get(path [, options][, writableStream]): read data from the database. The response body will contain the contents of the resource or a file listing, if the provided path is a collection. If a writableStream is passed in, the response body will be streamed into it.

    const { bodyText } = await rc.get('/db/rest-test/test.xml')
    console.log(bodyText)
    
  • del(path): remove resources and collections from an existdb instance

    await rc.del('/db/rest-test/test.xml')
    

Have a look at the rest-client example. The REST-client uses the Got library and works with streams and generators. Look at the rest tests to see examples.

XML-RPC

Creating, reading and removing a collection:

import { getXmlRpcClient } from '@existdb/node-exist'
const db = getXmlRpcClient()

db.collections.create('/db/apps/test')
  .then(result => db.collections.describe('/db/apps/test'))
  .then(result => console.log('collection description:', result))
  .catch(e => console.error('fail', e))

Uploading an XML file into the database

import { getXmlRpcClient } from '@existdb/node-exist'
const db = getXmlRpcClient()

db.documents.upload(Buffer.from('<root/>'))
  .then(fileHandle => db.documents.parseLocal(fileHandle, '/db/apps/test/file.xml'))
  .then(result => db.documents.read('/db/apps/test/file.xml'))
  .then(result => console.log('test file contents', result))
  .catch(error => console.error('fail', error))

Since all interactions with the database are promises you can also use async functions

import { getXmlRpcClient } from '@existdb/node-exist'
const db = getXmlRpcClient()

async function uploadAndParse (filePath, contents) {
  const fileHandle = await db.documents.upload(contents)
  await db.documents.parseLocal(fileHandle, filePath, {})
  return filePath
}

try {
  const filePath = await uploadAndParse('/db/apps/test-file.xml', Buffer.from('<root/>'))
  console.log("uploaded", filePath)
} catch (error) {
  console.error(error)
}

You can also have a look at the examples for more use-cases.

Connection Options

In the previous section you learned that there are two APIs you can use to interact with an exist-db instance.

Both client constructor functions do accept an option argument of type NodeExistConnectionOptions. Calling them without arguments, as in the examples above will use default options.

{
  basic_auth: {
    user: 'guest',
    pass: 'guest'
  },
  protocol: 'https:',
  host: 'localhost',
  port: '8443',
  path: '/exist/rest'|'/exist/xmlrpc'
}

NOTE: The path property, the endpoint to reach an API, is different for REST ('/exist/rest') and XML-RPC ('/exist/xmlrpc'). You most likely do not need to change it. However, if you need to you can override those.

RESTClient with defaults

import { getRestClient } from '@existdb/node-exist'
const rest = getRestClient({
  basic_auth: {
    user: 'guest',
    pass: 'guest'
  },
  protocol: 'https:',
  host: 'localhost',
  port: '8443',
  path: '/exist/rest'
})

XmlRpcClient with defaults

import {getXmlRpcClient} from '@existdb/node-exist'
const db = getXmlRpcClient({
  basic_auth: {
    user: 'guest',
    pass: 'guest'
  },
  protocol: 'https:',
  host: 'localhost',
  port: '8443',
  path: '/exist/xmlrpc'
})

Examples

  • Connect as someone else than guest

    {
      basic_auth: {
        user: 'me',
        pass: '1 troubadour artisanal #compost'
      }
    }
    
  • Connect to a local development server using HTTP

    {
      protocol: 'http:',
      port: 8080
    }
    
  • Connect to a server with an invalid or expired certificate.

    {
      host: 'here.be.dragons',
      rejectUnauthorized: false
    }
    

    NOTE: For remote hosts this is considered bad practice as it does only offer a false sense of security. For hosts considered local - localhost, 127.0.0.1 and [::1] - this is set automatically, because it is impossible to have trusted certificates for local hosts.

  • Give up on requests that take longer than ten minutes

    {
      timeout: 600000
    }
    

    timeout is in milliseconds and applies both to waiting for the response headers and to the pause between two chunks of the response body. The default, 0, waits indefinitely, so long running queries are not cut off by the client. Queries and REST post accept a timeout in their options that overrides it for a single call. Package operations (db.app.*) never time out.

Read options from environment

readOptionsFromEnv offers a comfortable way to read the connection options from a set of environment variables

variable namedefaultdescription
EXISTDB_USERnonethe user used to connect to the database and to execute queries with
EXISTDB_PASSnonethe password to authenticate the user against the database
EXISTDB_SERVERhttps://localhost:8443the URL of the database instance to connect to (only http and https protocol allowed)
EXISTDB_TIMEOUTnone (waits indefinitely)milliseconds before a request times out, 0 waits indefinitely

NOTE: In order to connect to an instance as a user other than guest both EXISTDB_USER and EXISTDB_PASS have to be set!

import {getXmlRpcClient, getRestClient, readOptionsFromEnv} from '@existdb/node-exist'
const db = getXmlRpcClient(readOptionsFromEnv())
const rest = getRestClient(readOptionsFromEnv())

For more details you can have a look how it is used in the connection script that is used for testing and in all example scripts.

Components

The XML-RPC commands are grouped into components by what they operate on. Every method returns a promise.

Queries

Status: working

execute, read and readAll accept a timeout in options, in milliseconds (0 waits indefinitely). It overrides the connection timeout for this query only and is not sent to the database.

db.queries.readAll(query, { variables, timeout: 0 })

execute

db.queries.execute(query, options)

read

db.queries.read(query, options)

readAll

This convenience function calls queries.count then retrieves all result pages and returns them in an array.

db.queries.readAll(query, options)

Example:

const query = `xquery version "3.1";
xmldb:get-child-collections($collection)
  => string-join(",\n")
`
const options = { variables: collection: "/db/apps" }

db.queries.readAll(query, options)
  .then(result => {
    const response = Buffer.concat(result.pages).toString() 
    console.log(response)
  })
  .catch(error => console.error(error))

count

db.queries.count(resultHandle)

retrieve

db.queries.retrieveResult(resultHandle, page)

retrieveAll

db.queries.retrieveAll(resultHandle)

releaseResult

free result on server

db.queries.releaseResult(resultHandle)

Documents

A document can be seen as a file. It might be indexed if it's type is not binary.

upload

Resolves into a file handle which can then be used by db.documents.parseLocal.

db.documents.upload(Buffer.from('test'))

parseLocal

db.documents.parseLocal(fileHandle, 'foo/test.txt')

read

Reads resources stored as XML (XMLResource). You can control how they are serialized by setting serialization options in the options parameter.

Use default serialization options.

db.documents.read('foo.xml')

Force XML declaration to be returned.

db.documents.read('foo.xml', { "omit-xml-declaration": "no" })

Force the file to end in a blank line (available since eXist-db v6.0.1).

db.documents.read('foo.xml', { "insert-final-newline": "yes" })

readBinary

Reads resources stored as binary (BinaryResource) inside existdb such as XQuery, textfiles, PDFs, CSS, images and the like.

db.documents.readBinary('foo.xml')

remove

db.documents.remove('foo.xml')

Resources

Status: working

A resource is identified by its path in the database. Documents and collections are resources.

describe

db.resources.describe(resourcePath)

setPermissions

db.resources.setPermissions(resourcePath, 400)

getPermissions

db.resources.getPermissions(resourcePath)

Collections

Status: working

create

db.collections.create(collectionPath)

remove

db.collections.remove(collectionPath)

describe

db.collections.describe(collectionPath)

read

db.collections.read(collectionPath)

exists

This function checks if the collection exists.

  • returns true if the collection exists and the current user can open it
  • returns false if the collection does not exist
  • throws an exception if the collection exists but the current user cannot access it
db.collections.exists(collectionPath)

existsAndCanOpen

This function checks if the collection exists and if it does, if the current user can access it.

  • returns true if the collection exists and the current user can open it
  • returns false if the collection does not exist
  • throws an exception if the collection exists but the current user cannot access it
db.collections.existsAndCanOpen(collectionPath)

App

Status: working

upload

After uploading a XAR you can install it

db.app.upload(xarBuffer, xarName)

Example:

const xarContents = fs.readFileSync('spec/files/test-app.xar')

db.app.upload(xarContents, 'test-app.xar')
  .then(result => console.log(result))
  .catch(error => console.error(error))

install

Install an uploaded XAR (this will call repo:install-and-deploy-from-db). For extra safety a previously installed version will be removed before installing the new version.

Dependencies will be resolved from http://exist-db.org/exist/apps/public-repo by default. If you want to use a different repository provide the optional customPackageRepoUrl.

db.app.install(xarName[, customPackageRepoUrl])

Example:

db.app.install('test-app.xar')
  .then(result => console.log(result))
  .catch(error => console.error(error))

Returns

{
  "success": true,
  "result": {
    "update": false, // true if a previous version was found
    "target": "/db/apps/test-app"
  }
}

Error

{
  success: false,
  error: Error
}

remove

Uninstall and remove the application identified by its namespace URL. If no app with packageUri could be found then this counts as success.

db.app.remove(packageUri)

Example:

db.app.remove('http://exist-db.org/apps/test-app')
  .then(result => console.log(result))
  .catch(error => console.error(error))

Returns

{ success: true }

Error

{
  success: false,
  error: Object | Error
}

packageCollection

The path to the collection where node-exist will upload packages to (/db/pkgtmp). Useful for cleanup after succesful installation.

Example:

db.documents.remove(`${db.app.packageCollection}/test-app.xar`)

Indices

Status: TODO

Users

Status: working

getUserInfo

Will return the information about the given user.

db.users.getUserInfo(username)

Example:

db.users.getUserInfo('admin')

Returns:

{
    uid: 1048574,
    'default-group-id': 1048575,
    umask: 18,
    metadata: {
      'http://exist-db.org/security/description': 'System Administrator',
      'http://axschema.org/namePerson': 'admin'
    },
    'default-group-name': 'dba',
    'default-group-realmId': 'exist',
    name: 'admin',
    groups: [ 'dba' ],
    enabled: 'true'
}

list

db.users.list()

Returns an array of user info objects (see getUserInfo()).

server

Status: working

version

Query the eXist-db version running on the server. Returns the SemVer version as a string (e.g. 5.4.1 or 6.1.0-SNAPSHOT).

db.server.version()

syncToDisk

db.server.syncToDisk()

shutdown

db.server.shutdown()

Note: There is no way to bring it up again.

Command Line Scripts

You can use this library to build a command line interface that interacts with existdb instances. A few basic examples how to do this are included in this repository.

Example:

spec/examples/exist-ls /db/apps

NOTE: Have a look at xst for a CLI client built with node-exist.

Test

All tests are in spec/tests and written for tape

npm test

NOTE: You can override connection settings with environment variables. See examples for more information.

To execute a single run using a different server you can also just define the variable directly:

EXISTDB_SERVER=http://localhost:8888 npm test

Roadmap

  • switch to use eXist-db's REST-API (available through rest-client)
  • refactor to ES6 modules
  • better type hints

Compatibility

node-exist is tested to be compatible with eXist-db 4, 5 and 6. It should be compatible with version 3, except for the XAR installation.

Disclaimer

Use at your own risk.

This software is safe for development. It may be used to work with a production instance, but think twice before your data is lost.

版本与发布记录

@existdb/node-exist 共发布 48 个版本,首次发布于 2018/07/26;过去 12 个月发布了 3 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
8.0.0latest2026/09/132 个月前
7.0.02026/01/289 个月前
6.0.02026/01/179 个月前
5.5.22025/04/011.6 年前
5.5.12023/11/152 年前
5.5.02023/10/213 年前

关于 @existdb/node-exist 的常见问题

@existdb/node-exist 是什么?

promise based interaction with eXist DB's XML-RPC API

如何安装 @existdb/node-exist?

在项目目录运行 npm install @existdb/node-exist,或使用 pnpm add / yarn add / bun add。

@existdb/node-exist 支持 TypeScript 吗?

该包的 package.json 没有声明类型入口。需要类型提示时,可以在 npm 上查找社区维护的 @types/existdb__node-exist 是否存在。

@existdb/node-exist 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@existdb/node-exist 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=22.19.0。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@existdb/node-exist 可以商用吗?

@existdb/node-exist 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@existdb/node-exist 的下载量有多少?

根据 npm 官方下载统计,@existdb/node-exist 在 2026/10/14 – 2026/11/12 这 30 天内被下载 3,000 次,日均约 100 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@existdb/node-exist 还在维护吗?

最新版本 8.0.0 发布于 2026/09/13(2 个月前);至今共发布 48 个版本,其中过去 12 个月发布了 3 个;首次发布于 2018/07/26。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。