Tutorial: Migrar um aplicativo React Native para o PowerSync
Nesta página
- Fase 1: preparação e configuração
- Implemente um Atlas cluster
- Adicionar IPs PowerSync à Lista de Acesso IP
- Importar dados de amostra
- Configurar o PowerSync
- Ver dados sincronizados
- Fase 2: preparar o aplicativo Realm para migração
- Clonar projeto inicial
- Refatorar o projeto de Device Sync apenas para local
- Executar e verificar alterações
- Fase 3: Migrar do Realm para o cliente PowerSync
- Instalar dependências
- Migrar esquema de dados
- Refatorar o código do aplicativo
- Executar e verificar alterações
- Fase 4: Sincronizar dados do Atlas para o PowerSync
- Configuração inicial
- Refatorar o código do cliente
- Executar e verificar alterações
- Fase 5: implementar API de backend
- Inspecionar conector
- Implementar método de upload
- Criar Servidor de Backend
- Executar e verificar alterações
- Fase 6: retoques finais e limpeza
- Implemente a alternância Mostrar tudo (opcional)
- Implementar alternância do modo offline (opcional)
- Projeto de limpeza
- Conclusão e próximas etapas
A partir de 2024 de setembro, o Atlas Device SDKs (Realm), Device Sync e App Services foram descontinuados. Isso significa que os usuários desses serviços terão que migrar para outra solução até 2025 de setembro. Se precisar de mais tempo, entre em contato com o suporte.
O PowerSync é uma das principais alternativas ao Atlas Device Sync. É uma solução baseada em SQLite e pode ser a solução certa para migrar se você tiver um aplicação móvel usando o Device Sync.
Este tutorial guiará você pelas etapas necessárias para migrar um aplicação móvel do Device Sync , escrito no React Native, para o PowerSync. Seus dados de backend permanecerão no Atlas, então você precisará configurar o serviço PowerSync, atualizar os esquemas e vinculações do banco de dados local e configurar um serviço de backend para gravar no Atlas.
Este tutorial usa um aplicação de lista de tarefas do Realm para o React Native disponível no 2repositório de exemplo do powersync Realm.
Fase 1: preparação e configuração
Implemente um Atlas cluster
Primeiro, você precisa implantar um Atlas Cluster e inserir alguns dados de teste. Isto irá orientá-lo como se estivesse configurando o Atlas pela primeira vez. Se você já tiver um cluster implantado, fique à vontade para pular.
Navegue até MongoDB Atlas e registre-se para uma conta Atlas , ou entre se você já tiver uma conta.
Em seguida, crie um cluster.
Para fins de teste, selecione o cluster M0 (grátis) com as configurações padrão. Sinta-se à vontade para fazer quaisquer alterações adicionais para atender às suas necessidades.
Clique em Criar sistema.
Você retorna ao seu painel. O modal Conectar ao cluster é exibido automaticamente.
Clique em Escolher um método de conexão e selecione Drivers.
Nesta tela, copie a URL que exibe na etapa 3.
Adicione a string de conexão ao código do seu aplicação . Esta é a sua string de conexão; é necessário acessar sua instância MongoDB . Salve a string de conexão para referência futura.
Você criará um nome de usuário e uma senha nas próximas etapas que a instância do PowerSync usará para se conectar ao banco de dados.
Clique em Concluído para fechar o modal.
Após a conclusão da implantação do cluster, seu dashboard deverá ter a seguinte aparência.
Clique em Adicionar dados para criar um novo banco de dados.
No cartão Criar um Banco de Dados no Atlas , clique em INICIAR.
Crie um banco de dados chamado PowerSync e uma collection chamada Item e clique em Criar banco de dados.
Você retorna ao painel e deve ver o banco de dados e a coleção recém-criados:
Finalmente, você precisa criar um novo usuário que o PowerSync usará para se conectar a esse banco de dados.
Na barra lateral esquerda, clique em Acesso ao banco de dados no cabeçalho Segurança.
Clique em Add New Database User, crie um novo usuário chamado
powersync
e forneça uma senha. Observe o nome de usuário e a senha a serem usados na string de conexão que você copiou anteriormente.Observação
Se o seu nome de usuário ou senha contiver qualquer um dos seguintes caracteres especiais, você deverá convertê-los em um formato seguro de URL para sua string de conexão:
$
,:
,/
,?
,!
,#
,[
,]
,@
. Você pode fazer isso manualmente ou usar um aplicação de codificação de URL , como urtencoder.org.Na seção Privilégios do usuário do banco de dados, clique em Adicionar privilégio específico e adicione um privilégio para
readWrite
dbAdmin
um role e um para o banco de dados PowerSync.Clique em Adicionar Usuário.
Você deve ver o usuário recém-criado com as permissões de banco de dados necessárias.
Para obter mais detalhes sobre permissões de usuário, consulte a seção MongoDB do guia Configuração do banco de dados de origem PowerSync.
Adicionar IPs PowerSync à Lista de Acesso IP
Para que o PowerSync acesse o banco de dados em execução no Atlas, é necessário adicionar os endereços IP do serviço à lista de acesso IP. Esses endereços IP estão listados na documentação Segurança da PowerSync e Filtragem de IP.
Na barra lateral esquerda, clique em Acesso à rede no cabeçalho Segurança.
Clique em + Adicionar Endereço IP e insira o endereço IP. Para melhor ajudar qualquer pessoa a administrar essa lista no futuro, também recomendamos inserir PowerSync como o comentário opcional.
Clique em Confirmar e repita para cada IP.
Importar dados de amostra
Atualize os espaços reservados na string de conexão que você copiou anteriormente com o nome de usuário e a senha do usuário de banco de dados.
Nesta etapa, você importará alguns dados de amostra que serão utilizados para sincronizar dados em etapas futuras.
Primeiro, instale o MongoDB Database Tools para obter acesso ao mongoimport
. Consulte as instruções do Guia de instalação do seu sistema operacional.
Depois de instalar o database-tools
, digite o seguinte no terminal para confirmar que você pode acessar o mongoimport
:
mongoimport --version
Isso deve retornar a versão da ferramenta. Consulte o Guia de instalação acima se estiver tendo problemas.
Em seguida, crie um arquivo JSON denominado sample.json
com o seguinte conteúdo:
[ { "isComplete": false, "summary": "Complete project documentation", "owner_id": "mockUserId" }, { "isComplete": true, "summary": "Buy groceries", "owner_id": "mockUserId" }, { "isComplete": false, "summary": "Schedule dentist appointment", "owner_id": "mockUserId" }, { "isComplete": false, "summary": "Prepare presentation for next week", "owner_id": "mockUserId" }, { "isComplete": true, "summary": "Pay utility bills", "owner_id": "mockUserId" }, { "isComplete": false, "summary": "Fix bug in login system", "owner_id": "mockUserId2" }, { "isComplete": false, "summary": "Call mom", "owner_id": "mockUserId" }, { "isComplete": true, "summary": "Submit expense reports", "owner_id": "mockUserId2" }, { "isComplete": false, "summary": "Plan team building event", "owner_id": "mockUserId2" }, { "isComplete": false, "summary": "Review pull requests", "owner_id": "mockUserId2" } ]
Estes dados de amostra contêm alguns itens da lista de tarefas. O owner_id
será usado para exemplos de filtragem mais tarde neste tutorial.
Para importar esse JSON, digite o seguinte comando, substituindo o espaço reservado <connection-string>
por sua string de conexão:
mongoimport --uri="<connection-string>" --db=PowerSync --collection=Item --file=sample.json --jsonArray
Você deve ver a seguinte mensagem:
10 document(s) imported successfully. 0 document(s) failed to import.
Caso contrário, confirme se seus parâmetros de comando (incluindo string de conexão) estão corretos e se seu usuário do Atlas tem o acesso correto ao banco de dados .
Você pode visualizar e gerenciar os documentos inseridos navegando até sua coleção na UI do Atlas ou usando o aplicação de desktop visual MongoDB Compass . Para exibir e gerenciar seu banco de dados e collections por meio do MongoDB Compass, você deve se conectar usando a mesma string de conexão.

