Skip to content

Latest commit

 

History

History
2149 lines (1430 loc) · 55.7 KB

File metadata and controls

2149 lines (1430 loc) · 55.7 KB
theme ./theme
title WarpDrive: Set Data to Stun
info Discover WarpDrive, the next-generation data framework that brings universal compatibility and performance to ambitious web applications. This talk explores WarpDrive's schema-driven architecture through building a real application, demonstrating how to boldly go where your data has never gone before with a truly framework-agnostic layer. Learn how WarpDrive's TypeScript-first approach delivers enterprise-grade features while eliminating traditional data management complexity.
author Krystan HuffMenne
keywords WarpDrive,EmberData,JavaScript,TypeScript,Framework,Data Layer,EmberFest
lineNumbers true
drawings
enabled persist
true
false
transition slide-left
mdc true
favicon ./favicon.png
selectable true

WarpDrive: Set Data to Stun

EmberFest 2025

Krystan HuffMenne • Staff Engineer @ AuditBoard


layout: default

WarpDrive logo
SYSTEM STATUS: READY
WARP CORE: ONLINE
DATA LAYER: ONLINE STUN

layout: two-cols

Your Captain for This Mission

After Party talking points:

Portland, Oregon

Answers to “mom”

Staff Engineer @ AuditBoard

EmberData/WarpDrive & Tooling Teams

Ember veteran since v2

::right::

Krystan's kids on the first day of school. Kindergartner is wearing an Ember backpack

layout: center

The Evolution Continues

Where are we boldly going next?


layout: section title: 'Episode 1: "What is WarpDrive?"'

Episode 1

"Computer, define 'WarpDrive'"


WarpDrive is...

...the lightweight data framework for ambitious web applications.

Universal

Works with any framework (Ember, React, Vue, Svelte)

Typed

Fully typed, ready to rock

Performant

Committed to best-in-class performance

Scalable

From weekend hobby to enterprise


"Boldly Go Where Your Data Has Gone Before"

Unlike other data libraries, WarpDrive is built around:

  • Resource-first architecture instead of heavy model inheritance patterns
  • Schema-driven development for consistent, sharable data shapes
  • Universal compatibility vs. framework-specific implementations
  • Fine-grained reactivity that JustWorks™

The Universal Promise — "Separate the Saucer Section!"

packages/
└── shared-data
    ├── @warp-drive/core
    ├── builders/
    ├── handlers/
    ├── schemas/
    ├── store/
    └── types/
apps/
├── emberjs
│   └── @warp-drive/ember
├── react
│   └── @warp-drive/react
├── vue
│   └── @warp-drive/vue
└── api
This isn't just theory — we can literally share our data layer across multiple applications.

layout: section title: 'Episode 2: "Engage! — Setting Up Our Mission"'

Episode 2

"Engage! — Setting Up Our Mission"


TodoMVC: Our Prime Directive

A spec for a simple Todo app

Implemented in multiple frameworks to compare approaches.

TodoMVC UI with Captain Picard's Todo List

As a user I can...

...create a Todo.

...read lists of:
All Todos, Active Todos, and Completed Todos.

...update each Todo.

...delete a Todo.

...perform bulk actions:
toggle and delete.


TodoMVC: Our Prime Directive

Todo resource:
```ts interface Todo { id: string; title: string; completed: boolean; } ```

a string id

a string title attribute

a boolean completed attribute


Our Mission Brief

We'll re-implement the Ember TodoMVC application using:

  • WarpDrive (our starship for data management)
  • {JSON:API} (the gold standard for API communication)
  • TypeScript (because we like our data typed and our code safe)
  • Modern Ember Polaris (the latest and greatest Ember patterns)
