RDS and Aurora feel confusing from the SDK until you separate two jobs that live in the same service. One job is running the database machine. The other is running queries against it. The AWS SDK owns the first and, with one deliberate exception, has nothing to do with the second.
In DynamoDB the SDK is the database - every read and write is an SDK call. RDS is the opposite. The SDK builds and operates a PostgreSQL or MySQL server for you, then steps aside while your application talks to it with the same driver it would use anywhere else.
This page builds the mental model behind that split, so every other page in this section reads as an application of one idea.
RDS and Aurora divide into a control plane and a data plane: the SDK operates the control plane (create, resize, back up, replicate, fail over), while your app owns the data plane (connect, query, transact).
Insight: the @aws-sdk/client-rds client never runs your SQL. It manages the database as infrastructure and hands you a hostname; a normal driver like psycopg2 or pg does the querying.
Key Concepts:control plane vs data plane, DB instance, Aurora cluster, writer/reader endpoint, waiter, the Aurora Data API exception.
When to Use: any time you provision or operate relational databases from code - environment automation, tooling, or learning the RDS control plane before wrapping it in IaC.
Limitations/Trade-offs: provisioning is slow and asynchronous (minutes, not milliseconds), and you must manage connection state, credentials, and networking that DynamoDB hides entirely.
Related Topics: provisioning instances and clusters, snapshots, failover and replicas, the Aurora Data API, and RDS Proxy.
Start with the single distinction the whole service is organized around.
The control plane is the set of operations that manage the database as a piece of infrastructure. Creating an instance, changing its size, enabling backups, adding a read replica, triggering a failover, taking a snapshot - these are control-plane actions, and every one of them is an AWS SDK call on the rds client. They are administrative, relatively rare, and often slow because real compute and storage are being provisioned behind them.
The data plane is the set of operations that read and write your actual data. SELECT, INSERT, UPDATE, a transaction across three tables - these run over a database connection, using a driver that speaks the PostgreSQL or MySQL wire protocol. The AWS SDK is not involved. Your app connects to a hostname and port and behaves exactly as it would against a database on any other host.
Here is the control-plane half - the SDK provisioning a database and returning where it lives. Notice that nothing here runs a query.
# --- Python (boto3) ---import boto3rds = boto3.client("rds", region_name="us-east-1")# Control plane: provision the database (this is ALL the SDK does here).rds.create_db_instance( DBInstanceIdentifier="app-db", Engine="postgres", DBInstanceClass="db.t3.micro", AllocatedStorage=20, MasterUsername="appuser", ManageMasterUserPassword=True, # password stored in Secrets Manager)print("provisioning app-db - the SDK's job ends at the endpoint")
// --- TypeScript (AWS SDK v3) ---import { RDSClient, CreateDBInstanceCommand } from "@aws-sdk/client-rds";const rds = new RDSClient({ region: "us-east-1" });// Control plane: provision the database (this is ALL the SDK does here).await rds.send(new CreateDBInstanceCommand({ DBInstanceIdentifier: "app-db", Engine: "postgres", DBInstanceClass: "db.t3.micro", AllocatedStorage: 20, MasterUsername: "appuser", ManageMasterUserPassword: true, // password stored in Secrets Manager}));console.log("provisioning app-db - the SDK's job ends at the endpoint");
Once that instance is available, your application connects with a driver. That connection is the data plane, and it never touches the AWS SDK.
Two shapes exist inside the control plane, and knowing which one you are working with is the second half of the model.
A DB instance is a single managed database server. CreateDBInstance provisions one machine with its own storage and one endpoint. Turning on MultiAZ gives you a hidden standby in another Availability Zone that AWS promotes automatically if the primary fails - but you still address a single endpoint, and the standby does not serve reads. This is the classic RDS shape for PostgreSQL, MySQL, MariaDB, Oracle, and SQL Server.
An Aurora cluster is a different topology. You call CreateDBCluster to create the cluster - which owns the shared, auto-replicated storage volume - and then CreateDBInstance one or more times with that DBClusterIdentifier to add compute. One instance becomes the writer; the rest are readers. The cluster exposes two DNS names: a cluster (writer) endpoint that always points at the current writer, and a reader endpoint that load-balances across replicas. Your app sends writes to one and read-only queries to the other.
Because provisioning takes minutes, the control plane is asynchronous. The create call returns immediately with the database in a creating state; the endpoint is not usable yet. The SDK provides waiters - db_instance_available and db_cluster_available - that poll DescribeDBInstances / DescribeDBClusters until the resource is ready. You block on the waiter, then read the endpoint.
# --- Python (boto3) ---# Wait for the async provisioning to finish, THEN read the endpoint.rds.get_waiter("db_instance_available").wait(DBInstanceIdentifier="app-db")inst = rds.describe_db_instances(DBInstanceIdentifier="app-db")["DBInstances"][0]print(inst["Endpoint"]["Address"], inst["Endpoint"]["Port"])
// --- TypeScript (AWS SDK v3) ---import { DescribeDBInstancesCommand, waitUntilDBInstanceAvailable } from "@aws-sdk/client-rds";// Wait for the async provisioning to finish, THEN read the endpoint.await waitUntilDBInstanceAvailable({ client: rds, maxWaitTime: 900 }, { DBInstanceIdentifier: "app-db" });const out = await rds.send(new DescribeDBInstancesCommand({ DBInstanceIdentifier: "app-db" }));const ep = out.DBInstances?.[0].Endpoint;console.log(ep?.Address, ep?.Port);
Credentials sit at the boundary between the two planes. The SDK sets up the master user, but the safe path is ManageMasterUserPassword, which stores the password in AWS Secrets Manager instead of your code. Your data-plane driver then fetches that secret at connect time. IAM database authentication is another option, letting the driver present a short-lived token instead of a password.
The model has one deliberate exception, and it is the reason Aurora Serverless feels different.
The Aurora Data API collapses the two planes into one. Through the @aws-sdk/client-rds-data client, you call ExecuteStatement with a SQL string, the cluster's ARN, and a Secrets Manager ARN - and AWS runs the query over HTTPS and returns rows in the SDK response. There is no driver, no connection, no pool. It is the one case where the AWS SDK does run your SQL. That makes it a fit for Lambda and other short-lived, high-fan-out callers that would otherwise exhaust a connection limit, at the cost of higher per-call latency than a warm driver connection. It has its own page in this section.
For everything that stays on the driver, connection management becomes your responsibility - the concern DynamoDB never imposes. Databases have a fixed connection limit, and serverless functions that each open their own connection can exhaust it quickly. RDS Proxy answers this by sitting between your app and the database as a managed connection pool, authenticated through Secrets Manager. It, too, has its own page.
The habit to internalize: before you write RDS code, decide which plane you are in. If you are creating, resizing, backing up, replicating, or failing over, you are on the control plane and the answer is an rds client call. If you are reading or writing rows, you are on the data plane and the answer is a driver - unless you have deliberately chosen the Data API. Almost every question in this section resolves the moment you name the plane.
"The AWS SDK runs my SQL queries against RDS." - It does not, except through the Aurora Data API. The rds client provisions and manages the database; a normal driver runs your queries over the endpoint.
"CreateDBInstance works the same for Aurora." - Aurora needs CreateDBCluster first (it owns the storage), then CreateDBInstance calls that attach compute as a writer and readers.
"Multi-AZ gives me a read replica." - A Multi-AZ standby is a failover target, not a read endpoint. It does not serve queries; use read replicas or Aurora reader instances for that.
"The database is ready when create returns." - Provisioning is asynchronous and takes minutes. Wait on db_instance_available / db_cluster_available before using the endpoint.
"I should put the master password in my code or config." - Use ManageMasterUserPassword (Secrets Manager) or IAM database authentication so no long-lived password lives in your source.
What is the difference between the control plane and the data plane in RDS?
The control plane manages the database as infrastructure - create, resize, back up, replicate, fail over - and every action is an AWS SDK call on the rds client. The data plane reads and writes your rows and runs over a normal SQL driver, not the SDK.
Does the AWS SDK run my SQL queries?
No, with one exception. For a standard instance or cluster, the SDK only provisions and manages the database; your app queries it with a driver like psycopg2 or pg. The single exception is the Aurora Data API, which runs SQL over HTTPS through the SDK.
How is provisioning an Aurora cluster different from a plain instance?
A plain instance is one CreateDBInstance call. Aurora needs CreateDBCluster to create the shared storage volume, then one or more CreateDBInstance calls with that cluster id to attach compute - the first becomes the writer, the rest readers.
Why do I need a waiter after creating a database?
Provisioning is asynchronous and takes several minutes. The create call returns with the database still in a creating state and no usable endpoint. The db_instance_available / db_cluster_available waiters poll until it is ready.
What is the writer endpoint versus the reader endpoint?
An Aurora cluster exposes both. The writer (cluster) endpoint always points at the current writer instance for reads and writes. The reader endpoint load-balances read-only connections across the replica instances.
Where should the database password live?
Not in your code. Use ManageMasterUserPassword so RDS stores and rotates the master password in AWS Secrets Manager, or use IAM database authentication so the driver presents a short-lived token instead of a password.
When would I use the Aurora Data API instead of a driver?
When you have many short-lived callers - Lambda functions, for example - that would otherwise open and exhaust database connections. The Data API is a connectionless HTTPS interface, at the cost of higher per-call latency than a warm driver connection.
Does Multi-AZ let me spread read traffic?
No. A Multi-AZ standby exists only to take over on failover and does not serve queries. For read scaling, create read replicas (RDS) or add reader instances and use the reader endpoint (Aurora).