Introduction to FSL
Fauna Schema Language (FSL) enables you to express your schema in a human readable form, using the Dashboard, which enables you to manage your schema and data model as code in a revision control system of your choice. By managing your schema as code, your data modeling and schema migrations can be done in accordance with the same DevOps principles you use to manage application code.
An FSL schema definition is a collection of schema item definitions. Each schema item is described by a name and properties, and can reference other schema items, including forward referencing. You define and manage your data model using FSL.
The FSL declarative language makes it possible for to define all your database elements as a set of files. Supported elements include:
E-commerce data model example
These are examples that might represent an e-commerce data model.
Customer collection
{
id: "357949884874096724",
coll: Customer,
ts: Time("2023-02-28T23:21:01.810Z"),
name: "Susan Nelson",
address: {
street: "5677 Strong St.",
state: "CA",
city: "San Rafael",
country: "USA",
zip: 97652
}
}
Product collection
{
id: "357949884859416660",
coll: Product,
ts: Time("2023-02-28T23:21:01.810Z"),
name: "The Doors of Stone",
type: "book",
price: 3000,
quantity: 0
}
Order collection
{
id: "357949884877242452",
coll: Order,
ts: Time("2023-02-28T23:21:01.810Z"),
customer: Customer.byId("357949884876193876"),
product: Product.byId("357949884871999572"),
date: "2003-03-03"
}
Index definition
An application requirement is to allow end-users to search for products by
product category. You can define an
index, which gives improved performance
over the where()
method, to facilitate that query pattern and give the index
the name byCategory
:
collection Product {
indexes byCategory {
terms: [ .category ]
values: [ desc(.price), .name ]
}
}
The "byCategory" index indexes on the category field as an index term and is ordered by descending price and ascending name. The index allows an application to efficiently query for products by product category, such as electronics, books, or hardware, and returns the result set in price and name order, listing the most expensive products by name first.
Unique constraint definition
FSL also allows you to constrain the possible values a given field can have to ensure that the document fields have correct and valid values according to your business logic.
The values can be a unique constraint. For example, the example e-commerce application requires that each product in the Product collection has a unique name. You can enforce that constraint with the following collection definition:
collection Product {
name: string
category: string
price: int
quantity: int
indexes byCategory {
terms: [ .category ]
values: [ desc(.price), .name ]
}
unique: [ .name ]
}
The unique
keyword accepts an array of field names so you can express all
of your unique constraints in a single statement.
Define a function
User-defined functions (UDFs) are conceptually similar to stored procedures and can encapsulate sophisticated transactions in manageable and maintainable units. Because UDFs execute in the Fauna service, they reduce the complexity of client-side applications.
This example adds a UDF to the e-commerce model to show how you define a UDF using FSL:
function getOrdersByCustomer( string: name ): Set<Order> {
Order.all(.customer.name == name)
}
Notice that the Fauna function syntax is similar to typescript syntax. The
function getOrderByCustomer
takes a parameter name, which is a
String type, and returns a Set of Order documents.
UDFs can have attributes that give them added utility. UDF attributes are
defined using annotations. This example assigns a role to a function using
the @role
annotation so the function executes with the permissions
associated with the ShopAdmin
role:
@role(ShopAdmin)
{
author: "A. Lovelace",
}
function getOrdersByCustomer( string: name ): Set<Order> {
Order.all.where(.customer.name == name)
})
Define a role
When managing access to a database, Fauna users can use the out-of-the-box roles or define custom roles.
To offer the e-commerce application as a retail platform for third-party shops,
you might want to define a new type of user, such as a shop administrator, and
limit shop administrator access to the Customer, Product, and Order
collections. To do this, add a shopAdmin
field to some Customer
documents. Documents that have a value of true
for this field are ShopAdmin
customers.
In defining the role, you can give ShopAdmin
customers greater privileges,
including adding or modifying their own products to the Product collection but
being prohibited from modifying the products of shops they aren’t an
administrator of:
role AmalgamatedWidgetAdmin {
membership Customer {
predicate (.shopAdmin == true) }
privileges Product {
read
create {
predicate (product => product.shopId == Query.identity()!.shopId)
}
write {
predicate ((old, new) => {
old.shopId == Query.identity()!.shopId && old.shopId == new.shopId
})
}
}
}
}
The membership property defines the restricted privileges shop
administrators have. Only customers where the ShopAdmin
field is true
have membership in this role.
The write
privilege takes the current and new state.
Shop administrators can read any product in the Product collection but may only create and write products in their own shop.
Is this article helpful?
Tell Fauna how the article can be improved:
Visit Fauna's forums
or email docs@fauna.com
Thank you for your feedback!