Skip to content

Get started

Pick the stack you work in. Each guide builds the same bookshop: books with an author and a stock count, a cost price only staff can see, and two commands, buy and restock. Every server listens on port 4000 and speaks the same protocol, so the React app works just as well against the Kotlin server as against the TypeScript one.

The code on these pages comes from the example projects, whose tests run on every change to the repository. Not ready to install anything? The playground runs the bookshop server in your browser.

The schema you will serve

Every guide starts from this file. It is the contract: the server is checked against it, and clients learn from it what they can ask for.

rayfold
entity Author {
  id: ID
  name: String
}

entity Book @cache(maxAge: 60s, scope: public) {
  id: ID
  title: String
  stock: Int
  author: Author
  """What the shop paid. Only staff can see it."""
  costPrice: Decimal? @allow(read: viewer.role == "staff")
}

error OutOfStock { bookId: ID, available: Int }

event StockChanged { bookId: ID, stock: Int }

query book(id: ID): Book?
query books(page: PageArgs = { first: 20 }): Page<Book>

"""Take copies off the shelf. Fails if there are not enough."""
command buy(bookId: ID, qty: Int = 1 @range(min: 1, max: 10)): Book
  throws OutOfStock
  emits StockChanged
  @allow(write: viewer != null)

command restock(bookId: ID, qty: Int @range(min: 1, max: 1000)): Book
  emits StockChanged
  @allow(write: viewer.role == "staff")
  • Fields are required unless they end in ?.
  • @allow says who may read costPrice or run restock, and the server enforces it on every request.
  • buy declares that it can fail with OutOfStock, so clients can handle that case by name.
  • Page<Book> gives books a cursor, a total and a hasMore flag without any extra code.

Released under the Apache-2.0 license.