Configurar o PowerSync
Agora navegue até PowerSync e registre-se ou faça login.
Se você estiver fazendo login pela primeira vez, precisará criar uma nova instância para começar.
Crie uma nova instância denominada TodoList
.

Selecione MongoDB como banco de dados de conexão .

Utilize sua string de conexão do Atlas para preencher as configurações de conexão.
Importante
Use uma versão reduzida da string de conexão que não contenha seu nome de usuário, senha ou outros parâmetros de URL . Por exemplo, sua conexão será parecida com mongodb+srv://m0cluster.h6folge.mongodb.net/
.
Insira o nome do banco de dados ("PowerSync"), o nome de usuário ("Powersync") e a senha que você atribuiu a esta conta em uma etapa anterior.

Clique em Testar conexão para garantir que você possa se conectar com sucesso.
Se você vir o erro a seguir, confirme se todos os IPs de serviço PowerSync necessários estão em sua lista de acesso IP do Atlas .

Se você ainda estiver tendo problemas, consulte o Guia de conexão do banco de dados PowerSync para conexões MongoDB .
Clique em Avançar para implementar sua nova instância do PowerSync. Isso pode levar alguns minutos para ser concluído.
Depois que sua instância for implantada, você pode garantir que pode visualizar os dados migrados criando algumas regras básicas de sincronização.
Primeiro, remova as regras de sincronização padrão e substitua-as pelo seguinte:
bucket_definitions: user_buckets: parameters: SELECT request.user_id() as user_id data: - SELECT _id as id, * FROM "Item" WHERE bucket.user_id = 'global' OR owner_id = bucket.user_id
Para que os itens sejam sincronizados corretamente com o serviço PowerSync, observe o seguinte:
O
_id
deve ser mapeado paraid
.O nome da collection "Item" deve ser colocado entre aspas. Isso ocorre porque o nome da nossa coleção começa com uma letra maiúscula.
Os blocos específicos do usuário devem corresponder a um
user_id
deglobal
, que fornece acesso a todo o banco de dados. Caso contrário, você corresponderá aouser_id
fornecido, que será recuperado do token de autenticação.
Observe que as regras do PowerSync Sync são um tópico bastante detalhado. Para saber mais, você pode conferir este blog post sobre Regras de Sincronização ou a documentação sobre as Regras de Sincronização do PowerSync.
Clique em Salvar e implantar. Mais uma vez, pode levar alguns minutos para que o sistema seja concluído.
Após a conclusão do sistema, você verá o seguinte:

Após a conclusão do sistema, você deverá ver o status apropriado.
Se você receber algum erro, verifique se o PowerSync
usuário está configurado com as permissões listadas na documentação de configuração do banco de dados de origem do PowerSync.
Clique em Gerenciar instâncias para revisar as regras de sincronização e o status do sistema.
Ver dados sincronizados
Para finalizar esta configuração, você usará o Aplicativo de Diagnóstico PowerSync para visualizar os itens da lista de tarefas que acabou de criar e adicionar às suas regras de sincronização. Para usar essa ferramenta, primeiro você precisa criar um token de desenvolvimento.
Na parte superior da página PowerSync, clique em
Manage Instances
.Na barra lateral esquerda, clique nas reticências (...) ao lado de TodoList para abrir o menu de contexto desta instância,
e, em seguida, selecione Editar instância. - Selecione a aba Autenticação do cliente e clique em Habilitar tokens de desenvolvimento. - Clique em Salvar e implantar.

Clique nas reticências (...) ao lado de TodoList para abrir o menu de contexto dessa instância novamente e selecione Gerar token de desenvolvimento.
Você será solicitado a fornecer um assunto de token/user_id. Isso agirá como user_id
e você pode configurar suas regras de sincronização para agir de acordo com ele.
Com as regras de sincronização que definimos anteriormente, você pode definir o Subject/User_id como global
para gerar um token que terá acesso a todo o conjunto de dados. Você também pode definir isso como mockUserId
ou mockUserId2
para sincronizar em um owner_id
específico.
Copie o token gerado e, em seguida, abra o aplicativo de diagnóstico e cole o token de desenvolvimento.
Observação
O token de desenvolvimento expira em 12 horas. A ferramenta de diagnóstico interromperá a sincronização com o Atlas após a expiração, portanto, você deve gerar um novo token se quiser que ela retome a sincronização.
Você deve ver uma página semelhante a esta.

Na barra lateral esquerda, clique em Console SQL.
Crie uma query SELECT
para visualizar todos os itens:
SELECT * FROM Item

