MySQL module for Testcontainers that enables programmatic creation and management of ephemeral MySQL database containers for testing Node.js applications.
npx @tessl/cli install tessl/npm-testcontainers--mysql@11.9.0Testcontainers MySQL is a specialized module that provides a fluent API for programmatically creating and managing ephemeral MySQL database containers for testing Node.js applications. It extends the base Testcontainers library with MySQL-specific configuration and connection management capabilities.
npm install @testcontainers/mysqlimport { MySqlContainer, StartedMySqlContainer } from "@testcontainers/mysql";For CommonJS:
const { MySqlContainer, StartedMySqlContainer } = require("@testcontainers/mysql");import { MySqlContainer } from "@testcontainers/mysql";
import { createConnection } from "mysql2/promise";
// Start a MySQL container with default settings
await using container = await new MySqlContainer("mysql:8.0").start();
// Connect using mysql2 client
const client = await createConnection({
host: container.getHost(),
port: container.getPort(),
database: container.getDatabase(),
user: container.getUsername(),
password: container.getUserPassword(),
});
// Execute queries
const [rows] = await client.execute("SELECT 1 as result");
console.log(rows); // [{ result: 1 }]
await client.end();
// Container automatically disposed with 'await using'The module consists of two main classes:
GenericContainer from testcontainers, providing MySQL-specific configuration methods in a fluent builder patternAbstractStartedContainer from testcontainers, representing a running container with access to MySQL-specific connection details and query executionThe module automatically configures MySQL environment variables, exposes port 3306, and provides convenient methods for obtaining connection details.
Configure and start a MySQL container with custom settings.
class MySqlContainer extends GenericContainer {
/**
* Create a new MySQL container configuration
* @param image - Docker image name (e.g., "mysql:8.0", "mysql:5.7")
*/
constructor(image: string);
/**
* Set the MySQL database name to be created
* @param database - Database name (default: "test")
* @returns this for method chaining
*/
withDatabase(database: string): this;
/**
* Set the MySQL username
* @param username - Username for the MySQL user (default: "test")
* @returns this for method chaining
*/
withUsername(username: string): this;
/**
* Set the MySQL user password
* @param userPassword - Password for the MySQL user (default: "test")
* @returns this for method chaining
*/
withUserPassword(userPassword: string): this;
/**
* Set the MySQL root password
* @param rootPassword - Root user password (default: "test")
* @returns this for method chaining
*/
withRootPassword(rootPassword: string): this;
/**
* Start the MySQL container
* @returns Promise resolving to a started container instance
*/
start(): Promise<StartedMySqlContainer>;
}Usage Examples:
// Custom database configuration
await using container = await new MySqlContainer("mysql:8.0")
.withDatabase("myapp_test")
.withUsername("app_user")
.withUserPassword("secret123")
.withRootPassword("rootsecret")
.start();
// Use URI-based connection
const connectionUri = container.getConnectionUri();
// mysql://app_user:secret123@localhost:32768/myapp_testMySqlContainer inherits extensive configuration capabilities from GenericContainer:
interface GenericContainer {
/** Configure exposed ports */
withExposedPorts(...ports: number[]): this;
/** Set startup timeout in milliseconds (default for MySQL: 120000) */
withStartupTimeout(startupTimeout: number): this;
/** Set additional environment variables */
withEnvironment(environment: Record<string, string>): this;
/** Configure wait strategy before considering container ready */
withWaitStrategy(waitStrategy: WaitStrategy): this;
/** Set network mode */
withNetworkMode(networkMode: string): this;
/** Copy files to container before start */
withCopyFilesToContainer(filesToCopy: FileToCopy[]): this;
/** Set container command */
withCommand(command: string[]): this;
/** Set container entrypoint */
withEntrypoint(entrypoint: string[]): this;
/** Configure bind mounts */
withBindMounts(bindMounts: BindMount[]): this;
/** Configure tmpfs mounts */
withTmpFs(tmpFs: TmpFs): this;
/** Configure container health check */
withHealthCheck(healthCheck: HealthCheck): this;
/** Set log consumer for container output */
withLogConsumer(consumer: (stream: Readable) => unknown): this;
/** Set container user */
withUser(user: string): this;
/** Set working directory */
withWorkingDir(workingDir: string): this;
/** Enable privileged mode */
withPrivilegedMode(): this;
/** Set container labels */
withLabels(labels: Labels): this;
/** Set container name */
withName(name: string): this;
/** Enable container reuse across test runs */
withReuse(): this;
/** Enable automatic container removal after stop (default behavior) */
withAutoRemove(): this;
/** Set image pull policy */
withPullPolicy(pullPolicy: ImagePullPolicy): this;
}Retrieve connection details from a started MySQL container.
class StartedMySqlContainer extends AbstractStartedContainer {
/**
* Get the host port mapped to MySQL's internal port 3306
* @returns The mapped port number
*/
getPort(): number;
/**
* Get the database name
* @returns Database name
*/
getDatabase(): string;
/**
* Get the MySQL username
* @returns Username
*/
getUsername(): string;
/**
* Get the MySQL user password
* @returns User password
*/
getUserPassword(): string;
/**
* Get the MySQL root password
* @returns Root password
*/
getRootPassword(): string;
/**
* Generate a MySQL connection URI
* @param isRoot - If true, use root credentials; if false, use user credentials (default: false)
* @returns Connection URI in format mysql://user:password@host:port/database
*/
getConnectionUri(isRoot?: boolean): string;
}Usage Examples:
await using container = await new MySqlContainer("mysql:8.0")
.withDatabase("testdb")
.withUsername("testuser")
.withUserPassword("testpass")
.start();
// Individual connection details
const host = container.getHost();
const port = container.getPort();
const database = container.getDatabase();
const username = container.getUsername();
const password = container.getUserPassword();
// Full connection URI
const userUri = container.getConnectionUri(); // Uses user credentials
const rootUri = container.getConnectionUri(true); // Uses root credentialsExecute SQL queries directly within the container using the MySQL CLI.
class StartedMySqlContainer {
/**
* Execute a SQL query inside the container using MySQL CLI
* @param query - SQL query to execute
* @param additionalFlags - Optional additional CLI flags (default: [])
* @param isRoot - If true, execute as root user; if false, use configured user (default: false)
* @returns Promise resolving to query output as string
* @throws Error if query execution fails (non-zero exit code)
*/
executeQuery(
query: string,
additionalFlags?: string[],
isRoot?: boolean
): Promise<string>;
}Usage Examples:
await using container = await new MySqlContainer("mysql:8.0").start();
// Basic query execution
const result = await container.executeQuery("SELECT 1 as result");
// Output: "result\n1\n"
// Execute with additional CLI flags
const jsonResult = await container.executeQuery(
"SELECT * FROM users",
["--json"]
);
// Execute as root user for admin operations
const adminResult = await container.executeQuery(
"SHOW DATABASES",
[],
true
);
// Error handling
try {
await container.executeQuery("INVALID SQL");
} catch (error) {
console.error("Query failed:", error.message);
}Manage the container lifecycle with inherited methods from AbstractStartedContainer.
class StartedMySqlContainer {
/**
* Stop the container
* @param options - Optional stop options
* @returns Promise resolving to stopped container
*/
stop(options?: Partial<StopOptions>): Promise<StoppedTestContainer>;
/**
* Restart the container
* @param options - Optional restart options
* @returns Promise that resolves when restart completes
*/
restart(options?: Partial<RestartOptions>): Promise<void>;
/**
* Get the container host address
* @returns Host address (typically "localhost")
*/
getHost(): string;
/**
* Get the container hostname
* @returns Container hostname
*/
getHostname(): string;
/**
* Get the container ID
* @returns Docker container ID
*/
getId(): string;
/**
* Get the container name
* @returns Container name
*/
getName(): string;
/**
* Get container labels
* @returns Container labels object
*/
getLabels(): Labels;
/**
* Async disposal for use with 'await using' syntax
*/
[Symbol.asyncDispose](): Promise<void>;
}Usage Examples:
// Manual lifecycle management
const container = await new MySqlContainer("mysql:8.0").start();
// Use the container
const client = await createConnection({
host: container.getHost(),
port: container.getPort(),
database: container.getDatabase(),
user: container.getUsername(),
password: container.getUserPassword(),
});
// Restart the container
await container.restart();
// Manual cleanup
await container.stop();
// Automatic disposal (recommended)
await using container2 = await new MySqlContainer("mysql:8.0").start();
// Container automatically stopped when leaving scopeAccess network-related information from a started container.
class StartedMySqlContainer {
/**
* Get the first mapped port
* @returns First mapped port number
*/
getFirstMappedPort(): number;
/**
* Get mapped port for a specific internal port
* @param port - Internal container port or port with protocol (e.g., "3306/tcp")
* @param protocol - Protocol ("tcp" or "udp"), defaults to "tcp"
* @returns Mapped host port
*/
getMappedPort(port: number, protocol?: string): number;
getMappedPort(portWithProtocol: `${number}/${"tcp" | "udp"}`): number;
/**
* Get network names the container is connected to
* @returns Array of network names
*/
getNetworkNames(): string[];
/**
* Get network ID for a specific network name
* @param networkName - Network name
* @returns Network ID
*/
getNetworkId(networkName: string): string;
/**
* Get container IP address in a specific network
* @param networkName - Network name
* @returns IP address in the specified network
*/
getIpAddress(networkName: string): string;
}Copy files and directories to/from the running container.
class StartedMySqlContainer {
/**
* Copy files to the running container
* @param filesToCopy - Array of file copy definitions
* @returns Promise that resolves when copy completes
*/
copyFilesToContainer(filesToCopy: FileToCopy[]): Promise<void>;
/**
* Copy directories to the running container
* @param directoriesToCopy - Array of directory copy definitions
* @returns Promise that resolves when copy completes
*/
copyDirectoriesToContainer(directoriesToCopy: DirectoryToCopy[]): Promise<void>;
/**
* Copy content to the running container
* @param contentsToCopy - Array of content copy definitions
* @returns Promise that resolves when copy completes
*/
copyContentToContainer(contentsToCopy: ContentToCopy[]): Promise<void>;
/**
* Copy tar archive to the container
* @param tar - Readable stream containing tar archive
* @param target - Target path in container (default: "/")
* @returns Promise that resolves when copy completes
*/
copyArchiveToContainer(tar: Readable, target?: string): Promise<void>;
/**
* Copy archive from the container
* @param path - Path in container to copy from
* @returns Promise resolving to readable stream of tar archive
*/
copyArchiveFromContainer(path: string): Promise<NodeJS.ReadableStream>;
}Execute commands and retrieve logs from the running container.
class StartedMySqlContainer {
/**
* Execute a command in the running container
* @param command - Command as string or array of strings
* @param opts - Optional execution options
* @returns Promise resolving to execution result
*/
exec(
command: string | string[],
opts?: Partial<ExecOptions>
): Promise<ExecResult>;
/**
* Get container logs
* @param opts - Optional log options (since, tail)
* @returns Promise resolving to readable stream of logs
*/
logs(opts?: { since?: number; tail?: number }): Promise<Readable>;
/**
* Commit the container to a new image
* @param options - Commit options
* @returns Promise resolving to new image ID
*/
commit(options: CommitOptions): Promise<string>;
}Usage Examples:
await using container = await new MySqlContainer("mysql:8.0").start();
// Execute arbitrary commands
const result = await container.exec(["ls", "-la", "/var/lib/mysql"]);
console.log(result.output);
// Get container logs
const logStream = await container.logs({ tail: 100 });
logStream.on("data", (chunk) => console.log(chunk.toString()));
// Copy SQL initialization file
await container.copyFilesToContainer([
{
source: "/path/to/init.sql",
target: "/docker-entrypoint-initdb.d/init.sql"
}
]);The following types are imported from the base testcontainers package and used in the API:
/** Options for stopping a container */
interface StopOptions {
timeout?: number;
remove?: boolean;
}
/** Options for restarting a container */
interface RestartOptions {
timeout?: number;
}
/** Result of command execution */
interface ExecResult {
output: string;
exitCode: number;
}
/** Options for command execution */
interface ExecOptions {
workingDir?: string;
env?: Record<string, string>;
user?: string;
privileged?: boolean;
cmd?: string[];
}
/** Options for committing a container to an image */
interface CommitOptions {
reference?: string;
comment?: string;
author?: string;
pause?: boolean;
changes?: string[];
}
/** File to copy to container */
interface FileToCopy {
source: string;
target: string;
mode?: number;
}
/** Directory to copy to container */
interface DirectoryToCopy {
source: string;
target: string;
mode?: number;
}
/** Content to copy to container */
interface ContentToCopy {
content: string | Buffer;
target: string;
mode?: number;
}
/** Container labels as key-value pairs */
type Labels = Record<string, string>;
/** Bind mount configuration */
interface BindMount {
source: string;
target: string;
mode?: "rw" | "ro";
}
/** TmpFs mount configuration */
type TmpFs = Record<string, string>;
/** Health check configuration */
interface HealthCheck {
test: string[];
interval?: number;
timeout?: number;
retries?: number;
startPeriod?: number;
}
/** Wait strategy interface */
interface WaitStrategy {
waitUntilReady(container: StartedTestContainer): Promise<void>;
}
/** Image pull policy */
interface ImagePullPolicy {
shouldPull(): boolean;
}
/** Stopped test container interface */
interface StoppedTestContainer {
getId(): string;
getName(): string;
}The executeQuery method throws an error if the SQL query fails:
try {
await container.executeQuery("SELECT * FROM nonexistent_table");
} catch (error) {
// Error message includes exit code and query
console.error(error.message);
// "executeQuery failed with exit code 1 for query: SELECT * FROM nonexistent_table"
}For connection errors with MySQL clients, use standard error handling for the respective client library (e.g., mysql2, mysql, etc.).