(Yes, we're using Vite)
By the end, you'll see how WarpDrive makes data management feel... logical.

layout: center


layout: section title: 'Episode 3: "Request Patterns — Making It So"'

Episode 3

"Request Patterns — Making It So"


The WarpDrive Store — "The Bridge"

It's in command.

<<< @/packages/shared-data/src/stores/index.ts ts {20|20|21|28-37|39-44|46-52}{maxHeight: '200px'}
  • Manages Requests — How we handle requests for data
  • Manages the Cache — How to cache that data
  • Manages Schemas — Schemas for what our data looks like
  • Manages Reactive State — What sort of reactive objects to create for that data

The WarpDrive Store — "The Bridge...Abridged"

import { useRecommendedStore } from '@warp-drive/core';
import { JSONAPICache } from '@warp-drive/json-api';

import { ApiHandler, useApiHandler } from '../handlers/api.ts';
import { FlagSchema } from '../schemas/flag.ts';
import { TodoSchema } from '../schemas/todo.ts';

export default useRecommendedStore({
  // RequestManager config
  handlers: [
    // Use `APIHandler` if `useApiHandler` returns true
    new Gate(ApiHandler, useApiHandler),
    // APIHandler calls `next()` to pass the request to `fetch`
    // Fetch handler is included last by default
  ],
  cache: JSONAPICache, // Cache config
  schemas: [FlagSchema, TodoSchema], // SchemaService config
});

The RequestManager — "The Communications Officer"

It manages all external contact.

<<< @/packages/shared-data/src/stores/index.ts ts {21|21|26|22-27|28|21-28}{maxHeight: '200px'}
  • Fetch Handler — Makes actual network requests (Fetch API + error handling)
  • Request Pipeline — Allows custom handlers for data transformation
  • Cache Integration — Automatically caches responses

{JSON:API} — "The Communicator"

By default, WarpDrive speaks {JSON:API} fluently, giving you:

{
  "data": [
    {
      "type": "todo",
      "id": "1",
      "attributes": {
        "title": "Learn WarpDrive",
        "completed": false
      }
    }
    // ...
  ]
}

Standardized format for:
resources,
relationships,
errors

Built-in conventions for:
pagination,
filtering,
sorting,
sparse fields

WarpDrive supports:
all
of
the
above


Making a Request

import Route from '@my-framework/routing/route';
import { service } from '@my-framework/service';

import type { Store } from '@workspace/shared-data';

export default class ActiveTodos extends Route {
  @service declare store: Store;

  model() {
    return this.store.request({
      method: 'GET',
      url: '/api/todo',
      // Additional options like headers, query params, etc.
    })
  }
}

Request Info

interface RequestInfo extends RequestInit {
  // Standard Fetch API options
  method: 'GET' | 'POST' | 'PATCH' | 'DELETE' | 'PUT'; // etc
  url: string;

  // Caching options
  op?: string;
  cacheOptions?: {
    types?: string[];
    key?: string;
    reload?: boolean;
    backgroundReload?: boolean;
  };

  // Custom handler options
  options?: Record<string, unknown>;

  // ...
}

Request Builders

<<< @/packages/shared-data/src/builders/todo/simple-query.ts ts {8-9|8-9|11|12|14-15}{maxHeight: '300px'}

Our getAllTodos builder

Specifies the request method

Generates the URL

Sets cache options:
'query' op, 'todo' type


Query Builders — Automatic Cache Invalidation

<<< @/packages/shared-data/src/builders/todo/create.ts ts {8-25|8-25|22-23}{maxHeight: '350px'}

Our createTodo builder

Sets cache options:
'createRecord' op, 'todo' type


Request Builders — Utilities

<<< @/packages/shared-data/src/builders/todo/simple-query.ts ts {19-33|19-33|21|21-24,28}{maxHeight: '300px'}
  • Our getCompletedTodos builder
  • buildQueryParams util

layout: section title: 'Episode 4: "Schemas — The Universal Translator"'

Episode 4

"Schemas — The Universal Translator"


Schema-Driven Development

Instead of models with complex inheritance, WarpDrive uses simple, declarative schemas:

<<< @/packages/shared-data/src/schemas/todo.ts ts {all|3|4|5-14}{maxHeight: '300px'}
  • withDefaults sets up defaults, like the id field
  • type defines the resource type
  • fields defines the shape of the resource
  • Check out Mehul's talk about "ReactiveResources & Schema‑Driven Data Handling"

TypeScript Integration

<<< @/packages/shared-data/src/types/todo.ts ts {11-27|11-27|11-15|17-21|23-27}{maxHeight: '380px'}
  • Types for various states:
  • TodoAttributes type
  • Readonly Todo type
  • EditableTodo type
"Data, are you getting readings on this?"
Yes, and they're perfectly structured!

Store: "Request Manager, fetch all Todos. Make it so."
Request Manager:
"Roger that, Captain. Sending request to the API."
Server:
"{JSON:API}$%^$@&$%#&!@*&^%$%%$##@$%^$@&$%#&!@*&!@*! !@*&^%$#@!#&^%%$$@@!!@#$%^&*&%%$##@$%^$%^{/JSON:API}"
Store: "Schema Service, we've received a subspace communication. Can you make sense of this?"
Schema Service: Yes, Captain. Translating now. Here's your array of Todo resources, ready to active!"
Store: "Instantiate ReactiveResource...Engage!"

layout: section title: 'Episode 5: "Reactive UI — Ember Integration"'

Episode 5

"Reactive UI — The Viewscreen"

(Ember Integration)


+ @warp-drive/ember

  • Provides Ember components — For request UX with elegant control flow.
  • A Thin Wrapper — Built on top of @warp-drive/core reactive utilities.
  • Reactive — Leverages Ember's reactivity system for fine-grained updates that JustWork™.

<<< @/apps/emberjs/app/components/todo-app/todo-provider-request-version.gts ts {14-27|14-27}{maxHeight: '460px',lines: false}
  • <Request /> component
  • Loading state
  • Error state
  • Success state
  • Autorefresh
  • ...and more!

layout: center


layout: center


<<< @/apps/emberjs/app/components/todo-app/todo-provider-request-version.gts ts {14-27|14-27}{maxHeight: '460px'}

Universal Framework Support

The core logic stays the same — only the framework integration changes!

import { Request } from '@warp-drive/react';

import { getAllTodos } from '@workspace/shared-data/builders';
import type { Todo } from '@workspace/shared-data/types';

export function TodoProvider() {
  return (
    <Request
      query={getAllTodos()}
      states={{
        loading: ({ state }) => <div>React Loading Spinner!</div>,
        content: ({ result }) => <div>React Todo List</div>,
        error: ({ state }) => (
          <div>
            <h2 class="error-message">Please contact TodoMVC support.</h2>
            <p>
              <button onClick={state.retry}>Or DDOS us!</button>
            </p>
          </div>
        ),
      }}
    />
  );
}
  • @warp-drive/ember
  • @warp-drive/react
  • @warp-drive/tc39-proposal-signals
  • @warp-drive/vue Soon!
  • @warp-drive/svelte Soon!

layout: section title: 'Episode 6: "Data Mutations — Quantum Mechanics"'

Episode 6

"Data Mutations — Quantum Mechanics"


Pessimistic vs. Optimistic

When changing data, you have two choices:

Pessimistic Mutation

Wait for server confirmation before updating UI

Optimistic Mutation

Update UI immediately, then confirm with server

Both have trade-offs

UX vs. consistency

WarpDrive supports both

You choose per request


Pessimistic Mutation

<<< @/apps/emberjs/app/components/todo-app/todo-item.gts gts {308-314|308-314|310}{maxHeight: '200px'}
  • Our patchTodoTitle action
  • Uses our patchTodo builder
  • Pass the Todo and attributes to update
  • When the request resolves, that Todo updates all over your app
  • This seems too easy...

Pessimistic Mutation

<<< @/packages/shared-data/src/builders/todo/update.ts gts {11-20|11-20|25|21-22,26|27-33|21,35|17-37}{maxHeight: '380px'}
  • Our patchTodo builder
  • Specifies the request method
  • Generates the URL
  • Serializes the request body
  • Sets cache options
  • tl;dr: It will update our Todos

layout: center


<<< @/apps/emberjs/app/components/todo-app/todo-item.gts gts {308-314|308-314|310}{maxHeight: '200px'}
<<< @/packages/shared-data/src/builders/todo/update.ts gts {17-37|17-37|21-36}{maxHeight: '460px'}

Locally Optimistic Mutation with Checkout

<<< @/apps/emberjs/app/components/todo-app/todo-item.gts gts {45-47|45-47|155-162|186-192,199-203|191-192}{maxHeight: '380px'}
  • Uses the same patchTodo builder, but this time with a mutated EditableTodo

Locally Optimistic Mutation with Checkout

<<< @/apps/emberjs/app/components/todo-app/todo-list.gts gts {21|21|22|25-31|22-36}{maxHeight: '380px'}
  • By default, resources are immutable
  • To get a mutable version, we use await checkout(todo)
  • Pass the mutable EditableTodo to our <TodoItem /> component
  • <Await /> component handles promise states declaratively

Patching State

<<< @/apps/emberjs/app/components/todo-app/todo-item.gts gts {186-192,199-203|186-192,199-203|194-198}{maxHeight: '380px'}
  • Our patchTodoToggle action
  • Patch the cached filter query documents manually

Patching State

<<< @/packages/shared-data/src/builders/todo/update.ts gts {58-62|58-62|63-64,67-76|63,65,77-85}{maxHeight: '380px'}
  • cache.patch() surgically updates cached documents
  • Add to completed list; remove from active list

layout: center


<<< @/apps/emberjs/app/components/todo-app/todo-item.gts gts {186-203|186-203|191-198}{maxHeight: '460px'}
<<< @/packages/shared-data/src/builders/todo/update.ts gts {17-37|17-37|21-36}{maxHeight: '460px'}
<<< @/packages/shared-data/src/builders/todo/update.ts gts {58-90|58-90|63-85}{maxHeight: '460px'}

Immutability Without the Hassle

Whether you choose"pessimistic" or "locally optimistic" updates:

  • Original data stays immutable
  • Changes are isolated until saved
"Captain, the data has been successfully modified...
...without temporal paradoxes!"
("Fully optimistic" also available)

layout: section title: 'Episode 7: "Performance — Warp 9.8"'

Episode 7

"Performance — Warp 9.8"


Built for Performance

WarpDrive optimizes automatically:

  • Request deduplication — Same request? Use cached result
  • Fine-grained reactivity — Only update what actually changed

layout: center

Live Demo: Scale Pioneers


layout: center


<<< @/apps/emberjs/app/components/todo-app/todo-provider.gts ts {28-51|28-51|30-47|45}{maxHeight: '460px',lines:false}

<Paginate /> component
(coming soon for real)

Loading and error states

Success state
(display all loaded data or just current page)

Pagination controls

Autorefresh


Pagination Controls

TodoMVC UI (Enterprise Edition) with Captain Picard's Paginated Todo List

Any Day Now I Swear: EachLink

<<< @/apps/emberjs/app/components/todo-app/pagination-controls.gts ts {30-42|30-42|33-37|52-58}{maxHeight: '200px',lines:false}
  • Previous and next page buttons + page links
  • <EachLink /> component
  • Yields a <:link> block for each known page link
  • Yields a <:placeholder> block for unknown links

Paginated Query Builder

<<< @/packages/shared-data/src/builders/todo/query.ts ts {9-24|9-24|11-15,19}{maxHeight: '380px'}
  • Our getAllTodos builder
  • Pagination query params

Paginated API Response

{
  "data": [
    /* the first page of Todos */
  ],
  "links": {
    "self": "/api/todo?page[limit]=5&page[offset]=0",
    "first": "/api/todo?page[limit]=5&page[offset]=0",
    "next": "/api/todo?page[limit]=5&page[offset]=5",
    "last": "/api/todo?page[limit]=5&page[offset]=99995"
  },
  "meta": {
    "currentPage": 1,
    "totalPages": 20000
  }
}
  • Our paginated /api/todo response
  • Returns a links object
  • Returns a meta — required only for EachLink support

Paginate + EachLink + Page Hints

<<< @/apps/emberjs/app/components/todo-app/todo-provider.gts ts {30|30|91-103}{maxHeight: '360px'}
  • <Paginate /> component
  • @pageHints argument
  • Extracts pagination meta for use in EachLink

Pagination: Putting it all together

TodoMVC UI (Enterprise Edition) with Captain Picard's Paginated Todo List

<<< @/apps/emberjs/app/components/todo-app/todo-provider.gts ts {28-51|28-51|30,32,34,36,39,41,43,45,47|91-103}{maxHeight: '460px',lines:false}
<<< @/apps/emberjs/app/components/todo-app/pagination-controls.gts ts {30-42|30-42|33,35,37|50-60|52-58}{maxHeight: '380px'}
<<< @/packages/shared-data/src/builders/todo/query.ts ts {9-24|9-24|11-23}{maxHeight: '380px'}

Houston, we have a problem

Load only part of the Todo list...

Load the entire Todo list just to perform bulk operations...

Or risk updating only part of the list.

Bulk Actions — Bulk Op Builders

<<< @/packages/shared-data/src/builders/todo/bulk.ts ts {154-167|154-167|164|165|163-166|159-161|163}{maxHeight: '360px'}
  • Our bulkDeleteCompletedTodos builder
  • Specifies the request method
  • Generates the URL
  • No body, no cache options
  • Query params filter
  • Expects empty response

Bulk Actions — State Invalidation

<<< @/packages/shared-data/src/builders/todo/query.ts ts {71-74|71-74}{maxHeight: '200px'}
  • Our invalidateAllTodoQueries util
  • invalidateRequestsForType method

layout: center

Live Demo: Bulk Actions


Bulk Actions — Putting It All Together

<<< @/apps/emberjs/app/components/todo-app/clear-completed-todos.gts ts {48,51-56,59|48,51-56,59|52|53}{maxHeight: '360px'}
  • Our clearCompleted action
  • Our bulkDeleteCompletedTodos builder
  • Our invalidateAllTodoQueries utility

<<< @/packages/shared-data/src/builders/todo/bulk.ts ts {154-167|158-166}{maxHeight: '360px'}
<<< @/packages/shared-data/src/builders/todo/query.ts ts {71-74|73}{maxHeight: '360px'}
TodoMVC UI with Paginated Todo List and all Paginate, Request, EachLink, and Await components highlighted
Paginate
EachLink
Request
Await

layout: section title: 'Episode 8: "The Future — Final Frontier?"'

Episode 8

"The Future — Final Frontier?"


🖖

What We've Discovered

WarpDrive is the lightweight data framework for ambitious web applications.

Universal

Works with any framework (Ember, React, Vue, Svelte)

Typed

Fully typed, ready to rock

Performant

Committed to best-in-class performance

Scalable

From weekend hobby to enterprise


Your Mission, Should You Choose to Accept It

  1. Try WarpDrive in your next project, regardless of framework.
  2. Explore the guides at docs.warp-drive.io and canary.warp-drive.io.
  3. Explore this WarpDrive TodoMVC monorepo at github.com/gitKrystan/2025-emberfest.
  4. Join the community discussions on Discord: https://discord.gg/eUPwQzRJ.
  5. Build something ambitious!

Questions? Let's explore the unknown together!

Special Thanks:
AuditBoard, Chris Thoburn, Julia Donaldson, Natasha Wolfe
My mom, who's seen this talk four times
(and my husband for being super-dad the last month while I prepared it)

The human adventure is just beginning...

And so is your journey with WarpDrive

The data is out there — go boldly and fetch it efficiently