Skip to main content

Quick Start

Get up and running with Dynatable in 5 minutes. This guide will walk you through creating your first table and performing basic CRUD operations.

1. Define Your Schema

Create a schema file that defines your data models:

schema.ts
export const BlogSchema = {
format: 'dynatable:1.0.0',
version: '1.0.0',
indexes: {
primary: {
hash: 'PK',
sort: 'SK',
},
},
models: {
User: {
key: {
PK: { type: String, value: 'USER#${username}' },
SK: { type: String, value: 'USER#${username}' },
},
attributes: {
username: { type: String, required: true },
name: { type: String, required: true },
email: { type: String, required: true },
bio: { type: String },
},
},
Post: {
key: {
PK: { type: String, value: 'USER#${username}' },
SK: { type: String, value: 'POST#${postId}' },
},
attributes: {
username: { type: String, required: true },
postId: { type: String, generate: 'ulid' },
title: { type: String, required: true },
content: { type: String },
published: { type: Boolean, default: false },
},
},
},
params: {
timestamps: true, // Automatic createdAt and updatedAt as ISO strings
},
} as const;
tip

The as const at the end is crucial for proper TypeScript type inference.

2. Create the Table Instance

Initialize your table with the AWS DynamoDB client:

table.ts
import { Table } from '@ftschopp/dynatable-core';
import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
import { BlogSchema } from './schema';

export const table = new Table({
name: 'BlogTable',
client: new DynamoDBClient({
region: 'us-east-1',
}),
schema: BlogSchema,
});

3. Create the DynamoDB Table

Before using your table, you need to create it in DynamoDB:

setup-table.ts
import { CreateTableCommand, DynamoDBClient } from '@aws-sdk/client-dynamodb';

const client = new DynamoDBClient({ region: 'us-east-1' });

await client.send(
new CreateTableCommand({
TableName: 'BlogTable',
KeySchema: [
{ AttributeName: 'PK', KeyType: 'HASH' },
{ AttributeName: 'SK', KeyType: 'RANGE' },
],
AttributeDefinitions: [
{ AttributeName: 'PK', AttributeType: 'S' },
{ AttributeName: 'SK', AttributeType: 'S' },
],
BillingMode: 'PAY_PER_REQUEST',
})
);

console.log('Table created successfully!');
info

For local development, make sure DynamoDB Local is running first.

4. Perform CRUD Operations

Now you're ready to perform operations on your table!

Create (Put)

Add a new user:

const user = await table.entities.User.put({
username: 'alice',
name: 'Alice Smith',
email: 'alice@example.com',
bio: 'Software engineer and blogger',
}).execute();

console.log(user);
// {
// username: 'alice',
// name: 'Alice Smith',
// email: 'alice@example.com',
// bio: 'Software engineer and blogger',
// createdAt: '2024-01-15T10:00:00.000Z',
// updatedAt: '2024-01-15T10:00:00.000Z'
// }

Create a post (with auto-generated ID):

const post = await table.entities.Post.put({
username: 'alice',
title: 'My First Post',
content: 'Hello, World!',
published: true,
}).execute();

console.log(post.postId); // Auto-generated ULID

Read (Get)

Retrieve a specific user:

const user = await table.entities.User.get({
username: 'alice',
}).execute();

console.log(user.name); // 'Alice Smith'

Get a specific post:

const post = await table.entities.Post.get({
username: 'alice',
postId: '01HMQ7X8Y2K3N4M5P6Q7R8S9T0',
}).execute();

Update

Update a user's bio:

await table.entities.User.update({
username: 'alice',
})
.set('bio', 'Senior software engineer and tech blogger')
.execute();

Increment a counter:

await table.entities.User.update({
username: 'alice',
})
.add('postCount', 1)
.execute();

Delete

Delete a post:

await table.entities.Post.delete({
username: 'alice',
postId: '01HMQ7X8Y2K3N4M5P6Q7R8S9T0',
}).execute();

5. Query Data

Find all posts by a user:

const posts = await table.entities.Post.query()
.where((attr, op) => op.eq(attr.username, 'alice'))
.execute();

console.log(posts.length); // Number of posts

Query with filtering and sorting:

const recentPosts = await table.entities.Post.query()
.where((attr, op) => op.and(op.eq(attr.username, 'alice'), op.eq(attr.published, true)))
.limit(10)
.scanIndexForward(false) // Most recent first
.execute();

6. Pagination

Handle large result sets with pagination:

const result = await table.entities.Post.query()
.where((attr, op) => op.eq(attr.username, 'alice'))
.limit(20)
.executeWithPagination();

console.log(result.items); // First 20 posts
console.log(result.lastEvaluatedKey); // Key for next page

// Get next page
if (result.lastEvaluatedKey) {
const nextPage = await table.entities.Post.query()
.where((attr, op) => op.eq(attr.username, 'alice'))
.limit(20)
.startFrom(result.lastEvaluatedKey)
.executeWithPagination();
}

Complete Example

Here's a complete working example:

example.ts
import { Table } from '@ftschopp/dynatable-core';
import { DynamoDBClient } from '@aws-sdk/client-dynamodb';

const BlogSchema = {
format: 'dynatable:1.0.0',
version: '1.0.0',
indexes: {
primary: { hash: 'PK', sort: 'SK' },
},
models: {
User: {
key: {
PK: { type: String, value: 'USER#${username}' },
SK: { type: String, value: 'USER#${username}' },
},
attributes: {
username: { type: String, required: true },
name: { type: String, required: true },
email: { type: String, required: true },
},
},
},
params: {
timestamps: true,
},
} as const;

const table = new Table({
name: 'BlogTable',
client: new DynamoDBClient({ region: 'us-east-1' }),
schema: BlogSchema,
});

async function main() {
// Create
await table.entities.User.put({
username: 'alice',
name: 'Alice Smith',
email: 'alice@example.com',
}).execute();

// Read
const user = await table.entities.User.get({
username: 'alice',
}).execute();

console.log(user);

// Update
await table.entities.User.update({ username: 'alice' }).set('name', 'Alice Johnson').execute();

// Delete
await table.entities.User.delete({
username: 'alice',
}).execute();
}

main();

What's Next?

Now that you've completed the quick start, dive deeper into specific topics: