RDS Data API
Protocol: REST JSON
Endpoint: POST http://localhost:4566/{operation}
Backing data plane: Local RDS MySQL / MariaDB / PostgreSQL containers
Floci implements the AWS RDS Data API routes used by AWS SDK clients and executes raw SQL against local RDS resources created through the RDS emulator. It supports MySQL, MariaDB, and PostgreSQL resources for local development workflows that already use ExecuteStatement and transactions.
For the upstream API shape, see the AWS RDS Data API documentation:
- Using the Data API for Aurora DB clusters
- Data API operations
ExecuteStatementBeginTransactionCommitTransactionRollbackTransactionBatchExecuteStatement
Supported Actions
| Action | Route | Required request fields | Description |
|---|---|---|---|
ExecuteStatement |
POST /Execute |
resourceArn, secretArn, sql |
Execute raw SQL against a local RDS cluster or instance |
BeginTransaction |
POST /BeginTransaction |
resourceArn, secretArn |
Open a JDBC transaction and return a transaction ID |
CommitTransaction |
POST /CommitTransaction |
resourceArn, secretArn, transactionId |
Commit an open transaction |
RollbackTransaction |
POST /RollbackTransaction |
resourceArn, secretArn, transactionId |
Roll back an open transaction |
BatchExecuteStatement |
POST /BatchExecute |
resourceArn, secretArn, sql |
Run one SQL statement once per entry in parameterSets |
The deprecated ExecuteSql operation is recognized at POST /ExecuteSql and returns an AWS-style BadRequestException.
Compatibility Notes
resourceArnandsecretArnare required on Data API requests.resourceArnmust identify an existing local RDS cluster or instance.databaseis optional when the resolved RDS resource has a database name; otherwise it must be provided. TransactionalExecuteStatementrequests must use the same database as the active transaction whendatabaseis present.- Transaction requests validate
resourceArnagainst the active transaction resource. Floci resolves accepted ARN aliases to the local resource before comparing transaction identity. - MySQL, MariaDB, and PostgreSQL resources are supported. Aurora PostgreSQL resources resolve to the same PostgreSQL execution path.
- SQL is sent directly to the local database engine through JDBC.
SqlParameterbinding is supported for all engines: named:placeholdermarkers are rewritten to positional JDBC bind parameters and executed through aPreparedStatement. Colons inside string literals, quoted/backtick identifiers, comments, PostgreSQL::casts, and PostgreSQL dollar-quoted strings are left untouched. A placeholder used more than once binds its value at each position. Supported value variants arestringValue,booleanValue,longValue,doubleValue,blobValue, andisNull;typeHintvaluesDECIMAL,TIMESTAMP,DATE,TIME,UUID, andJSONare honored.arrayValueparameters are not supported yet and returnBadRequestException, as do malformedparameterspayloads (not a JSON array, or an entry missingname). - Result records include Data API field variants such as
stringValue,longValue,blobValue,booleanValue,doubleValue, andisNull.recordsis returned only for statements that produce a result set — empty when the query matched no rows — and is omitted entirely from the response to a statement that reports an update count, as AWS does. AnINSERT,UPDATE,DELETE, or DDL statement therefore answers withnumberOfRecordsUpdatedandgeneratedFieldsand norecordsfield at all. includeResultMetadatareturns the full AWSColumnMetadatashape per column:label,name,type,typeName,tableName,schemaName,nullable,precision,scale,isSigned,isCaseSensitive,isCurrency,isAutoIncrement, andarrayBaseColumnType.labelis the result-set label (the alias when the query aliases a column) andnameis the underlying column name, falling back to the label for computed columns — clients that hydrate rows bylabelneed it present.typeis a JDBC type code andtypeNameis the engine's own type name (for example4andint4for a PostgreSQL integer), both taken from the driver so they match the engine actually serving the query.arrayBaseColumnTypeis always0because array columns are not mapped toarrayValuefields yet.ExecuteStatementreturnsgeneratedFieldsfor statements that report an update count on MySQL and MariaDB, carrying the auto-increment keys the engine generated. As on Aurora PostgreSQL, PostgreSQL resources always return an empty list — use aRETURNINGclause to read generated values.generatedFieldsis omitted for statements that return a result set.BatchExecuteStatementruns the statement once per entry inparameterSetsthrough a JDBC batch and returns oneupdateResultsentry per set, each carrying thegeneratedFieldsthat set produced under the same engine rules asExecuteStatement— a set inserting several rows reports one field per generated key. On MySQL and MariaDB the sets are executed one at a time so each entry owns its own keys, becausegetGeneratedKeys()after a JDBC batch reports every key the batch generated with nothing tying a key back to the set that produced it. It acceptstransactionId, so a batch can take part in a Data API transaction; without one the batch commits automatically. An absent or emptyparameterSetsruns nothing and returns an emptyupdateResults: AWS runs the statement once per parameter set provided and points a caller who wants a single parameterless execution at one empty set ([[]], which runs once here) or atExecuteStatement. A statement that returns rows rather than an update count — aSELECT, or a CTE ending in one — is rejected withBadRequestExceptionbefore any set runs, because a batch reports oneUpdateResultper set and has nowhere to carry a result set, and AWS documents the operation as taking a DML statement. The check reads the driver's description of the prepared statement, so nothing executes first, and a DML statement that also reports rows through a PostgreSQLRETURNINGclause is left alone: the batch still performs its writes, andgeneratedFieldsis empty on PostgreSQL anyway.- A PostgreSQL result set with a
point,box,circle,line,lseg,path,polygon,interval, ormoneycolumn is rejected withUnsupportedResultException, as on Aurora PostgreSQL. Cast the column to text to read it. - SQL errors are returned as
DatabaseErrorExceptionso AWS SDK callers can handle database failures with normal AWS error decoding. - If
secretArnpoints to a local Secrets Manager secret with JSON credentials (usernameoruser, pluspassword), those credentials are used. If the secret is missing or cannot be parsed, Floci falls back to the resolved RDS resource's master credentials for local development convenience. formatRecordsAs=JSON,formattedRecords, andresultSetOptionsare not implemented yet, andExecuteStatementrequests asking for those result modes returnBadRequestException. They areExecuteStatement-only fields in the AWS API, soBatchExecuteStatement— whose input is limited toresourceArn,secretArn,sql,database,schema,parameterSets, andtransactionId— ignores them as AWS does rather than rejecting them.- RDS
HttpEndpointEnabledcontrol-plane gating is not modeled locally; availability is controlled byFLOCI_SERVICES_RDS_DATA_ENABLEDand whether the target local RDS resource is running.
Configuration
| Variable | Default | Description |
|---|---|---|
FLOCI_SERVICES_RDS_DATA_ENABLED |
true |
Enable or disable the RDS Data API service |
FLOCI_SERVICES_RDS_DATA_TRANSACTION_TTL_SECONDS |
180 |
Idle timeout, in seconds, before leaked Data API transactions expire |
The RDS Data API also requires the RDS service itself to be enabled because it resolves resourceArn values to local RDS containers.
Example
export AWS_ENDPOINT_URL=http://localhost:4566
aws rds create-db-cluster \
--db-cluster-identifier appdb \
--engine aurora-mysql \
--master-username admin \
--master-user-password secret123 \
--database-name app \
--endpoint-url "$AWS_ENDPOINT_URL"
RESOURCE_ARN=$(aws rds describe-db-clusters \
--db-cluster-identifier appdb \
--query 'DBClusters[0].DBClusterArn' \
--output text \
--endpoint-url "$AWS_ENDPOINT_URL")
SECRET_ARN=$(aws secretsmanager create-secret \
--name appdb/data-api \
--secret-string '{"username":"admin","password":"secret123"}' \
--query ARN \
--output text \
--endpoint-url "$AWS_ENDPOINT_URL")
aws rds-data execute-statement \
--resource-arn "$RESOURCE_ARN" \
--secret-arn "$SECRET_ARN" \
--database app \
--sql "select 1 as count" \
--include-result-metadata \
--endpoint-url "$AWS_ENDPOINT_URL"
aws rds-data batch-execute-statement \
--resource-arn "$RESOURCE_ARN" \
--secret-arn "$SECRET_ARN" \
--database app \
--sql "insert into items (id, title) values (:id, :title)" \
--parameter-sets '[
[{"name":"id","value":{"longValue":1}},{"name":"title","value":{"stringValue":"first"}}],
[{"name":"id","value":{"longValue":2}},{"name":"title","value":{"stringValue":"second"}}]
]' \
--endpoint-url "$AWS_ENDPOINT_URL"