rocicorp / rails

Replicache on Rails

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Replicache on Rails

Generates a CRUD-style interface for Replicache and Reflect, with optional schema validation.

Install

npm install --save-dev @rocicorp/rails

Usage

1. Define Entities

// All entities must include at least id:string.
export type Todo = {
  id: string;
  text: string;
  complete: boolean;
  sort: number;
};

2. Generate Helpers

import {generate} from '@rocicorp/rails';

export const {
  put: putTodo,
  get: getTodo,
  list: listTodos,
  // ...
} = generate<Todo>('todo');

3. Build Mutators

The generated functions all have the same signatures as mutators, so they can be used as mutators directly:

import {putTodo, updateTodo, deleteTodo} from './todo';

export const mutators = {
  putTodo,
  updateTodo,
  deleteTodo,
};

You can also compose them to make more advanced mutators:

import {listTodos, updateTodo} from './todo';

async function markAllComplete(tx: WriteTransaction) {
  const todos = await listTodos(tx);
  for (const t of todos) {
    // Mutators are transactional, so this is safe. The entire function will
    // run atomically and `t.complete` cannot change while it is running.
    if (!t.complete) {
      await updateTodo(todo.id, {complete: true});
    }
  }
}

export const mutators = {
  // ...
  markAllComplete,
};

4. Build Subscriptions

The generated functions that are read-only (get, has, list, etc) have the correct signature to be used as subscriptions. So you can use them directly:

// subscribe to a query
const todos = useSubscribe(rep, listTodos, []);

But as with mutators, you can also compose them to make more interesting subscriptions:

async function listIncompleteTodos(tx: WriteTransaction) {
  const todos = await listTodos(tx);
  return todos.filter(t => !t.complete);
}

const incompleteTodos = useSubscribe(rep, listIncompleteTodos, []);

Validation

You can optionally pass generate a validation function as a second parameter. For example, to use Zod as your schema validator:

import * as z from 'zod';
import {generate} from '@rocicorp/rails';

const todoSchema = {
  id: z.string(),
  text: z.string(),
  complete: z.boolean(),
  sort: z.number(),
};

// In this case, the template parameter to generate can be omitted because it
// is inferred from return type of todoSchema.parse().
export const {
  put: putTodo,
  get: getTodo,
  update: updateTodo,
  delete: deleteTodo,
  list: listTodos,
} = generate('todo', todoSchema.parse);

Reference

init(tx: WriteTransaction, value: T): Promise<boolean>

Writes value if it is not already present. If value is already present, does nothing. Returns true if the value was written or false otherwise.

put(tx: WriteTransaction, value: T): Promise<void>

Writes value. If not present, creates, otherwise overwrites.

update(tx: WriteTransaction, value: Update<T>) => Promise<void>

Updates value. Value can specify any of the fields of T and must contain id. Fields not included in value are left unaffected.

All fields in an update are applied together atomically. If the entity does not exist a debug message is printed to the console and the update is skipped.

delete(tx: WriteTransaction, id: string) => Promise<void>

Delete any existing value or do nothing if none exist.

has(tx: ReadTransaction, id: string) => Promise<boolean>

Return true if specified value exists, false otherwise.

get(tx: ReadTransaction, id: string) => Promise<T | undefined>

Get value by ID, or return undefined if none exists.

mustGet(tx: ReadTransaction, id: string) => Promise<T>

Get value by ID, or throw if none exists.

list(tx: ReadTransaction, options?: {startAtID?: string, limit:? number}) => Promise<T[]>

List values matching criteria.

listIDs(tx: ReadTransaction, options?: {startAtID?: string, limit:? number}) => Promise<string[]>

List ids matching criteria.

listEntries(tx: ReadTransaction, options?: {startAtID?: string, limit:? number}) => Promise<[string, T][]>

List [id, value] entries matching criteria.

Upgrade from 0.6

Pluggable Schema

Rails 0.7 made the schema validator pluggable. Instead of passing an instance of zod, pass the parse function.

Before:

export const {
  put: putTodo,
  // ...
} = generate<Todo>('todo', todoSchema);

Now:

export const {
  put: putTodo,
  // ...
} = generate<Todo>('todo', todoSchema.parse);

EntitySchema no longer part of Rails

Because the validator is pluggable it no longer makes sense for Rails to provide entitySchema. So either define it yourself:

const entitySchema = z.object({
  id: z.string(),
});

... or simply add id: z.string() to each of your entity definitions.

Parse called in Debug mode

In 0.6.0, zod was only used if process.env.NODE_ENV !== 'production'. Now that the validator is pluggable, it makes more sense for the app to do this.

Before:

export const {
  put: putTodo,
  // ...
} = generate<Todo>('todo', todoSchema);

Now:

export const {
  put: putTodo,
  // ...
} = generate<Todo>(
  'todo',
  process.env.NODE_ENV !== 'production' ? todoSchema.parse : undefined,
);

About

Replicache on Rails

License:Other


Languages

Language:TypeScript 100.0%