DynamoDB GetItem em Python (boto3)

get_item busca um item pela sua chave primária completa. O client de baixo nível do boto3 (boto3.client("dynamodb")) fala JSON do DynamoDB nas duas direções, então a chave vai envolvida com o seu tipo e o item volta do mesmo jeito. Como isso difere de query e scan está em ações baseadas em item.

Código

import boto3

client = boto3.client("dynamodb")

response = client.get_item(
    TableName="Music",
    Key={"Artist": {"S": "Arturo Sandoval"}, "SongTitle": {"S": "Cubano Chant"}},
    ProjectionExpression="#proj0, #proj1, #proj2, #proj3",
    ExpressionAttributeNames={"#proj0": "Artist", "#proj1": "SongTitle", "#proj2": "AlbumTitle", "#proj3": "Year"},
)

item = response.get("Item")
if item is None:
    print("Item not found")
else:
    print(item)

Explicação

Um item não encontrado retorna uma resposta sem nenhuma chave Item. Não None, não um dicionário vazio. Lendo a mesma tabela com uma chave que não existe, as chaves de nível superior da resposta foram exatamente estas:

['ResponseMetadata']

É por isso que o trecho usa response.get("Item"). response["Item"] levanta KeyError no caminho comum de não encontrado, que é como uma linha ausente vira um 500 em um handler web. Você continua sendo cobrado pela leitura: a página de capacidade de leitura da AWS afirma que "if you perform a read operation on an item that doesn't exist, DynamoDB will still consume read throughput as outlined above" (consultada em 2026-07-28).

Year é uma palavra reservada, e é por isso que o trecho gerado cria alias para todo atributo projetado. Tire os aliases #proj e passe ProjectionExpression="Year" e o motor rejeita a leitura:

ValidationException: Invalid ProjectionExpression: Attribute name is a reserved keyword; reserved keyword: Year

Criar alias incondicionalmente não custa nada e elimina a classe inteira de falha. A lista completa tem 573 palavras; veja "Attribute name is a reserved keyword".

Quatro formas de errar a Key, três mensagens diferentes. Vale saber distingui-las, porque nenhuma delas é o erro "provided key element does not match the schema" que as pessoas esperam. Reproduzido contra uma tabela Music chaveada em Artist (partição) + SongTitle (ordenação):

O que você passouMensagem literal do ValidationException
{"Artist": …} — chave de ordenação ausenteThe number of conditions on the keys is invalid
{"Artist": …, "SongTitle": …, "Extra": …}The number of conditions on the keys is invalid
{"Artist": …, "Song": …} — nome de atributo erradoOne of the required keys was not given a value
{"Artist": {"N": "1"}, …} — tipo erradoOne or more parameter values were invalid: Type mismatch for key

Repare que um atributo de chave ausente e um a mais produzem a mesma mensagem, então "number of conditions" quer dizer "você não me entregou exatamente o schema de chave", e não "você passou de menos".

ProjectionExpression corta o payload, não a conta. Lendo um item de ~15 KB de três jeitos com ReturnConsumedCapacity="TOTAL":

full item, eventually consistent          CapacityUnits: 2.0
ProjectionExpression="#y" (Year only)     CapacityUnits: 2.0
ProjectionExpression="#y" + ConsistentRead  CapacityUnits: 4.0

A projeção mudou a resposta de ~15 KB para um único número e mudou o custo em nada. A AWS diz isso sem rodeios: "The number of capacity units consumed will be the same whether you request all of the attributes (the default behavior) or just some of them (using a projection expression)" (Query API Reference, consultada em 2026-07-28). ConsistentRead=True é a única flag daquela lista que mexe no número, e ela o dobra. Veja expressões de projeção para o que as projeções realmente servem.

A API de resource é um contrato diferente, não uma grafia mais bonita. boto3.resource("dynamodb").Table("Music").get_item(...) retorna Python puro e todo número como decimal.Decimal:

{'Artist': 'Arturo Sandoval', 'AlbumTitle': 'Danzon', 'Awards': Decimal('0'), 'Year': Decimal('1994'), 'SongTitle': 'Cubano Chant'}

Isso corta dos dois lados. Escrever de volta pela mesma API com um float levanta erro antes mesmo de a requisição sair da sua máquina:

TypeError: Float types are not supported. Use Decimal types instead.

Se essa te pegar, "Float types are not supported" tem a correção. Misturar as duas APIs em uma mesma base de código é a armadilha de verdade: o client de baixo nível vai aceitar alegremente um {"N": "1.5"} que a API de resource teria rejeitado.

Erros chegam como exceções do botocore, e o boto3 lhes dá classes de verdade. Na 1.43.58 o objeto levantado para uma condição malsucedida é ConditionalCheckFailedException, uma subclasse de ClientError, então tanto except ClientError mais uma verificação de err.response["Error"]["Code"] quanto except client.exceptions.ConditionalCheckFailedException funcionam. Prefira o que a sua base de código já usa; não faça match em str(e).

Faça isso visualmente

Antes de criar aliases na mão: o verificador de palavras reservadas do DynamoDB gratuito pega os seus nomes de atributo, diz quais das 573 palavras reservadas você acertou e emite o mapa ExpressionAttributeNames pronto para colar.

Para navegar por tabelas e executar GetItem contra os seus próprios dados — formulário de chave, grade de resultados, copiar a requisição de volta como boto3 — baixe o DynoTable.

Guias relacionados

Referências

Reproduzido em 2026-07-28 no DynamoDB Local (amazon/dynamodb-local) na porta 9000 com boto3 1.43.58 / botocore 1.43.58. Toda mensagem e todo número de capacidade acima são saída do motor, copiados literalmente. O DynamoDB Local não é o serviço; onde se sabe que os dois redigem um erro de forma diferente, dizemos isso na página do erro.

Trabalhe com o DynamoDB sem o Console

Um cliente desktop rápido para DynamoDB que roda o SQL de verdade que o DynamoDB não consegue — JOINs, GROUP BY, agregações — com edição visual e um agente de IA com suas próprias chaves do Bedrock.

Teste grátis de 30 dias, sem cartão de crédito — depois o plano Grátis sem limite de tempo.