Additional Features
Next.js Support
Next.js is a widely used framework for full-stack TypeScript apps, that gives you a set of shared conventions for creating your frontend and backend.
By default, Next.js apps use CLI commands like next dev
to launch the app.
Backend (/api) <----- Next.js server (`next dev`) <----- Browser
|
Frontend (/app) <---
You can use Canvas with Next.js, by creating a custom server.ts file. This will replace the next start
and next dev
commands with your own server, but the rest of your app will remain unchanged.
Next.js API server (`next()`) <----- Express server <----- Browser
|
Canvas class (`new Canvas()`) <---
First, follow these instructions to set up a custom server. This will give you a handle
function that you need to call inside an Node.js server.
From there, initialize a Canvas class, and make it available inside the custom server using the createAPI
method:
import { Canvas } from "@canvas-js/core"
import { createAPI } from "@canvas-js/core/api"
// ...
nextApp.prepare().then(async () => {
const canvasApp = await Canvas.initialize({
path: process.env.DATABASE_URL,
contract: MyApp,
})
const expressApp = express()
expressApp.use("/api", createAPI(canvasApp))
// Next.js handler goes here
expressApp.all("*", (req, res) => {
return handle(req, res)
})
// expressApp.listen(...)
})
To deploy the application, you'll need a cloud provider that can support persistent processes, like Railway or Fly.io.
You can deploy to Railway out of the box by updating just the dev
and start
commands in your package.json:
{
"scripts": {
"build": "next build",
"dev": "tsx server.ts",
"lint": "next lint",
"start": "NODE_ENV=production tsx server.ts",
"typecheck": "npx tsc --watch"
},
}
Refer to the chat-next package for another example.
Synchronous Loader
In Next.js, React Native, or bundlers targeting earlier versions of JS, you might not be able to use the top-level await feature to initialize a Canvas contract.
But you still might want to create a Canvas instance as an export. To work around this, we provide an experimental @canvas-js/core/sync
module:
import { Canvas } from "@canvas-js/core/sync";
export const app = new Canvas({
topic: "pogiciv.vercel.app",
contract: MyApp,
})
This constructor wraps and proxies the regular Canvas
object, and defers calls to the actions
and db
APIs.
Other calls on the Canvas object, like .listen() or .startLibp2p(), will not work until await Canvas.initialize()
is completed.
Sync Status API
The app.syncStatus
property exposes the sync status of a browser-to-server application.
Browser-to-server sync runs over multiple sessions, in case a sync is interrupted or internet connectivity is momentarily lost. Each sync session either runs to completion, or times out after a predetermined period, triggering another sync session.
export type ClientSyncStatus =
"offline" | "starting" | "inProgress" | "complete" | "error"
- The sync status is initialized as "offline".
- When an initial connection is established, the sync status is set to "starting".
- When part of a longer sync is completed, the sync status is set to "inProgress".
- When a sync session runs to completion, the sync status is set to "complete".
You can also observe changes to the sync status by listening to the app's message log:
app.messageLog.addEventListener("connect", updateSyncStatus)
app.messageLog.addEventListener("disconnect", updateSyncStatus)
app.messageLog.addEventListener("sync:status", updateSyncStatus)