reym
Example Application

2 // GraphQL Schema Definition

Before you start writing some code, we start by defining the structure of the data in our application.

Remember, we want to build a simple application for pizza orders.

CRUD

An order is placed by a customer. Since the customer data does not involve any complex business logic, we simply need to create and list them.

CRUD types are ideal for this scenario as they offer a straightforward way to handle create, read, update, and delete operations.

To achieve this, we define a Customer type in schema/crud/customer.graphql:

customer.graphql
type Customer @crudType {
    name: String! @index @unique
}

For a detailed explanation please refer to the CRUD Schema Definition.

Projections

Ingredients

In our pizza order application, managing ingredients is crucial as they involve specific business logic. To handle this efficiently, we use projections.

Each ingredient has an amount property that tracks its availability. This amount is automatically reduced whenever an order is placed.

To implement this functionality, we define a projection for ingredients in schema/projections/ingredient.graphql:

ingredient.graphql
type Ingredient
    @upsertOn(
        on: { topic: "demo", eventTypes: ["CreateIngredient"] }
        identifyBy: { payload: ["id"] }
    )
    @upsertOn(
        on: { topic: "demo", eventTypes: ["PlaceOrder"] }
        identifyBy: { payload: ["ingredients"] }
    ) {
    name: String! @index
    amount: Int!
        @from(events: ["CreateIngredient"], value: "payload.amount")
        @from(events: ["PlaceOrder"], value: "projection.amount - 1")
        @index
}

The Ingredient projection is designed to handle ingredient management efficiently. It creates new ingredients when a CreateIngredient event occurs and automatically decreases the amount of each ingredient by 1 whenever a PlaceOrder event is processed. This ensures accurate tracking of ingredient availability.

For a detailed explanation please refer to the Projections Schema Definition.

Orders

In the future, we anticipate introducing more complex business logic for orders. To accommodate this, we will use a projection for orders.

Currently, we only need orders to be created when PlaceOrder events occur.

To implement this functionality, we define a projection for orders in schema/projections/order.graphql:

order.graphql
type Order
    @upsertOn(on: { topic: "demo", eventTypes: ["PlaceOrder"] }, identifyBy: { payload: ["id"] }) {
    customer: Customer!
    ingredients: [Ingredient!]!
}

For a detailed explanation, please refer to the Projections Schema Definition.

Additionally, we need an optimized way to list all placed orders efficiently. To achieve this, we define a view projection for placed orders:

order.graphql
type PlacedOrder @view(sqlFile: "./placed_order.sql") {
    id: ID!
    name: String!
    date: DateTime! @index
    ingredients: [String!]!
}

This view projection uses a QSL query to generate its data:

placed_order.sql
SELECT 
	o.id as id,
	c.name as name,
	o.createdat as date,
  	jsonb_agg(i.name) AS ingredients
FROM orderview AS o
INNER JOIN customerview AS c ON o.customer = c.id
INNER JOIN ingredientview AS i ON o.ingredients::jsonb ? i.id
GROUP BY o.id, c.name, o.createdat
ORDER BY o.createdat ASC

For a detailed explanation please refer to the @view documentation.

Auth Permissions

For the purpose of a simple demo we do not handle permissions and authentication. Therefore the permission enum in schema/auth/permissions.graphql is just a placeholder:

permissions.graphql
enum Permission {
    TODO
}