From 8ea61dda65ca5c80e3c98c00a8a3db89a289394e Mon Sep 17 00:00:00 2001 From: benitav Date: Tue, 31 Oct 2023 19:24:46 +0200 Subject: [PATCH 1/6] Update Readme for alpha -> beta --- packages/powersync-react/README.md | 2 +- packages/powersync-sdk-common/README.md | 4 ++-- packages/powersync-sdk-react-native/README.md | 18 +++--------------- 3 files changed, 6 insertions(+), 18 deletions(-) diff --git a/packages/powersync-react/README.md b/packages/powersync-react/README.md index e998222d..5b64b604 100644 --- a/packages/powersync-react/README.md +++ b/packages/powersync-react/README.md @@ -1,6 +1,6 @@ # React components for PowerSync -This package is currently in an alpha release. Functionality could change dramatically in future releases. Certain functions may be partially implemented or buggy. +This package is currently in a beta release. ## Context Configure a PowerSync DB connection and add it to a context provider. diff --git a/packages/powersync-sdk-common/README.md b/packages/powersync-sdk-common/README.md index 373b5a9d..5637b1a8 100644 --- a/packages/powersync-sdk-common/README.md +++ b/packages/powersync-sdk-common/README.md @@ -1,5 +1,5 @@ -# Alpha -This package is currently in an alpha release. Functionality could change dramatically in future releases. Certain functions may be partially implemented or buggy. +# Beta +This package is currently in a beta release. # PowerSync SDK common JS diff --git a/packages/powersync-sdk-react-native/README.md b/packages/powersync-sdk-react-native/README.md index 2500780f..721bcde5 100644 --- a/packages/powersync-sdk-react-native/README.md +++ b/packages/powersync-sdk-react-native/README.md @@ -1,9 +1,9 @@ # PowerSync SDK for React Native -[PowerSync](https://powersync.co) is a service and set of SDKs that keeps PostgreSQL databases in sync with on-device SQLite databases. +[PowerSync](https://powersync.co) is a service and set of SDKs that keeps Postgres databases in sync with on-device SQLite databases. See a summary of features [here](https://docs.powersync.co/resources/api-reference#react-native-and-expo). -## Alpha Release -This React Native SDK package is currently in an alpha release. Functionality could change dramatically in future releases. Certain functions may be partially implemented or buggy. +## Beta Release +This React Native SDK package is currently in a beta release. # Installation @@ -23,7 +23,6 @@ Install it in your app with: npx expo install @journeyapps/react-native-quick-sqlite ``` - ## Polyfills: Fetch This SDK requires HTTP streaming in order to function. The following `fetch` polyfills are required for the React Native implementation of `fetch`: @@ -88,17 +87,6 @@ This package uses native libraries. Create native Android and iOS projects (if n npx expo run:android ``` -# SDK Features - - * Direct access to the SQLite database - use SQL on the client and server. - * Operations are asynchronous by default - does not block the UI. - * No need for client-side database migrations - these are handled automatically. - * Real-time streaming of changes. - * Subscribe to queries for live updates. - - Upcoming features: - * Support one write and many reads concurrently. - # Getting Started ## Declare the Schema From 8589a9d39bbed02155c28b7488e12e16899e2e36 Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Tue, 31 Oct 2023 20:08:52 +0200 Subject: [PATCH 2/6] minimal getting started docs --- packages/powersync-sdk-react-native/README.md | 428 ++---------------- 1 file changed, 48 insertions(+), 380 deletions(-) diff --git a/packages/powersync-sdk-react-native/README.md b/packages/powersync-sdk-react-native/README.md index 721bcde5..5bbed387 100644 --- a/packages/powersync-sdk-react-native/README.md +++ b/packages/powersync-sdk-react-native/README.md @@ -13,7 +13,7 @@ This React Native SDK package is currently in a beta release. npx expo install @journeyapps/powersync-sdk-react-native ``` -## Peer Dependencies: SQLite +## Install Peer Dependency: SQLite This SDK currently requires `@journeyapps/react-native-quick-sqlite` as a peer dependency. @@ -23,7 +23,8 @@ Install it in your app with: npx expo install @journeyapps/react-native-quick-sqlite ``` -## Polyfills: Fetch +## Install Polyfills +### Fetch This SDK requires HTTP streaming in order to function. The following `fetch` polyfills are required for the React Native implementation of `fetch`: @@ -46,7 +47,7 @@ This SDK requires HTTP streaming in order to function. The following `fetch` pol import 'react-native-polyfill-globals/auto'; ``` -## Polyfills & Babel Plugins: Watched Queries +### Babel Plugins: Watched Queries Watched queries require support for Async Iterators. Expo apps currently require polyfill and Babel plugins in order to use this functionality. @@ -61,11 +62,13 @@ import 'react-native-polyfill-globals/auto'; import '@azure/core-asynciterator-polyfill'; ``` +Install the async generator Babel plugin + ```bash yarn add -D @babel/plugin-transform-async-generator-functions ``` - Add the Babel plugin to your `babel.config.js` file +Add the Babel plugin to your `babel.config.js` file ```JavaScript module.exports = function (api) { @@ -89,406 +92,71 @@ npx expo run:android # Getting Started -## Declare the Schema +See our [Docs](https://docs.powersync.co/usage/installation/client-side-setup/integrating-with-your-backend#react-native-and-expo) for detailed instructions. ```JavaScript -// lib/Schema.js -import { Column, ColumnType, Index, IndexedColumn, Schema, Table } from '@journeyapps/powersync-sdk-react-native'; +import { + Column, + ColumnType, + RNQSPowerSyncDatabaseOpenFactory, + Schema, + Table +} from '@journeyapps/powersync-sdk-react-native'; export const AppSchema = new Schema([ - new Table({ - name: 'todos', - columns: [ - new Column({ name: 'list_id', type: ColumnType.TEXT }), - new Column({ name: 'created_at', type: ColumnType.TEXT }), - new Column({ name: 'completed_at', type: ColumnType.TEXT }), - new Column({ name: 'description', type: ColumnType.TEXT }), - new Column({ name: 'completed', type: ColumnType.INTEGER }), - new Column({ name: 'created_by', type: ColumnType.TEXT }), - new Column({ name: 'completed_by', type: ColumnType.TEXT }) - ], - indexes: [new Index({ name: 'list', columns: [new IndexedColumn({ name: 'list_id' })] })] - }), - new Table({ - name: 'lists', - columns: [ - new Column({ name: 'created_at', type: ColumnType.TEXT }), - new Column({ name: 'name', type: ColumnType.TEXT }), - new Column({ name: 'owner_id', type: ColumnType.TEXT }) - ] - }) + new Table({ name: 'customers', columns: [new Column({ name: 'description', type: ColumnType.TEXT })] }) ]); -``` -## Create an Upload Connector +let PowerSync; -```JavaScript -// lib/Connector.js -import { UpdateType} from '@journeyapps/powersync-sdk-react-native'; - -/// Postgres Response codes that we cannot recover from by retrying. -const FATAL_RESPONSE_CODES = [ - // Class 22 — Data Exception - // Examples include data type mismatch. - new RegExp('^22...$'), - // Class 23 — Integrity Constraint Violation. - // Examples include NOT NULL, FOREIGN KEY and UNIQUE violations. - new RegExp('^23...$'), - // INSUFFICIENT PRIVILEGE - typically a row-level security violation - new RegExp('^42501$') -]; - -export class Connector { - - constructor() { - // Setup a connection to your server for uploads - this.serverConnectionClient = TODO; - } - - async fetchCredentials() { - // TODO logic to fetch a session - return { - endpoint: '[The PowerSync instance URL]', - token: 'An authentication token', - expiresAt: 'When the token expires', - userID: 'User ID to associate the session with' - }; - } - - async uploadData(database) { - const transaction = await database.getNextCrudTransaction(); - - if (!transaction) { - return; - } - - let lastOp = null; - - try { - // Note: If transactional consistency is important, use database functions - // or edge functions to process the entire transaction in a single call. - for (let op of transaction.crud) { - lastOp = op; - // Have your server connection setup before uploading - const table = this.serverConnectionClient.from(op.table); - switch (op.op) { - case UpdateType.PUT: - const record = { ...op.opData, id: op.id }; - const { error } = await table.upsert(record); - break; - case UpdateType.PATCH: - await table.update(op.opData).eq('id', op.id); - break; - case UpdateType.DELETE: - await table.delete().eq('id', op.id); - break; - } - } - - await transaction.complete(); - } catch (ex) { - console.debug(ex); - if (typeof ex.code == 'string' && FATAL_RESPONSE_CODES.some((regex) => regex.test(ex.code))) { - /** - * Instead of blocking the queue with these errors, - * discard the (rest of the) transaction. - * - * Note that these errors typically indicate a bug in the application. - * If protecting against data loss is important, save the failing records - * elsewhere instead of discarding, and/or notify the user. - */ - console.error(`Data upload error - discarding ${lastOp}`, ex); - await transaction.complete(); - } else { - // Error may be retryable - e.g. network error or temporary server error. - // Throwing an error here causes this call to be retried after a delay. - throw ex; - } - } - } -} +export const openDatabase = async () => { + const PowerSync = new RNQSPowerSyncDatabaseOpenFactory({ + schema: AppSchema, + dbFilename: 'test.sqlite' + //location: 'optional location directory to DB file' + }).getInstance(); -``` + await PowerSync.init(); -## Create a PowerSync Connection + // Run local statements. + await PowerSync.execute('INSERT INTO customers(id, name, email) VALUES(uuid(), ?, ?)', ['Fred', 'fred@example.org']); +}; - Use a DB adapter to connect to a SQLite DB: +class Connector { + async fetchCredentials() { + // TODO logic to fetch a session + return { + endpoint: '[The PowerSync instance URL]', + token: 'An authentication token', + expiresAt: 'When the token expires', + userID: 'User ID to associate the session with' + }; + } + + async uploadData(database) { + // Upload local changes to backend, see docs for example + } +} -```JavaScript -// lib/setup-powersync.js -import { RNQSPowerSyncDatabaseOpenFactory } from '@journeyapps/powersync-sdk-react-native'; -import { Connector } from './Connector'; -import { AppSchema } from './Schema'; - -/** - * This uses React Native Quick SQLite to open a SQLite DB file - */ -const factory = new RNQSPowerSyncDatabaseOpenFactory({ - schema: AppSchema, // Which was created earlier - dbFilename: 'test.sqlite' - //location: 'optional location directory to DB file' -}); - -export const PowerSync = factory.getInstance(); - -export const setupPowerSync = async () => { +export const connectPowerSync = async () => { const connector = new Connector(); // Which was declared above - await PowerSync.init(); await PowerSync.connect(connector); }; -``` - -# Using PowerSync - -Once the PowerSync instance is configured you can start using the SQLite DB functions - -### Fetching an Item - -```JSX -// TodoItemWidget.jsx -import {Text} from 'react-native'; - -export const TodoItemWidget = ({id}) => { - const [todoItem, setTodoItem] = React.useState([]); - const [error, setError] = React.useState([]); - - React.useEffect(() => { - // .get returns the first item of the result. Throws an exception if no result is found. - PowerSync.get('SELECT * from todos WHERE id = ?', [id]) - .then(setTodoItem) - .catch(ex => setError(ex.message)) - }, []); - - return {error || todoItem.description} -} -``` - -### Querying Items - -```JSX -// ListsWidget.jsx -import {FlatList, Text} from 'react-native'; - -export const ListsWidget = () => { - const [lists, setLists] = React.useState([]); - - React.useEffect(() => { - PowerSync.getAll('SELECT * from lists').then(setLists) - }, []); - - return ( ({key: list.id, ...list}))} - renderItem={({item}) => {item.name}} - />) -} -``` - -### Querying with React Query -The PowerSync instance can be used with [React Query](https://tanstack.com/query/v3/docs/react/quick-start). The example below omits the necessary provider setup (see Quickstart). - -```JSX -// ListsWidget.jsx -import {useQuery} from 'react-query'; -import {FlatList, Text} from 'react-native'; - -export const ListsWidget = () => { - const {data: lists} = useQuery({ - queryKey: 'lists', - queryFn: async () => PowerSync.getAll('SELECT * from lists'), - }); - - return ( ({key: list.id, ...list}))} - renderItem={({item}) => {item.name}} - />) -} -``` - -### Watching Queries - -A watch API allows for queries to be executed whenever a change to a dependant table is made. - -```JSX -// ListsWidget.jsx -import {FlatList, Text} from 'react-native'; - - export const ListsWidget = () => { - const [lists, setLists] = React.useState([]); - React.useEffect(() => { - const abortController = new AbortController(); - (async () => { - for await(const update of PowerSync.watch('SELECT * from lists', [], {signal: abortController.signal})) { - setLists(update) - } - })(); - - return () => { - abortController.abort(); - } - }, []); - - return ( ({key: list.id, ...list}))} - renderItem={({item}) => {item.name}} - />) -} -``` - -### Mutations - -The `execute` method can be used for executing single SQLite statements. - -```JSX -// ListsWidget.jsx -import {Alert, Button, FlatList, Text, View} from 'react-native'; - -export const ListsWidget = () => { - // Populate lists with one of methods listed above - const [lists, setLists] = React.useState([]); +export const CustomerListDisplay = () => { + const customers = usePowerSyncWatchedQuery('SELECT * from customers'); return ( - ({key: list.id, ...list}))} - renderItem={({item}) => ( - {item.name} -