Basic FTP Client for Node.js
basic-ftp is a robust and actively maintained FTP/FTPS client library designed specifically for Node.js environments. Currently stable at version 5.3.0, it demonstrates a consistent release cadence with frequent patch and minor updates addressing bugs and security enhancements, as evidenced by recent 5.x releases. A key differentiator is its modern Promise-based API, leveraging `async/await` for asynchronous operations, alongside native TypeScript support for improved developer experience and type safety. The library provides comprehensive features including FTPS over TLS for secure connections, IPv6 support, and convenient methods for performing directory-level operations like uploading and downloading entire folders. It explicitly supports Passive Mode but does not support Active Mode. Users are strongly advised to prefer FTPS (FTP over TLS) for any security-sensitive transfers, or ideally, alternative protocols like HTTPS or SFTP, as plain FTP is an inherently insecure and older protocol. The library maintains a lean dependency tree, requiring only Node.js 10.0 or later.
Common errors
-
Error: Client has been closed
cause Attempting to perform an operation (e.g., upload, list) on a `Client` instance after `client.close()` has been called or after a connection error/timeout has occurred. A client cannot be reused after being closed.fixIf you need to reconnect or perform new operations, create a new `Client` instance. If an error or timeout occurs, `basic-ftp` automatically closes the client, and you must instantiate a new one to reconnect. -
TypeError: (0 , basic_ftp__WEBPACK_IMPORTED_MODULE_0__.Client) is not a constructor
cause This error typically occurs in bundled environments (like Webpack, Rollup, Vite) or when mixing CommonJS `require` with ESM `import` syntax, where the bundler incorrectly handles the module import for `basic-ftp`.fixEnsure your project's `package.json` correctly defines `"type": "module"` for ESM, or use CommonJS `const { Client } = require('basic-ftp')` if not using ESM. Check your bundler configuration to correctly transpile or resolve module imports for `basic-ftp`. -
Error: 530 Login authentication failed
cause The username or password provided in the `client.access()` method is incorrect, or the user lacks the necessary permissions on the FTP server.fixDouble-check your `host`, `user`, and `password` credentials. Verify them against the FTP server's configuration or by attempting to log in with another client. Ensure the user has appropriate directory access. -
Error: connect ECONNREFUSED <IP_ADDRESS>:<PORT>
cause The client failed to establish a TCP connection to the specified FTP host and port. This could be due to an incorrect host/port, the server being offline, a firewall blocking the connection, or network issues.fixVerify the `host` and `port` (default 21 for FTP, 990 for implicit FTPS) in your `client.access()` options. Check if the FTP server is running and accessible from your network. Inspect any firewalls (local or network) that might be blocking the connection.
Warnings
- breaking Version 5.3.0 introduced an upper bound on the total bytes of directory listing data to mitigate a security vulnerability (GHSA-rp42-5vxx-qpwr). Large listings might now be truncated by default. An option to increase this limit (`Client` constructor) was added concurrently.
- breaking Versions 5.2.1 and 5.2.2 addressed critical security advisories (GHSA-chqc-8p9q-pq6q, GHSA-6v7q-wjvx-w8wg) by rejecting control character injection attempts in paths and improving control character rejection, respectively. This might cause previously 'working' but insecure path manipulations to now fail.
- gotcha FTP is an old and inherently insecure protocol. Users are strongly advised to use FTPS (FTP over TLS) by setting `secure: true` in the `client.access()` options, or to prefer alternative, more secure protocols like SFTP or HTTPS for data transfer, especially for sensitive information. Plain FTP does not provide any encryption.
- gotcha The `allowSeparateTransferHost` option in the `Client` constructor defaults to `true` for backwards compatibility. This allows the server to instruct the client to use a different IP address for data transfers, which can be a security risk (FTP bounce attacks) or cause issues in NAT environments.
- gotcha basic-ftp does not support Active Mode FTP. It exclusively operates in Passive Mode (and EPSV).
Install
-
npm install basic-ftp -
yarn add basic-ftp -
pnpm add basic-ftp
Imports
- Client
const { Client } = require('basic-ftp')import { Client } from 'basic-ftp' - Client (CommonJS)
import { Client } from 'basic-ftp'const { Client } = require('basic-ftp')
Quickstart
import { Client } from 'basic-ftp';
import fs from 'node:fs';
const FTP_HOST = process.env.FTP_HOST ?? 'myftpserver.com';
const FTP_USER = process.env.FTP_USER ?? 'very';
const FTP_PASSWORD = process.env.FTP_PASSWORD ?? 'password';
// Create a dummy README.md for the example
fs.writeFileSync('README.md', '# Local README\n\nThis is a test file for basic-ftp example.');
async function runFtpExample() {
const client = new Client();
client.ftp.verbose = true; // Enable verbose logging
try {
await client.access({
host: FTP_HOST,
user: FTP_USER,
password: FTP_PASSWORD,
secure: true // Use FTPS over TLS
});
console.log('Connected and logged in. Remote directory listing:');
console.log(await client.list());
const remoteFileName = 'README_FTP.md';
const localFileName = 'README.md';
const copiedFileName = 'README_COPY.md';
console.log(`Uploading ${localFileName} to ${remoteFileName}...`);
await client.uploadFrom(localFileName, remoteFileName);
console.log('Upload complete.');
console.log(`Downloading ${remoteFileName} to ${copiedFileName}...`);
await client.downloadTo(copiedFileName, remoteFileName);
console.log('Download complete.');
// Clean up local dummy file
fs.unlinkSync(localFileName);
fs.unlinkSync(copiedFileName);
} catch (err) {
console.error('FTP Operation failed:', err);
} finally {
if (!client.closed) {
client.close();
console.log('FTP client closed.');
}
}
}
runFtpExample();