Skip to content
cloudemu
Services

§ Documentation

Database

In-memory NoSQL document/key-value store modelled on DynamoDB, Cosmos DB, and Firestore, driven with the real cloud SDKs

aws DynamoDBazr Cosmos DBgcp Firestore

Emulates a managed NoSQL store — the DynamoDB/Cosmos/Firestore table your app writes items to and queries by key. You create a table with a partition key (and optional sort key), then put, get, update, query, and scan schemaless items. Each item is just a map of attributes; the table indexes them by their key so lookups and range queries stay fast.

Reach for it when your code puts an item and reads it back by key, runs a query with a key condition, or scans with a filter — so you can exercise those paths without a real DynamoDB account. For managed relational databases (RDS/Aurora, Azure SQL, Cloud SQL), see Relational Database instead.

ProviderServiceSDK-compatDriver
AWSDynamoDB✓ Liveaws.DynamoDB
AzureCosmos DB✓ Liveazure.CosmosDB
GCPFirestore✓ Livegcp.Firestore

Drive it with the real SDK#

Run cloudemu as a server and point the real SDK at it, so your production data-access code runs unchanged. Start it with cloudemu serve (or docker run --rm -p 4566:4566 ghcr.io/stackshy/cloudemu), which serves the AWS API on http://localhost:4566:

cloudemu serve   # AWS API on http://localhost:4566

Then point the stock DynamoDB client at that endpoint — only the endpoint is redirected — and create a table keyed on id and write one item:

import (
    "github.com/aws/aws-sdk-go-v2/service/dynamodb"
    "github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
)

client := dynamodb.NewFromConfig(cfg, func(o *dynamodb.Options) {
    o.BaseEndpoint = aws.String("http://localhost:4566")
})

client.CreateTable(ctx, &dynamodb.CreateTableInput{
    TableName: aws.String("Users"),
    AttributeDefinitions: []types.AttributeDefinition{
        {AttributeName: aws.String("id"), AttributeType: types.ScalarAttributeTypeS},
    },
    KeySchema: []types.KeySchemaElement{
        {AttributeName: aws.String("id"), KeyType: types.KeyTypeHash},
    },
    BillingMode: types.BillingModePayPerRequest,
})

client.PutItem(ctx, &dynamodb.PutItemInput{
    TableName: aws.String("Users"),
    Item: map[string]types.AttributeValue{
        "id":   &types.AttributeValueMemberS{Value: "u1"},
        "name": &types.AttributeValueMemberS{Value: "Alice"},
    },
})

Same shape with azcosmos (Azure) and cloud.google.com/go/firestore (GCP). See the SDK-Compat Server page for the full quick starts.

In-process (Go unit tests)#

For Go unit tests written inside cloudemu-aware code, stand the same wire server up in-process with httptest.NewServer and point the client at ts.URL. The Drivers you pass name exactly the drivers this service needs:

import (
    "github.com/stackshy/cloudemu/v2"
    awsserver "github.com/stackshy/cloudemu/v2/server/aws"
)

cloud := cloudemu.NewAWS()
ts := httptest.NewServer(awsserver.New(awsserver.Drivers{DynamoDB: cloud.DynamoDB}))
defer ts.Close()
// point the same dynamodb client at ts.URL instead of the running endpoint

Call the driver directly#

For in-process setup or assertions you can skip the HTTP hop and call the driver. Items are plain map[string]any, so there's no attribute-value wrapping to deal with:

import dbdriver "github.com/stackshy/cloudemu/v2/services/database/driver"

aws.DynamoDB.CreateTable(ctx, dbdriver.TableConfig{
    Name: "Users", PartitionKey: "id",
})
aws.DynamoDB.PutItem(ctx, "Users", map[string]any{"id": "u1", "name": "Alice"})
item, _ := aws.DynamoDB.GetItem(ctx, "Users", map[string]any{"id": "u1"})

GetItem takes just the key attributes and returns the full item (or nil if absent). Query, Scan, UpdateItem, and batch/transactional writes take the same shape via dbdriver input structs.

Behavior & fidelity#

BehaviorWhat happens
Query and scan paginateResults come back in stable key order; pass a Limit and resume with an offset PageToken or a DynamoDB-style ExclusiveStartKey, and page tokens stay valid across calls.
Numeric-aware comparisonsNumbers order numerically ("10" > "9"), strings lexically, and bools false < true, matching the real services rather than naive string compare.
TTL expiry on readAn item past its TTL attribute reads as absent, so a Get or Query never returns expired data.
Streams / change feedWith streams enabled, writes emit INSERT / MODIFY / REMOVE records you can read back — useful for change-driven pipelines.
Global secondary indexesGSIs can be created, described, listed, and deleted, and a Query can target one via its IndexName.

Query & expression fidelity#

The expression and query grammars are evaluated for real in memory, not approximated — so a query with a real filter returns exactly the items the cloud would.

  • DynamoDBFilterExpression and ConditionExpression (a conditional write fails when its condition is false), KeyCondition with begins_with / BETWEEN, ProjectionExpression, and the full UpdateExpression grammar (ADD / DELETE, arithmetic, if_not_exists, list_append) with real set types.
  • Firestore — structured-query WHERE with OR / IN / array-contains / unary operators, plus orderBy, cursors, and select projection.
  • Cosmos DB — SQL query support over the document store.

SDK-compat — Live#

Real dynamodb, azcosmos, and Firestore clients drive it end-to-end — see SDK-Compat for the full operation list.

On this page

On this page