See the basics of InstantDB in action
# Clone repo
git clone https://github.com/instantdb/instant-examples
# Navigate into the todos example
cd instant-examples/todos
# Install dependencies
pnpm i
pnpx instant-cli login
pnpx instant-cli init
instant.schema.ts that you can push to your app.
You may have already pushed this during init in the previous step. If you
answered 'no' to the prompt during init, or if you're unsure whether you pushed
the schema, you can push it now.pnpx instant-cli push
pnpm run dev
instant.schema.ts. By defining
our schema in code we can keep it in version control and get type-safety
throughout our app.$users and $files.isCompleted, and text with their types.todos which will be used to who's viewing the
todo app. More on that later!const _schema = i.schema({
entities: {
// ... built-in entities
todos: i.entity({
isCompleted: i.boolean().optional(),
text: i.string().optional(),
}),
},
links: {
// ... built-in links
},
rooms: {
todos: {
presence: i.entity({}),
},
},
});
init from
@instantdb/react. We create a lib/db.ts file to initialize our connection to
the database and export it for use throughout our app.import { init } from '@instantdb/react';
import schema from '../instant.schema';
export const db = init({
appId: process.env.NEXT_PUBLIC_INSTANT_APP_ID!, // connect to your Instant app
schema, // our schema from earlier, pass it here to enable type-safety
});
init supports a few
additional options for customizing the database connection. We won't need them
for this app, but you can learn more in the init docs.useQuery hook. Not only will this
fetch the data, but it will also subscribe to real-time updates so that when
todos are added, updated, or deleted, our UI will automatically update to
reflect those changes.// This is similar to writing `SELECT * FROM todos` in SQL
const { isLoading, error, data } = db.useQuery({ todos: {} });
isLoading and error
states to handle loading and error states in our UI.// We can show a spinner or placeholder while fetching todos
// But sometimes it's nice to just render nothing to avoid layout flickers
if (isLoading) {
return;
}
// There can be an error fetching data. For example if someone has never
// fetched todos before and go offline. Guarding against these error can provide
// helpful feedback to users.
if (error) {
return <div className="p-4 text-red-500">Error: {error.message}</div>;
}
data object and
render them in our UI.function App() {
// ... After loading and error handling
// Unpack todos from data
const { todos } = data;
return (
// ... Render the todo list somewhere in our app
<TodoList todos={todos} />
);
}
// We can now render todos as data in the same way we would
// as if they were local data.
function TodoList({ todos }: { todos: Todo[] }) {
return (
<>
{todos.map((todo) => (
// ... Render each todo
))}
</>
);
}
// We can get type-safety for our entities from the schema
// If you change the schema this type will automagically update too!
type Todo = InstaQLEntity<AppSchema, "todos">;
db.transact with one or more db.txdb.transact as a single "transaction" that can contain multiple
operations. Each operation is represented by a db.tx.// id() generates a unique ID for the new todo
function createTodo(text: string) {
db.transact(db.tx.todos[id()].create({ text }));
}
function deleteTodo(todoId: string) {
db.transact(db.tx.todos[todoId].delete());
}
function toggleTodo(todo: Todo) {
db.transact(db.tx.todos[todo.id].update({ isCompleted: !todo.isCompleted }));
}
function deleteCompletedTodos(todoIds: string[]) {
const txs = todoIds.map((todoId) => db.tx.todos[todoId].delete());
db.transact(txs);
}
function toggleAllTodos(todos: Todo[]) {
const notCompletedTodos = todos.filter((t) => !t.isCompleted);
if (notCompletedTodos.length > 0) {
db.transact(
notCompletedTodos.map((t) =>
db.tx.todos[t.id].update({ isCompleted: true }),
),
);
} else {
db.transact(
todos.map((t) => db.tx.todos[t.id].update({ isCompleted: false })),
);
}
}
setState works in React, but instead of just updating local state
we are updating the database!db.rooms.usePresence.// Get number of users viewing this room
// Add 1 to include self
const { peers } = db.rooms.usePresence(db.room('todos'));
const numUsers = 1 + Object.keys(peers).length;
todo room we defined in our schema earlier.
This means whenever someone else opens or closes the app, our peers object
will automatically update to reflect their presence.