Skip to content

Latest commit

 

History

History
339 lines (246 loc) · 9.39 KB

README.md

File metadata and controls

339 lines (246 loc) · 9.39 KB

Fauna Java sample app

This sample app shows how to use Fauna in a production application.

The app uses Java 17 with Gradle 8.9 and the Fauna v10 JVM driver to create HTTP API endpoints for an e-commerce store. You can use the app's API endpoints to manage products, customers, and orders for the store.

The app uses Fauna schemas and queries to:

  • Read and write data with strong consistency.

  • Define and handle relationships between resources, such as linking orders to products and customers.

  • Validate data changes against business logic.

The app's source code includes comments that highlight Fauna best practices.

Highlights

The sample app uses the following Fauna features:

  • Document type enforcement: Collection schemas enforce a structure for the app's documents. Fauna rejects document writes that don't conform to the schema, ensuring data consistency. Zero-downtime migrations let you safely change the schemas at any time.

  • Relationships: Normalized references link documents across collections. The app's queries use projection to dynamically retrieve linked documents, even when deeply nested. No complex joins, aggregations, or duplication needed.

  • Computed fields: Computed fields dynamically calculate their values at query time. For example, each customer's orders field uses a query to fetch a set of filtered orders. Similarly, each order's total is calculated at query time based on linked product prices and quantity.

  • Constraints: The app uses constraints to ensure field values are valid. For example, the app uses unique constraints to ensure each customer has a unique email address and each product has a unique name. Similarly, check constraints ensure each customer has only one cart at a time and that product prices are not negative.

  • User-defined functions (UDFs): The app uses UDFs to store business logic as reusable queries. For example, the app uses a checkout() UDF to process order updates. checkout() calls another UDF, validateOrderStatusTransition(), to validate status transitions for orders.

Requirements

To run the app, you'll need:

Setup

  1. Clone the repo and navigate to the java-sample-app directory:

    git clone git@github.com:fauna/java-sample-app.git
    cd java-sample-app
  2. If you haven't already, log in to Fauna using the Fauna CLI:

    fauna login
  3. Use the CLI to create the ECommerceJava database:

    # Replace 'us' with your preferred Region Group:
    # 'us' (United States), 'eu' (Europe), or `global` (available to Pro accounts and above).
    fauna database create \
      --name ECommerceJava \
      --database us
  4. Push the .fsl files in the schema directory to the ECommerceJava database:

    # Replace 'us' with your Region Group.
    fauna schema push \
      --database us/ECommerceJava

    When prompted, accept and stage the schema.

  5. Check the status of the staged schema:

    fauna schema status \
      --database us/ECommerceJava
  6. When the status is ready, commit the staged schema to the database:

    fauna schema commit \
      --database us/ECommerceJava

    The commit applies the staged schema to the database. The commit creates the collections and user-defined functions (UDFs) defined in the .fsl files of the schema directory.

  7. Create a key with the server role for the ECommerceJava database:

    fauna query "Key.create({ role: 'server' })" \
      --database us/ECommerceJava

    Copy the returned secret. The app can use the key's secret to authenticate requests to the database.

Add sample data

The app includes a seed script that adds sample documents to the ECommerceJava database. From the root directory, run:

chmod +x ./scripts/seed.sh
FAUNA_SECRET=<secret> ./scripts/seed.sh

You can view documents created by the script in the Fauna Dashboard.

Run the app

The app runs an HTTP API server. From the root directory, run:

FAUNA_SECRET=<secret> ./gradlew bootRun

Once started, the local server is available at http://localhost:8080.

HTTP API endpoints

The app's HTTP API endpoints are defined in *Controller files in the java.sample.controllers.* modules.

An OpenAPI spec and Swagger UI docs for the endpoints are available at:

Make API requests

You can use the endpoints to make API requests that read and write data from the ECommerceJava database.

For example, with the local server running in a separate terminal tab, run the following curl request to the POST /products endpoint. The request creates a Product collection document in the ECommerceJava database.

curl -v \
  http://localhost:8080/products \
  -H "Content-Type: application/json" \
  -d '{
    "name": "The Old Man and the Sea",
    "price": 899,
    "description": "A book by Ernest Hemingway",
    "stockQuantity": 10,
    "category": "books"
  }' | jq .

Expand the app

You can further expand the app by adding fields and endpoints.

As an example, the following steps adds a computed totalPurchaseAmt field to Customer documents and related API responses:

  1. If you haven't already, add the sample data:

    FAUNA_SECRET=<secret> ./scripts/seed.sh

    If the app server is running, stop the server by pressing Ctrl+C.

  2. In schema/collections.fsl, add the following totalPurchaseAmt computed field definition to the Customer collection:

    collection Customer {
      ...
      // Use a computed field to get the set of Orders for a customer.
      compute orders: Set<Order> = (customer => Order.byCustomer(customer))
    
    + // Use a computed field to calculate the customer's cumulative purchase total.
    + // The field sums purchase `total` values from the customer's linked Order documents.
    + compute totalPurchaseAmt: Number = (customer => customer.orders.fold(0, (sum, order) => {
    +   let order: Any = order
    +   sum + order.total
    + }))
      ...
    }
    ...

    Save schema/collections.fsl.

  3. Push the updated .fsl files in the schema directory to the ECommerceJava database to stage the changes:

    fauna schema push \
      --database us/ECommerceJava

    When prompted, accept and stage the schema.

  4. Check the status of the staged schema:

    fauna schema status \
      --database us/ECommerceJava
  5. When the status is ready, commit the staged schema changes to the database:

    fauna schema commit \
      --database us/ECommerceJava
  6. In CustomersController.java, add the totalPurchaseAmt field to the response FQL template:

    // Project Customer document fields for consistent responses.
    private final Query response = fql("""
              customer {
                id,
                name,
                email,
    +           address,
    +           totalPurchaseAmt
              }
            """);
    `;

    Save CustomersController.java.

    Customer-related endpoints use this template to project Customer document fields in responses.

  7. In Customer.java, add the totalPurchaseAmt field and a related getter to the Customer class:

    private String email;
    private Address address;
    + private int totalPurchaseAmt;
    +
    + public int getTotalPurchaseAmt() {
    +     return totalPurchaseAmt;
    + }
    
    public String getId() {
        return id;
    }

    Save Customer.java.

    Customer-related endpoints return responses that conform to the Customer class.

  8. Start the app server:

    FAUNA_SECRET=<secret> ./gradlew bootRun
  9. With the local server running in a separate terminal tab, run the following curl request to the POST /customers endpoint:

    curl -v http://localhost:8080/customers/999 | jq .

    The response includes the computed totalPurchaseAmt field:

    {
      "id": "999",
      "name": "Valued Customer",
      "email": "fake@fauna.com",
      "address": {
        "street": "Herengracht",
        "city": "Amsterdam",
        "state": "North Holland",
        "postalCode": "1015BT",
        "country": "Netherlands"
      },
      "totalPurchaseAmt": 36000
    }