Agora você tem todos os serviços necessários para sincronizar seu banco de dados MongoDB com um aplicação móvel.
Fase 2: preparar o aplicativo Realm para migração
Clonar projeto inicial
Nesta fase, você vai clonar um aplicação de lista de tarefas do Realm para o React Native. A main
ramificação do repositório de exemplo contém o resultado final da migração.
Para acompanhar este guia usando o repositório de exemplo , faça o checkout da ramificação 00-Start-Here
:
git clone https://github.com/takameyer/realm2powersync cd realm2powersync git checkout 00-Start-Here
Em seguida, instale as dependências para que o editor possa selecionar quaisquer importações e garantir que não haja erros ao editar este projeto.
Importante
Este tutorial pressupõe que você tenha a versão mais recente do Node.js instalada.
npm install
Como o aplicação pressupõe que há um Atlas cluster com um serviço Device Sync ativo, ele ainda não pode ser executado. Nas próximas etapas, você fará as modificações necessárias para executar o projeto como um aplicação somente local.
Refatorar o projeto de Device Sync apenas para local
Você deve remover as partes do Atlas Device Sync para que o aplicação seja executado com dados somente locais.
Primeiro, abra o source/AppWrapper.txs
e remova a configuração do AppProvider
, UserProvider
e sync
.
O arquivo AppWrapper.txs
atualizado deve ser semelhante ao seguinte:
import React from 'react'; import { StyleSheet, View, ActivityIndicator } from 'react-native'; import { RealmProvider } from '@realm/react'; import { App } from './App'; import { Item } from './ItemSchema'; const LoadingIndicator = () => { return ( <View style={styles.activityContainer}> <ActivityIndicator size="large" /> </View> ); }; export const AppWrapper = () => { return ( <RealmProvider schema={[Item]} fallback={LoadingIndicator}> <App /> </RealmProvider> ); }; const styles = StyleSheet.create({ activityContainer: { flex: 1, flexDirection: 'row', justifyContent: 'space-around', padding: 10, }, });
Em seguida, abra source/App.tsx
e remova as partes sobre dataExplorerLink
e os botões de cabeçalho para OfflineMode
e Logout
(isso será implementado posteriormente).
O arquivo App.tsx
atualizado deve ser semelhante ao seguinte:
import React from 'react'; import { SafeAreaProvider } from 'react-native-safe-area-context'; import { StyleSheet, Text, View } from 'react-native'; import { NavigationContainer } from '@react-navigation/native'; import { createStackNavigator } from '@react-navigation/stack'; import { LogoutButton } from './LogoutButton'; import { ItemListView } from './ItemListView'; import { OfflineModeButton } from './OfflineModeButton'; const Stack = createStackNavigator(); const headerRight = () => { return <OfflineModeButton />; }; const headerLeft = () => { return <LogoutButton />; }; export const App = () => { return ( <> {/* All screens nested in RealmProvider have access to the configured realm's hooks. */} <SafeAreaProvider> <NavigationContainer> <Stack.Navigator> <Stack.Screen name="Your To-Do List" component={ItemListView} options={{ headerTitleAlign: 'center', //headerLeft, //headerRight, }} /> </Stack.Navigator> </NavigationContainer> <View style={styles.footer}> <Text style={styles.footerText}> Log in with the same account on another device or simulator to see your list sync in real time. </Text> </View> </SafeAreaProvider> </> ); }; const styles = StyleSheet.create({ footerText: { fontSize: 12, textAlign: 'center', marginVertical: 4, }, hyperlink: { color: 'blue', }, footer: { paddingHorizontal: 24, paddingVertical: 12, }, });
Por fim, abra o source/ItemListView.tsx
e faça as seguintes atualizações:
Remova o código de assinatura Flexible Sync
Substituir usuário por um usuário simulado: -
const user={ id: 'mockUserId' };
Remova quaisquer referências
dataExplorerer
Remover funcionalidade da chave
Show All Tasks
(isso será implementado posteriormente)
O arquivo ItemListView.tsx
atualizado deve ser semelhante ao seguinte:
import React, { useCallback, useState, useEffect } from 'react'; import { BSON } from 'realm'; import { useRealm, useQuery } from '@realm/react'; import { SafeAreaProvider } from 'react-native-safe-area-context'; import { Alert, FlatList, StyleSheet, Switch, Text, View } from 'react-native'; import { Button, Overlay, ListItem } from '@rneui/base'; import { CreateToDoPrompt } from './CreateToDoPrompt'; import { Item } from './ItemSchema'; import { colors } from './Colors'; export function ItemListView() { const realm = useRealm(); const items = useQuery(Item).sorted('_id'); const user = { id: 'mockUserId' }; const [showNewItemOverlay, setShowNewItemOverlay] = useState(false); const [showAllItems, setShowAllItems] = useState(true); // createItem() takes in a summary and then creates an Item object with that summary const createItem = useCallback( ({ summary }: { summary: string }) => { // if the realm exists, create an Item realm.write(() => { return new Item(realm, { summary, owner_id: user?.id, }); }); }, [realm, user], ); // deleteItem() deletes an Item with a particular _id const deleteItem = useCallback( (id: BSON.ObjectId) => { // if the realm exists, get the Item with a particular _id and delete it const item = realm.objectForPrimaryKey(Item, id); // search for a realm object with a primary key that is an objectId if (item) { if (item.owner_id !== user?.id) { Alert.alert("You can't delete someone else's task!"); } else { realm.write(() => { realm.delete(item); }); } } }, [realm, user], ); // toggleItemIsComplete() updates an Item with a particular _id to be 'completed' const toggleItemIsComplete = useCallback( (id: BSON.ObjectId) => { // if the realm exists, get the Item with a particular _id and update it's 'isCompleted' field const item = realm.objectForPrimaryKey(Item, id); // search for a realm object with a primary key that is an objectId if (item) { if (item.owner_id !== user?.id) { Alert.alert("You can't modify someone else's task!"); } else { realm.write(() => { item.isComplete = !item.isComplete; }); } } }, [realm, user], ); return ( <SafeAreaProvider> <View style={styles.viewWrapper}> <View style={styles.toggleRow}> <Text style={styles.toggleText}>Show All Tasks</Text> <Switch trackColor={{ true: '#00ED64' }} onValueChange={() => { setShowAllItems(!showAllItems); }} value={showAllItems} /> </View> <Overlay isVisible={showNewItemOverlay} overlayStyle={styles.overlay} onBackdropPress={() => setShowNewItemOverlay(false)}> <CreateToDoPrompt onSubmit={({ summary }) => { setShowNewItemOverlay(false); createItem({ summary }); }} /> </Overlay> <FlatList keyExtractor={item => item._id.toString()} data={items} renderItem={({ item }) => ( <ListItem key={`${item._id}`} bottomDivider topDivider> <ListItem.Title style={styles.itemTitle}> {item.summary} </ListItem.Title> <ListItem.Subtitle style={styles.itemSubtitle}> <Text>{item.owner_id === user?.id ? '(mine)' : ''}</Text> </ListItem.Subtitle> <ListItem.Content> {!item.isComplete && ( <Button title="Mark done" type="clear" onPress={() => toggleItemIsComplete(item._id)} /> )} <Button title="Delete" type="clear" onPress={() => deleteItem(item._id)} /> </ListItem.Content> </ListItem> )} /> <Button title="Add To-Do" buttonStyle={styles.addToDoButton} onPress={() => setShowNewItemOverlay(true)} /> </View> </SafeAreaProvider> ); } const styles = StyleSheet.create({ viewWrapper: { flex: 1, }, sectionContainer: { marginTop: 32, paddingHorizontal: 24, }, addToDoButton: { backgroundColor: colors.primary, borderRadius: 4, margin: 5, }, completeButton: { backgroundColor: colors.primary, borderRadius: 4, margin: 5, }, showCompletedButton: { borderRadius: 4, margin: 5, }, showCompletedIcon: { marginRight: 5, }, itemTitle: { flex: 1, }, itemSubtitle: { color: '#979797', flex: 1, }, toggleRow: { flexDirection: 'row', alignItems: 'center', padding: 12, }, toggleText: { flex: 1, fontSize: 16, }, overlay: { backgroundColor: 'white', }, status: { width: 40, height: 40, justifyContent: 'center', borderRadius: 5, borderWidth: 1, borderColor: '#d3d3d3', backgroundColor: '#ffffff', alignSelf: 'flex-end', }, delete: { alignSelf: 'flex-end', width: 65, marginHorizontal: 12, }, statusCompleted: { borderColor: colors.purple, }, statusIcon: { textAlign: 'center', fontSize: 17, color: colors.purple, }, });
Com essas alterações, o aplicativo deve funcionar em um banco de dados local.
Executar e verificar alterações
Antes de iniciar a migração, você precisa construir e executar o aplicação atualizado para verificar se ele funciona conforme o esperado.
Para iOS, execute os seguintes comandos:
npx pod-install npm run ios
Para Android, execute o seguinte comando:
npm run android
Observe que quaisquer erros de construção estão fora do escopo desta documentação. Se você estiver enfrentando problemas relacionados à compilação, consulte a documentação do React Native para garantir que seu ambiente esteja configurado corretamente.
Enquanto seu aplicativo está em execução, você pode verificar a funcionalidade básica. Você deve ser capaz de:
Criar novos itens
Marcar itens como concluídos
Excluir itens

Fase 3: Migrar do Realm para o cliente PowerSync
Instalar dependências
Agora que você tem um aplicação Realm somente local em execução, pode começar a converter esse aplicação para usar uma versão somente local do cliente PowerSync.
O PowerSync usa um banco de dados baseado em SQLite, portanto, será necessário fazer algumas modificações no esquema para garantir que seja compatível.
Para fazer isso, você precisará configurar o cliente PowerSync. Para obter instruções detalhadas, você pode consultar o repositório npm @PowerSync/react-native ou a documentação do PowerSync React Native Setup.
Primeiro, execute o seguinte comando para adicionar dependências para o PowerSync React Native Client, o banco de dados SQLite de apoio, um iterador assíncrono polyfill (necessário conforme as instruções), bem como o bson
dependência (usada para gerar ObjectId's para inserir documentos no MongoDB):
npm install @powersync/react-native @journeyapps/react-native-quick-sqlite @azure/core-asynciterator-polyfill bson
Para configurar o polyfill, abra o index.js
e adicione import '@azure/core-asynciterator-polyfill';
ao topo do arquivo.
O arquivo index.js
atualizado deve ser semelhante ao seguinte:
import '@azure/core-asynciterator-polyfill'; import 'react-native-get-random-values'; import {AppRegistry} from 'react-native'; import {AppWrapper} from './source/AppWrapper'; import {name as appName} from './app.json'; AppRegistry.registerComponent(appName, () => AppWrapper);
Agora que as dependências foram adicionadas, você precisa reconstruir o aplicação:
Para iOS, execute
pod-install
.Para Android, atualize o SDK mínimo exigido para 24 para ser compatível com
react-native-quick-sqlite
. Para fazer isso, abraandroid/build.gradle
e altereminSdkVersion
de 21 para 24.
Migrar esquema de dados
Agora você configurará os tipos de dados e esquemas para o banco de dados local.
Consulte a documentação de mapeamento de tipo do PowerSync MongoDB para determinar como configurar seu esquema específico. A seguir, uma referência rápida dos tipos disponíveis:
Tipo | Descrição |
---|---|
zero | valores indefinidos ou valores não definidos |
inteiro | um número inteiro assinado de 64bits |
real | um número de ponto flutuante de 64bits |
text | uma string de texto UTF-8 |
blob | Dados binários |
Para este tutorial, você modificará source/ItemSchema.tsx
da seguinte forma:
import {column, Schema, Table} from '@powersync/react-native'; export const ItemSchema = new Table({ isComplete: column.integer, summary: column.text, owner_id: column.text, }); export const AppSchema = new Schema({ Item: ItemSchema, }); export type Database = (typeof AppSchema)['types']; export type Item = Database['Item'];
Importante
O nome da propriedade passado para Schema
representa o nome da tabela local e da collection MongoDB . Neste caso, certifique-se de que ele seja nomeado Item
.
Observe que este código exporta os tipos diretamente do AppSchema
, em vez de defini-los manualmente.
Refatorar o código do aplicativo
Para obter acesso ao PowerSync e vincular seus dados, você precisará de acesso aos hooks e provedores do cliente PowerSync. Esta funcionalidade é fornecida pelo componente PowerSyncContext.
Primeiro, atualize o source/AppWrapper.tsx
para usar o PowerSyncContext
e inicialize seu cliente PowerSync:
import React from 'react'; import {App} from './App'; import {AppSchema} from './ItemSchema'; import {PowerSyncContext, PowerSyncDatabase} from '@powersync/react-native'; const powerSync = new PowerSyncDatabase({ schema: AppSchema, database: { dbFilename: 'powersync.db', }, }); powerSync.init(); export const AppWrapper = () => { return ( <PowerSyncContext.Provider value={powerSync}> <App /> </PowerSyncContext.Provider> ); };
Em seguida, atualize ItemListView.tsx
para usar o cliente PowerSync. Para conseguir isso, você deve atualizar os ganchos usados na parte superior deste componente:
Para obter acesso ao banco de dados local para fazer gravações e atualizações, use o hook
usePowerSync
.Para obter uma lista de itens da lista de tarefas que são renderizados automaticamente na atualização, use o hook
useQuery
.
Faça as seguintes alterações:
Remover
import { BSON } from 'realm';
Adicionar
import { ObjectId } from 'bson';
Altere as duas primeiras linhas da função
ItemListView
para corresponder ao seguinte:export function ItemListView() { const db = usePowerSync(); const {data: items} = useQuery<Item>('SELECT * FROM Item');
Em seguida, você precisa atualizar os métodos createItem
, deleteItem
e toggleItemIsComplete
.
Para cada um desses métodos, você usará o objeto db
retornado de usePowerSync
. Assim como no Realm, o banco de dados local abre uma transação para executar qualquer operação mutável, como inserir, atualizar ou excluir. Você também adicionará blocos try/catch para propagar quaisquer erros no frontend do aplicação.
Observe que o código está importando ObjectId
de bson
para criar os IDs exclusivos para cada item. Lembre-se de que o PowerSync espera que os itens da chave primária sejam denominados id
.
O código de criação também implementa os valores padrão para os itens diretamente nessa lógica. Nesse caso, isComplete
é inicializado como falso e o id
é inicializado com o resultado de string do ObjectId
recém-criado.
O método createItem
pode ser implementado da seguinte forma:
// createItem() takes in a summary and then creates an Item object with that summary const createItem = useCallback( async ({summary}: {summary: string}) => { try { // start a write transaction to insert the new Item db.writeTransaction(async tx => { await tx.execute( 'INSERT INTO Item (id, summary, owner_id, isComplete) VALUES (?, ?, ?, ?)', [new ObjectId().toHexString(), summary, user?.id, false], ); }); } catch (ex: any) { Alert.alert('Error', ex?.message); } }, [db], );
Os métodos deleteItem
e toggleItemIsComplete
são semelhantes, portanto, implemente-os da seguinte maneira:
// deleteItem() deletes an Item with a particular _id const deleteItem = useCallback( async (id: String) => { // start a write transaction to delete the Item try { db.writeTransaction(async tx => { await tx.execute('DELETE FROM Item WHERE id = ?', [id]); }); } catch (ex: any) { Alert.alert('Error', ex?.message); } }, [db], ); // toggleItemIsComplete() updates an Item with a particular _id to be 'completed' const toggleItemIsComplete = useCallback( async (id: String) => { // start a write transaction to update the Item try { db.writeTransaction(async tx => { await tx.execute( 'UPDATE Item SET isComplete = NOT isComplete WHERE id = ?', [id], ); }); } catch (ex: any) { Alert.alert('Error', ex?.message); } }, [db], );
Finalmente, atualize o FlatList
renderizado. Você irá:
Substituir instâncias de
_id
porid
Atualize o
keyExtractor
doFlatList
para usar a stringid
diretamente.Anteriormente, o banco de dados retornava um
ObjectId
. Isso precisará ser convertido em uma string.
O FlatList
atualizado agora se parece com o seguinte:
<FlatList keyExtractor={item => item.id} data={items} renderItem={({item}) => ( <ListItem key={`${item.id}`} bottomDivider topDivider> <ListItem.Title style={styles.itemTitle}> {item.summary} </ListItem.Title> <ListItem.Subtitle style={styles.itemSubtitle}> <Text>{item.owner_id === user?.id ? '(mine)' : ''}</Text> </ListItem.Subtitle> <ListItem.Content> <Pressable accessibilityLabel={`Mark task as ${ item.isComplete ? 'not done' : 'done' }`} onPress={() => toggleItemIsComplete(item.id)} style={[ styles.status, item.isComplete && styles.statusCompleted, ]}> <Text style={styles.statusIcon}> {item.isComplete ? '✓' : '○'} </Text> </Pressable> </ListItem.Content> <ListItem.Content> <Pressable accessibilityLabel={'Remove Item'} onPress={() => deleteItem(item.id)} style={styles.delete}> <Text style={[styles.statusIcon, {color: 'blue'}]}> DELETE </Text> </Pressable> </ListItem.Content> </ListItem> )} />
Executar e verificar alterações
Depois de atualizar o código, você poderá usar um cliente PowerSync local.
Para verificar, reconstrua o aplicação. Se você estiver usando o iOS, não se lembre de atualizar os Pods com npx pod-install
.

Agora você deve ter um aplicação funcional que permita adicionar, atualizar e excluir itens da lista de tarefas usando o PowerSync.
Se você encontrar problemas, poderá visualizar as alterações feitas até esse ponto na ramificação 02-Migrate-Local-Client do repositório de exemplo .
Seu aplicação móvel agora está pronto para sincronizar dados em tempo real do MongoDB.
Observação
Você provavelmente notou que os dados do Realm ainda não foram migrados. Este guia pressupõe que o cluster MongoDB hospedado no Atlas é a fonte da verdade para os dados e sincronize isso com o aplicação. A migração de dados locais está fora do escopo deste tutorial, mas pode ser abordada em documentação futura.
Fase 4: Sincronizar dados do Atlas para o PowerSync
Configuração inicial
Agora você deve ter um serviço PowerSync em execução que contenha dados sincronizados do Atlas, que foram verificados usando a ferramenta de diagnóstico PowerSync.
Nesta fase, você obterá esses dados para sincronizar com o aplicação React Native .
Para começar, você precisa criar uma maneira de definir algumas variáveis de ambiente para tokens e endpoints.
Primeiro, instale react-native-dotenv
em suas dependências de desenvolvimento. Este é um plugin-in babel que pega um arquivo .env
da raiz do projeto e permite importar variáveis de ambiente diretamente para o aplicação.
npm install -D react-native-dotenv
Em seguida, adicione a seguinte linha ao arquivo babel.config.js
:
module.exports = { presets: ['module:metro-react-native-babel-preset'], plugins: ['module:react-native-dotenv'], };
Crie um novo diretório denominado types
e, nele, crie um novo arquivo denominado env.d.ts
que contenha as seguintes variáveis que queremos importar:
declare module '@env' { export const AUTH_TOKEN: string; export const POWERSYNC_ENDPOINT: string; }
Você precisa recuperar os valores necessários para as variáveis de ambiente do PowerSync.
No console do PowerSync, na barra lateral esquerda, clique em ... ao lado de TodoList para abrir o menu de contexto.
Selecione Editar instância.
Copie e salve o URL.

Em seguida, gere um novo token de desenvolvimento para sua instância com o assunto/user_id. mockUserId
Copie e salve o token gerado.
No projeto do aplicação , crie um arquivo .env
no diretório raiz e cole o endpoint e o token do PowerSync que você acabou de gerar:
POWERSYNC_ENDPOINT=<endpoint> AUTH_TOKEN=<dev-token>
Refatorar o código do cliente
Você precisará refatorar um pouco seu aplicação para que ele possa se conectar à sua instância do PowerSync.
Primeiro, crie um novo arquivo em source
chamado PowerSync.ts
e cole o seguinte:
import { AppSchema } from './ItemSchema'; import { AbstractPowerSyncDatabase, PowerSyncDatabase, } from '@powersync/react-native'; import { AUTH_TOKEN, POWERSYNC_ENDPOINT } from '@env'; const powerSync = new PowerSyncDatabase({ schema: AppSchema, database: { dbFilename: 'powersync.db', }, }); powerSync.init(); class Connector { async fetchCredentials() { return { endpoint: POWERSYNC_ENDPOINT, token: AUTH_TOKEN, }; } async uploadData(database: AbstractPowerSyncDatabase) { console.log('Uploading data'); } } export const setupPowerSync = (): PowerSyncDatabase => { const connector = new Connector(); powerSync.connect(connector); return powerSync; }; export const resetPowerSync = async () => { await powerSync.disconnectAndClear(); setupPowerSync(); };
Este arquivo faz o seguinte:
Cria uma nova classe
Connector
, que será usada para definir o token de desenvolvimento e o endpoint PowerSync em nosso cliente PowerSync.Define uma função simulada
uploadData
, que será utilizada na fase seguinte para enviar alterações para o Atlas.Define métodos para configurar e redefinir nosso cliente PowerSync. A redefinição do cliente será útil para o desenvolvimento agora, pois todas as alterações feitas serão colocadas em uma fila. Até que essas alterações sejam processadas, você não receberá novas atualizações.
Em seguida, atualize AppWrapper.tsx
para usar o novo método setupPowerSync
:
import { PowerSyncContext } from '@powersync/react-native'; import React from 'react'; import { App } from './App'; import { setupPowerSync } from './PowerSync'; const powerSync = setupPowerSync(); export const AppWrapper = () => { return ( <PowerSyncContext.Provider value={powerSync}> <App /> </PowerSyncContext.Provider> ); };
Em seguida, refatore LogoutButton.tsx
para implementar o método resetPowerSync
. Renomeie-o para ResetButton.tsx
e atualize seu conteúdo da seguinte forma:
import React, { useCallback } from 'react'; import { Pressable, Alert, View, Text, StyleSheet } from 'react-native'; import { colors } from './Colors'; import { resetPowerSync } from './PowerSync'; export function ResetButton() { const signOut = useCallback(() => { resetPowerSync(); }, []); return ( <Pressable onPress={() => { Alert.alert('Reset Database?', '', [ { text: 'Yes, Reset Database', style: 'destructive', onPress: () => signOut(), }, { text: 'Cancel', style: 'cancel' }, ]); }}> <View style={styles.buttonContainer}> <Text style={styles.buttonText}>Reset</Text> </View> </Pressable> ); } const styles = StyleSheet.create({ buttonContainer: { paddingHorizontal: 12, }, buttonText: { fontSize: 16, color: colors.primary, }, });
Em seguida, modifique App.tsx
para mostrar o botão Reset
no lado esquerdo do cabeçalho:
Substitua
import { LogoutButton } from './LogoutButton';
porimport { ResetButton } from './ResetButton';
No
headerLeft
, substitua a linha existente porreturn <ResetButton />;
Descomente a linha
//headerLeft
para que o botão Redefinir seja exibido.
Suas alterações serão parecidas com o seguinte:
import React from 'react'; import { SafeAreaProvider } from 'react-native-safe-area-context'; import { StyleSheet, Text, View } from 'react-native'; import { NavigationContainer } from '@react-navigation/native'; import { createStackNavigator } from '@react-navigation/stack'; import { ResetButton } from './ResetButton'; import { ItemListView } from './ItemListView'; import { OfflineModeButton } from './OfflineModeButton'; const Stack = createStackNavigator(); const headerRight = () => { return <OfflineModeButton />; }; const headerLeft = () => { return <ResetButton />; }; export const App = () => { return ( <> {/* All screens nested in RealmProvider have access to the configured realm's hooks. */} <SafeAreaProvider> <NavigationContainer> <Stack.Navigator> <Stack.Screen name="Your To-Do List" component={ItemListView} options={{ headerTitleAlign: 'center', headerLeft, //headerRight, }} /> </Stack.Navigator> </NavigationContainer> <View style={styles.footer}> <Text style={styles.footerText}> Log in with the same account on another device or simulator to see your list sync in real time. </Text> </View> </SafeAreaProvider> </> ); }; const styles = StyleSheet.create({ footerText: { fontSize: 12, textAlign: 'center', marginVertical: 4, }, hyperlink: { color: 'blue', }, footer: { paddingHorizontal: 24, paddingVertical: 12, }, });
Executar e verificar alterações
Por último, a biblioteca react-native-dotenv
exige que nosso servidor React Native seja redefinido com um cache limpo, o que é normal ao adicionar funcionalidade ao Babel.
Para fazer isso, elimine qualquer instância do React Native atualmente em execução com ctrl-c
e, em seguida, insira o seguinte para executar a instância com um cache limpo:
npm start -- --reset-cache
Agora você deve estar pronto para sincronizar seus dados do Atlas com seu aplicação React Native .
Agora redefina o aplicação. Se você fez modificações no banco de dados local do aplicativo antes, precisará clicar no novo botão Reset
para redefinir o banco de dados local com o conteúdo do que está armazenado no Atlas.
Você deve agora ver todos os itens da lista de tarefas de mockUserId
:

Se você encontrar problemas, exclua o aplicação em seu emulador/simulador e reconstrua-o para começar do zero.
Se você ainda estiver encontrando problemas, pode visualizar as alterações feitas até esse ponto na ramificação 03-Sync-Data-From-Atlas do repositório de exemplo .
Fase 5: implementar API de backend
Inspecionar conector
Agora que seus dados estão sincronizando com o aplicação móvel, a próxima etapa é criar uma maneira de propagar as alterações locais no Atlas.
Nesta fase, você:
Implemente o método
uploadData
em seuConnector
Criar um servidor de backend simples para lidar com operações do dispositivo móvel
Por uma questão de simplicidade, este guia executará o servidor localmente. Para casos de uso de produção, considere o uso de um serviço de nuvem para lidar com essas solicitações (por exemplo O JournalApps oferece funções de nuvem sem servidor para ajudar com isso).
Implementar método de upload
Comece examinando as operações enviadas para o método uploadData
quando são feitas alterações locais no aplicação móvel.
Faça as seguintes alterações em source/PowerSync.ts
:
async uploadData(database: AbstractPowerSyncDatabase) { const batch = await database.getCrudBatch(); console.log('batch', JSON.stringify(batch, null, 2)); }
Em seguida, você fará alterações no aplicação móvel que incluem:
Excluindo um item
Alternar um item como completo ou incompleto
Adicionando um novo item
Conclua a implementação do método uploadData
para enviar essas informações em uma solicitação de busca.
Primeiro, adicione um novo valor ao seu .env
:
BACKEND_ENDPOINT=http://localhost:8000
e types/env.d.ts
:
declare module '@env' { export const AUTH_TOKEN: string; export const POWERSYNC_ENDPOINT: string; export const BACKEND_ENDPOINT: string; }
Se você estiver usando o emulador Android, deverá garantir que as solicitações para localhost
na porta 8000
estejam sendo encaminhadas do emulador para sua máquina local. Para habilitar isso, execute o seguinte comando:
adb reverse tcp:8000 tcp:8000
Em seguida, adicione BACKEND_ENDPOINT
à declaração de importação em source/PowerSync.ts
:
import { AUTH_TOKEN, POWERSYNC_ENDPOINT, BACKEND_ENDPOINT } from '@env';
Em seguida, atualize o método uploadData
:
async uploadData(database: AbstractPowerSyncDatabase) { const batch = await database.getCrudBatch(); if (batch === null) { return; } const result = await fetch(`${BACKEND_ENDPOINT}/update`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(batch.crud), }); if (!result.ok) { throw new Error('Failed to upload data'); } batch.complete(); }
O método atualizado agora enviará uma matriz de operações CRUD para o endpoint de backend:
Se o aplicação estiver offline, ele simplesmente falhará.
Se o aplicação receber uma resposta positiva, ele marcará as operações como concluídas e o lote de operações será removido do aplicação móvel.
Criar Servidor de Backend
Agora, crie uma nova pasta no seu projeto chamada backend
:
mkdir backend
Em seguida, crie um arquivo package.json
:
{ "main": "index.js", "scripts": { "start": "node --env-file=.env index.js" }, "dependencies": { "express": "^4.21.2", "mongodb": "^6.12.0" } }
Este package.json
inclui um script start
que adiciona variáveis de um .env
ao serviço.
Crie um novo .env
com seu Atlas connection string
de antes:
MONGODB_URI=<connection_string>
Agora, instale as dependências:
npm install
Observe que este guia não incluirá como adicionar Typescript e outras ferramentas a este serviço, mas você pode fazê-lo à vontade. Além disso, o guia mantém a validação no mínimo e implementa apenas as alterações necessárias para preparar os dados recebidos do aplicação móvel para serem inseridos no MongoDB.
Primeiro, crie um index.js
com o seguinte conteúdo:
const express = require("express"); const { MongoClient, ObjectId } = require("mongodb"); const app = express(); app.use(express.json()); // MongoDB setup const client = new MongoClient( process.env.MONGODB_URI || "mongodb://localhost:27017", ); // Helper function to coerce isComplete to boolean function coerceItemData(data) { if (data && "isComplete" in data) { data.isComplete = !!Number(data.isComplete); } return data; } async function start() { await client.connect(); const db = client.db("PowerSync"); const items = db.collection("Item"); app.post("/update", async (req, res) => { const operations = req.body; try { for (const op of operations) { console.log(JSON.stringify(op, null, 2)); switch (op.op) { case "PUT": await items.insertOne({ ...coerceItemData(op.data), _id: new ObjectId(op.id), }); break; case "PATCH": await items.updateOne( { _id: new ObjectId(op.id) }, { $set: coerceItemData(op.data) }, ); break; case "DELETE": await items.deleteOne({ _id: new ObjectId(op.id), }); break; } } res.json({ success: true }); } catch (error) { res.status(500).json({ error: error.message }); } }); app.listen(8000, () => { console.log("Server running on port 8000"); }); } start().catch(console.error);
Observe a partir do serviço acima que o isComplete
é forçado em um valor boolean
. Isso garante que os novos itens do todolist cheguem ao MongoDB com true
ou false
em vez de 1
ou 0
. Uma instância ObjectId
também está sendo criada a partir do op.id
. Definir isso para a propriedade _id
moldará os dados de acordo com os requisitos e as melhores práticas do MongoDB .
Executar e verificar alterações
Agora você pode ativar o servidor:
npm start
O aplicação móvel já deve estar tentando enviar operações para esse endpoint. A declaração console.log
deve mostrar as solicitações conforme elas estão sendo enviadas e as alterações devem se propagar para o Atlas.
Você pode verificar isso visualizando sua coleção do MongoDB na UI do Atlas ou no MongoDB Compass.

Agora você deve ter um aplicação móvel totalmente funcional que sincroniza dados de e para o Atlas. Você também pode tentar desligar o Wi-Fi para testar como o aplicativo funciona quando está offline.
Se você encontrar problemas, poderá visualizar as alterações feitas até esse ponto na ramificação 04-Write-To-Backend do repositório de exemplo .
Fase 6: retoques finais e limpeza
Esta fase final aborda como implementar duas funcionalidades opcionais de teste de aplicação, bem como como limpar seu projeto de qualquer código e dependências desnecessários.
No processo de criação deste aplicação, as seguintes funcionalidades foram omitidas: Mostrar Todas as Tarefas e a chave de modo Offline. Esses recursos são úteis para testar a funcionalidade do aplicativo e não se destinam a ser usados em um aplicação de produção.
Observação
As etapas relacionadas a esses recursos são marcadas como opcionais. Sinta-se livre para pular estas etapas opcionais se isso não for do seu interesse.
Implemente a alternância Mostrar tudo (opcional)
Para implementar a alternância opcional Mostrar todos, será criado um segundo bucket que será ativado com base em um parâmetro do cliente . Você aplicará isso desconectando a sessão de sincronização atual e reconectando com um novo conjunto de valores. Esse valor será um booleano chamado view_all
, que será usado como um backdoor inseguro para mostrar todos os itens da lista de tarefas já criados no cluster. Essa funcionalidade ajuda a mostrar que os buckets podem ser criados dinamicamente com base em determinados parâmetros.
Observação
A metodologia usada aqui é insegura, portanto, o sinalizador accept_potentially_dangerous_queries
precisará ser ativado no bucket para que isso seja feito. Uma maneira segura de fazer isso seria baseá-lo em uma função de usuário e atualizar as autorizações dos usuários em seu banco de dados de apoio, que está fora do escopo deste guia.
Para começar, navegue até o painel do PowerSync e atualize as regras de sincronização para incluir um bucket com base no parâmetro view_all
que está sendo definido:
bucket_definitions: user_buckets: parameters: - SELECT request.user_id() as user_id data: - SELECT _id as id FROM "Item" WHERE bucket.user_id = 'global' OR owner_id = bucket.user_id view_all_bucket: accept_potentially_dangerous_queries: true parameters: - SELECT (request.parameters() ->> 'view_all') as view_all data: - SELECT _id as id FROM "Item" WHERE bucket.view_all = true
Observe que as definições de bucket são combinadas juntas, portanto, quando o view_all_bucket
estiver ativo, ele será adicionado aos dados do user_buckets
.
Em seguida, atualize source/PowerSync.ts
em seu projeto para incluir uma variável local para determinar o estado de sinalizador view_all
e aplique-o aos parâmetros da instância de conexão.
Primeiro, adicione um parâmetro viewAll
e atualize a função setupPowerSync
:
let viewAll = false; export const setupPowerSync = (): PowerSyncDatabase => { const connector = new Connector(); powerSync.connect(connector, {params: {view_all: viewAll}}); return powerSync; };
Em seguida, adicione as seguintes duas funções:
export const resetPowerSync = async () => { await powerSync.disconnectAndClear(); setupPowerSync(); }; export const toggleViewAll = () => { viewAll = !viewAll; resetPowerSync(); };
Finalmente, atualize source/ItemListView.tsx
.
Primeiro, importe toggleViewAll
de PowerSync
:
import { toggleViewAll } from './PowerSync';
Em seguida, modifique o atributo onValueChange
da opção "Mostrar todas as tarefas" para invocar o método toggleViewAll
. Use o seguinte código para substituir os componentes Text
e Switch
:
<Text style={styles.toggleText}>Show All Tasks</Text> <Switch trackColor={{true: '#00ED64'}} onValueChange={() => { setShowAllItems(!showAllItems); toggleViewAll(); }} value={showAllItems} />
Agora reinicie seu aplicação e verifique se ele funciona conforme o esperado:

Implementar alternância do modo offline (opcional)
Para implementar a alternância opcional do Modo offline, você precisará desconectar a sessão de sincronização e reconectá-la. Isso permitirá que você faça alterações locais enquanto não estiver conectado à sincronização e verifique se elas são enviadas quando a sessão de sincronização for restabelecida.
Você adicionará uma variável para o estado da conexão e, em seguida, criará um método para alternar isso e invocar os métodos connect
e disconnect
no cliente PowerSync.
Primeiro, adicione o seguinte a source/PowerSync.ts
:
let connection = true; export const toggleConnection = () => { if (connection) { powerSync.disconnect(); } else { setupPowerSync(); } connection = !connection; };
Em seguida, refatore o source/OfflineModeButton.tsx
para remover a funcionalidade Realm e substitua-a invocando o novo método toggleConnection
. Você também precisará adicionar algumas importações:
import { useState } from 'react'; import { Pressable, Text, StyleSheet } from 'react-native'; import { colors } from './Colors'; import {toggleConnection} from './PowerSync'; export function OfflineModeButton() { const [pauseSync, togglePauseSync] = useState(false); return ( <Pressable onPress={() => { toggleConnection(); togglePauseSync(!pauseSync); }}> <Text style={styles.buttonText}> {pauseSync ? 'Enable Sync' : 'Disable Sync'} </Text> </Pressable> ); } const styles = StyleSheet.create({ buttonText: { padding: 12, color: colors.primary, }, });
Finalmente, abra o source/App.tsx
e descomente o componente headerRight
de volta no Stack.Screen
do aplicação:
<Stack.Screen name="Your To-Do List" component={ItemListView} options={{ headerTitleAlign: 'center', headerLeft, headerRight, }} />
Agora, verifique as atualizações abrindo uma segunda instância do aplicativo e, em seguida, fazendo algumas alterações:

Projeto de limpeza
Finalmente, você pode limpar seu projeto.
Os seguintes arquivos podem ser excluídos com segurança:
atlasConfig.json
source/WelcomeView.tsx
Você também pode remover as seguintes dependências do seu package.json
:
@realm/react
realm
Conclusão e próximas etapas
Este guia deve ter fornecido a você os blocos de construção para iniciar sua viagem de migração para o PowerSync.
Para resumir, seguindo este guia, você deverá ter realizado o seguinte:
Implementou um banco de dados MongoDB com dados de amostra
Implementou um serviço PowerSync que sincroniza os dados de amostra
Aprendou como visualizar e consultar esses dados usando a Ferramenta de Diagnóstico
Converteu um aplicação móvel do Device Sync apenas para local
Migrado de um banco de dados Realm somente local para o PowerSync
Configurar a sincronização do PowerSync com um banco de dados móvel
Criou um backend para enviar alterações do cliente PowerSync para MongoDB
Para as próximas etapas, tente pegar um pequeno fragmento do seu aplicação móvel e convertê-lo para usar o PowerSync. E fique atento à documentação futura que aborda casos de uso mais avançados.