Skip to main content
Bijection provides you with a fully featured backend with cloud functions, database, scheduling, and a sync engine that keeps your frontend and backend up to date in real-time. Today, in about 10 lines of code, we’ll build a backend that reads and writes to the database and automatically updates all users in a chat app. After that we’ll see how to connect to external services and setup your product for success and scale.

Start developing with Bijection

Install the Bijection command line on macOS or Linux:
See Install Bijection for PATH setup. The backend needs nothing else, but the chat app’s web frontend is a React app built with Vite, so you also need Node.js with a package manager: pnpm, bun, yarn or npm.
First, create the app from the react-vite template:
bijection init writes a React app and a folder called bijection/, where you’ll write your backend code, and installs the app’s dependencies with your package manager. Log this machine in to Bijection (once per machine), then start the backend:
Bijection’s hosted service is not yet open for public sign-up, and the CLI has no default management service. Before running bijection login, set BIJECTION_MANAGEMENT_URL to the management service URL you were given, in your shell’s startup file so that every later bijection command sees it too:
The first run of bijection dev creates your backend, a dev deployment, and writes its URL to .env.local, where the app finds it. Make sure you keep bijection dev running in the background throughout this tutorial. It keeps your backend in sync with your local codebase, pushing the bijection/ folder on every change. In another terminal, start the web app with the command bijection init printed for your package manager, for example:
The template came with a sample message list, in bijection/messages.ts and bijection/schema.ts. In this tutorial you write the chat backend yourself, so delete them:
The app at localhost:5173 still calls the functions you deleted, so it shows an error until you connect it to your own functions below. First, here’s a quick summary of how Bijection works.

How Bijection works

Database. The Bijection database is a document-relational database, which means you have tables with JSON like documents in them. All documents have an auto-generated _id that can be used to create relations between documents. You interact with the database through mutation and query functions that are written entirely in TypeScript. Mutation functions. Mutations are TypeScript functions that update the database. All mutation functions in Bijection run as a database transaction. So either all the changes are committed, or none are. Query functions. Queries are TypeScript functions that can only read from the database. As we’ll see in a bit, you subscribe to them from your frontend to keep your app automatically up to date. Your frontend registers to listen to query updates through the client library. The client libraries talk to Bijection via WebSockets for fast realtime updates. The sync engine reruns query functions when any input to the function changes, including any changes to the documents in the database that the query reads. It then updates every app listening to the query. The sync engine is the combination of queries, mutations and the database. Now, let’s dive into the code!

Your first mutation

Create a new file in your bijection/ folder called chat.ts. This is where you’ll write your Bijection backend functions for this application. Add the following to your bijection/chat.ts file.
Let’s break this down:
  1. You’ve added a new backend mutation function called sendMessage and exposed it as a public api.
  2. The whole function automatically runs as a transaction that will roll back if an exception is thrown.
  3. Since this is just a TypeScript function you can drop console.log lines to do simple debugging on the server.
  4. args: ensures the function arguments are two strings named user and body, both as types and runtime values.
  5. ctx.db.insert tells Bijection to insert a new message document into the table.
Now, let’s connect this mutation to your web app. Replace your src/App.tsx file with this chat UI:
There are two steps to call a mutation in your frontend:
  1. const sendMessage = useMutation(api.chat.sendMessage); gives your frontend app a handle to the mutation function
  2. await sendMessage({ user: NAME, body: newMessageText }); calls the mutation with the proper parameters.
This is a good time to open up the Bijection console. Open a new browser window and go to https://console.bijection.com and find your new project. Go to the “Data” screen. So far, there is no data in your database. Keep your chat app and console windows open side by side. Now try to send some messages from your chat app. You’ll notice new chat messages showing up live in the messages table. Bijection automatically created a messages table when you sent the first message. In Bijection, schemas are optional. Eventually, you’ll want to enforce the structure of your tables, but for the purposes of the tutorial we’ll skip this. In the console you can also go to the logs screen and see every call to the mutation as you ran with the log line we added earlier. The logs screen is a critical part of debugging your backend in development. You’ve successfully created a mutation function, which is also a database transaction, and connected it to your UI. Now, let’s make sure your app can update live the same way the console is updating live.

Your first query

Update your bijection/chat.ts file like this:
Let’s break this down:
  1. You’ve added a new backend query function called getMessages and exposed it as a public api.
  2. Since this is a query function, the ctx.db in this function only lets you read data.
  3. In the first line of the handler you are querying the most recent 50 messages from newest to oldest.
  4. In the second line you’re reversing the list using plain old TypeScript.
Now update src/App.tsx to read from your query:
That one useQuery line is doing a lot of work automatically for you. It’s telling the Bijection client library to subscribe to your getMessages function. Anytime there are new messages to show the query function is automatically rerun. The result is put in const messages variable and React rerenders your UI component to show the latest messages. That’s it. Now go back to your app and try sending messages. Your app should be showing live updates as new messages arrive:

Don’t believe it? Try opening two chat windows side by side and send some messages:

What you built

With just a few lines of code you’ve built a live updating chat app.
  1. You created a mutation TypeScript function that, in a transaction, adds new chat messages to your database.
  2. You created a query TypeScript function updates your app with the latest data.
  3. You used the client library that keeps your frontend in live sync with the backend.
You’ve learned the fundamentals of Bijection and the sync engine that powers everything.

Next up

In this tutorial we just touched on the very basics. It’s ok to just stop here and go explore the rest of the docs, including efficient queries via indexes and traversing relationships through joins. If you’re deeply curious about how Bijection works, you can read this excellent deep dive. But if you want to see how to call external services and build sophisticated backend workflows, jump into the next section →.

Calling external services

Extend your chat app by calling external APIs using Bijection actions and the scheduler to integrate Wikipedia summaries into your application.