Store user-generated data
- How-to
- 4 minutes
If your app allows the user to save user-generated data that needs to be available across multiple user sessions, our document service is your best option. One example of such user-defined data is to-do items that the user enters in a Todo app. This guide explains how you can store, update, and delete data from the document service.
In the following examples, you'll see two code snippets for each type of operation.
- The React hook version uses the
@dynatrace-sdk/react-hooks
package for all the operations. You can use this in your React frontend code. - The Client SDK version uses the
@dynatrace-sdk/client-document
) package for all the operations. The example code shown uses this package in an app function. You can use the same code in Notebook, Dashboard, and Workflow as well.
Create a document
To create a document, use the useCreateDocument
hook. This hook provides an execute
function that you can use to create a new document. It also provides some additional fields with information about the loading status, errors, and the response. These fields are common to all the document hooks. The example below shows how to create a Todo list document:
- React Hook
- Client SDK
import React from 'react';
import { useCreateDocument } from '@dynatrace-sdk/react-hooks';
import { Button } from '@dynatrace/strato-components/buttons';
import { Page } from '@dynatrace/strato-components-preview/layouts';
const todos = [
{
title: 'Send email to John about vacation',
done: false,
},
{
title: 'Plan workshop for next week',
done: false,
},
];
export const App = () => {
const { execute, data, error, isLoading } = useCreateDocument();
const handleClick = () => {
execute({
body: {
name: 'My Todos',
type: 'TodoList',
content: new Blob([JSON.stringify(todos)], {
type: 'application/json',
}),
},
});
};
return (
<Page>
<Page.Main>
<Button onClick={handleClick}>Create Todos</Button>
</Page.Main>
</Page>
);
};
import { documentsClient } from '@dynatrace-sdk/client-document';
const todos = [
{ title: 'Send email to John about vacation', done: false },
{ title: 'Plan workshop for next week', done: false },
];
export default async function (payload: unknown = undefined) {
try {
const data = await documentsClient.createDocument({
body: {
name: 'My Todos',
type: 'TodoList',
content: new Blob([JSON.stringify(todos)], {
type: 'application/json',
}),
},
});
return data ? 'document created successfully' : 'document creation failed';
} catch (error) {
console.error('Error creating document:', error);
return 'document creation failed due to an error';
}
}
This operation requires the scope document:documents:write
. Read more about scopes in this guide.
List documents
The useListDocuments
hook lets you to get a filtered list of the available documents. Besides the common fields, this hook also provides a refetch
function to refetch the list of documents. The following code fetches the documents of type TodoList.
- React Hook
- Client SDK
import React from 'react';
import { useListDocuments } from '@dynatrace-sdk/react-hooks';
import { Button } from '@dynatrace/strato-components/buttons';
import { List, Text } from '@dynatrace/strato-components/typography';
import { Page } from '@dynatrace/strato-components-preview/layouts';
export const App = () => {
const { data, refetch } = useListDocuments({
filter: `type == 'TodoList'`,
});
const handleClick = () => {
refetch().then((list) => console.log(list.documents));
};
return (
<Page>
<Page.Main>
{data && (
<List>
{data.documents.map((doc) => (
<Text key={doc.id}>{doc.name}</Text>
))}
</List>
)}
<Button onClick={handleClick}>Refetch Todos</Button>
</Page.Main>
</Page>
);
};
import { documentsClient } from '@dynatrace-sdk/client-document';
export default async function (payload: unknown = undefined) {
try {
const list = await documentsClient.listDocuments({
filter: `type == 'TodoList'`,
});
return list?.documents ?? [];
} catch (error) {
console.error('Error fetching TodoList documents:', error);
return [];
}
}
This operation requires the scope document:documents:read
. Read more about scopes in this guide.
Get document content
To retrieve the content of a specific document, you can use the useDownloadDocument
hook. This hook expects the document ID as a parameter and returns binary data with the document's contents, which you can extract by calling the get
function. This example shows how to fetch the content for the Todo list document that we created in the first step:
- React Hook
- Client SDK
import React, { useState, useEffect } from 'react';
import { useDownloadDocument } from '@dynatrace-sdk/react-hooks';
import { Page } from '@dynatrace/strato-components-preview/layouts';
import { Checkbox } from '@dynatrace/strato-components-preview/forms';
interface Todo {
title: string;
done: boolean;
}
export const App = () => {
const [todos, setTodos] = useState<Todo[]>();
const { data } = useDownloadDocument({
id: 'ff96e29d-2622-48f0-8c88-b2f20b2cf532',
});
useEffect(() => {
if (data) {
data.get('json').then((todos) => {
setTodos(todos);
});
}
}, [data]);
return (
<Page>
<Page.Main>
{todos &&
todos.map((todo) => (
<Checkbox key={todo.title} name="done" defaultValue={todo.done}>
{todo.title}
</Checkbox>
))}
</Page.Main>
</Page>
);
};
import { documentsClient } from '@dynatrace-sdk/client-document';
export default async function (payload: unknown = undefined) {
try {
const documentContent = await documentsClient.downloadDocumentContent({
id: 'ff96e29d-2622-48f0-8c88-b2f20b2cf532',
});
const jsonContent = documentContent?.get('json');
if (!jsonContent) {
console.warn('No JSON content found in the document.');
return null;
}
return jsonContent;
} catch (error) {
console.error('Error downloading document content:', error);
return null;
}
}
This operation requires the scope document:documents:read
. Read more about scopes in this guide.
Update document
Whenever you need to change the contents of a document, use the useUpdateDocument
or the useUpdateDocumentMetadata
hooks.
For all modifying operations, keep optimistic locking in mind. Every document has a version that's incremented whenever its metadata or content is modified. For every modifying operation, you have to pass this exact version, which you can retrieve with the useDocumentMetadata
hook. It could look like the following:
- React Hook
- Client SDK
import React from 'react';
import { useDocumentMetaData, useUpdateDocument } from '@dynatrace-sdk/react-hooks';
import { Button } from '@dynatrace/strato-components/buttons';
import { Page } from '@dynatrace/strato-components-preview/layouts';
const updatedTodos = [
{
title: 'Send e-mail to John concerning vacation',
done: true,
},
{
title: 'Plan workshop for next week',
done: false,
},
];
export const App = () => {
const metadata = useDocumentMetaData({
id: 'ff96e29d-2622-48f0-8c88-b2f20b2cf532',
});
const { execute } = useUpdateDocument();
const handleClick = () => {
if (metadata.data) {
execute({
id: metadata.data.id,
optimisticLockingVersion: metadata.data.version,
body: {
content: new Blob([JSON.stringify(updatedTodos)], {
type: 'application/json',
}),
},
});
}
};
return (
<Page>
<Page.Main>
<Button onClick={handleClick}>Update Todos</Button>
</Page.Main>
</Page>
);
};
import { documentsClient } from '@dynatrace-sdk/client-document';
export default async function (payload: unknown = undefined) {
const updatedTodos = [
{
title: 'Send e-mail to John concerning vacation',
done: true,
},
{
title: 'Plan workshop for next week',
done: false,
},
];
try {
const metadata = await documentsClient.getDocumentMetadata({
id: 'ff96e29d-2622-48f0-8c88-b2f20b2cf532',
});
if (!metadata) {
console.warn('No metadata found for the document.');
return 'document updation failed';
}
const data = await documentsClient.updateDocumentContent({
id: metadata.id,
optimisticLockingVersion: metadata.version,
body: {
content: new Blob([JSON.stringify(updatedTodos)], {
type: 'application/json',
}),
},
});
return data ? 'document updated successfully' : 'document updation failed';
} catch (error) {
console.error('Error updating document:', error);
return 'document updation failed';
}
}
This operation requires the scope document:documents:write
. Read more about scopes in this guide.
Delete document
You can delete documents as well. To do so, use the useDeleteDocument
hook. To delete a document, you need to provide the optimistic locking version of the document you want to delete.
To delete the document we created in the first step, your code should look like this:
- React Hook
- Client SDK
import React from 'react';
import { useDocumentMetaData, useDeleteDocument } from '@dynatrace-sdk/react-hooks';
import { Button } from '@dynatrace/strato-components/buttons';
import { Page } from '@dynatrace/strato-components-preview/layouts';
export const App = () => {
const metadata = useDocumentMetaData({
id: 'ff96e29d-2622-48f0-8c88-b2f20b2cf532',
});
const { execute } = useDeleteDocument();
const handleClick = () => {
if (metadata.data) {
execute({
id: metadata.data.id,
optimisticLockingVersion: metadata.data.version,
});
}
};
return (
<Page>
<Page.Main>
<Button onClick={handleClick}>Delete Todos</Button>
</Page.Main>
</Page>
);
};
import { documentsClient } from '@dynatrace-sdk/client-document';
export default async function (payload: unknown = undefined) {
try {
const metadata = await documentsClient.getDocumentMetadata({
id: 'ff96e29d-2622-48f0-8c88-b2f20b2cf532',
});
if (!metadata) {
console.warn('No metadata found for the document.');
return 'document deletion failed';
}
await documentsClient.deleteDocument({
id: metadata.id,
optimisticLockingVersion: metadata.version,
});
return 'document successfully delete';
} catch (error) {
console.error('Error deleting document:', error);
return 'document deletion failed';
}
}
This operation requires the scope document:documents:delete
. Read more about scopes in this guide.