Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

logux/docs

Repository files navigation

Logux

Logux is a flexible JS framework to make local-first sync engine with real-time updates, offline-first, CRDT, an optimistic UI.

  • Instead of other local-first solutions, it is not a database, but a framework to build sync engines with specific needs of your project.
  • No vendor lock-in. It works with any database and in any cloud.
  • Great TypeScript support with end-to-end type checking from client to server.
  • We thought about many production-ready problems like monitoring, scaling, outdated clients, authentication, rich test API.
  • Optional end-to-end encryption.
  • Just extra 7 KB in client-side JS bundle.

Ask your questions at community or commercial support.

Next chapter

Sponsored by Evil Martians

Client Example

Using Logux Client:

React client
import { syncMapTemplate } from '@logux/client'
export type TaskValue = {
 finished: boolean
 text: string
 authorId: string
}
export const Task = syncMapTemplate<TaskValue>('tasks')
export const ToDo = ({ userId }) => {
 const tasks = useFilter(Task, { authorId: userId })
 if (tasks.isLoading) {
 return <Loader />
 } else {
 return <ul>
 {tasks.map(task => <li>{task.text}</li>)}
 </ul>
 }
}
export const TaskPage = ({ id }) => {
 const client = useClient()
 const task = useSync(Task, id)
 if (task.isLoading) {
 return <Loader />
 } else {
 return <form>
 <input type="checkbox" checked={task.finished} onChange={e => {
 changeSyncMapById(client, Task, id, { finished: e.target.checked })
 }}>
 <input type="text" value={task.text} onChange={e => {
 changeSyncMapById(client, Task, id, { text: e.target.value })
 }} />
 </form>
 }
}
Vue client

Using Logux Vuex:

<template>
 <h1 v-if="isSubscribing">Loading</h1>
 <div v-else>
 <h1>{{ counter }}</h1>
 <button @click="increment"></button>
 </div>
</template>
<script>
import { computed } from 'vue'
import { useStore, useSubscription } from '@logux/vuex'
export default {
 setup () {
 // Inject store into the component
 let store = useStore()
 // Retrieve counter state from store
 let counter = computed(() => store.state.counter)
 // Load current counter from server and subscribe to counter changes
 let isSubscribing = useSubscription(['counter'])
 function increment () {
 // Send action to the server and all tabs in this browser
 store.commit.sync({ type: 'INC' })
 }
 return {
 counter,
 increment,
 isSubscribing
 }
 }
}
</script>
Pure JS client

You can use Logux Client API with any framework:

client.type('INC', (action, meta) => {
 counter.innerHTML = parseInt(counter.innerHTML) + 1
})
increase.addEventListener('click', () => {
 client.sync({ type: 'INC' })
})
loading.classList.add('is-show')
await client.sync({ type: 'logux/subscribe' channel: 'counter' })
loading.classList.remove('is-show')

Server Example

Using Logux Server:

addSyncMap<TaskValue>(server, 'tasks', {
 async access (ctx, id) {
 const task = await Task.find(id)
 return ctx.userId === task.authorId
 },
 async load (ctx, id, since) {
 const task = await Task.find(id)
 if (!task) throw new LoguxNotFoundError()
 return {
 id: task.id,
 text: ChangedAt(task.text, task.textChanged),
 finished: ChangedAt(task.finished, task.finishedChanged),
 }
 },
 async create (ctx, id, fields, time) {
 await Task.create({
 id,
 authorId: ctx.userId,
 text: fields.text,
 textChanged: time,
 finished: fields.finished,
 finishedChanged: time
 })
 },
 async change (ctx, id, fields, time) {
 const task = await Task.find(id)
 if ('text' in fields) {
 if (task.textChanged < time) {
 await task.update({
 text: fields.text,
 textChanged: time
 })
 }
 }
 if ('finished' in fields) {
 if (task.finishedChanged < time) {
 await task.update({
 finished: fields.finished,
 finishedChanged: time
 })
 }
 }
 }
 async delete (ctx, id) {
 await Task.delete(id)
 }
})
addSyncMapFilter<TaskValue>(server, 'tasks', {
 access (ctx, filter) {
 return true
 },
 initial (ctx, filter, since) {
 let tasks = await Tasks.where({ ...filter, authorId: ctx.userId })
 return tasks.map(task => ({
 id: task.id,
 text: ChangedAt(task.text, task.textChanged),
 finished: ChangedAt(task.finished, task.finishedChanged),
 }))
 },
 actions (filterCtx, filter) {
 return (actionCtx, action, meta) => {
 return actionCtx.userId === filterCtx.userId
 }
 }
})

About

Guide, recipes, and protocol specifications for Logux

Resources

License

Stars

Watchers

Forks

Contributors 32

AltStyle によって変換されたページ (->オリジナル) /