Click Below to Get the Code

Browse, clone, and build from real-world templates powered by Harper.
Blog
GitHub Logo

The Resource API in Harper v5: HTTP Done Right

Harper v5's Resource API maps JavaScript class methods directly to HTTP verbs, eliminating routing and translation layers. Tables extend the same Resource class, unifying HTTP handling and data access into one interface. Key v5 additions include pre-parsed RequestTarget objects, Response-aware source caching with stale-while-revalidate support, and async context tracking via getContext().
Product Update
Blog
Product Update

The Resource API in Harper v5: HTTP Done Right

Kris Zyp
SVP of Engineering
at Harper
April 14, 2026
Kris Zyp
SVP of Engineering
at Harper
April 14, 2026
Kris Zyp
SVP of Engineering
at Harper
April 14, 2026
April 14, 2026
Harper v5's Resource API maps JavaScript class methods directly to HTTP verbs, eliminating routing and translation layers. Tables extend the same Resource class, unifying HTTP handling and data access into one interface. Key v5 additions include pre-parsed RequestTarget objects, Response-aware source caching with stale-while-revalidate support, and async context tracking via getContext().
Kris Zyp
SVP of Engineering

There is a gap that shows up consistently in application frameworks between what HTTP actually specifies and what the framework exposes to developers. HTTP's uniform interface — GET, PUT, PATCH, POST, DELETE — is precise and well-reasoned. The semantics are defined. The contract is clear. But most frameworks layer their own routing and handler abstractions on top, and the correspondence between what HTTP says and what your code does becomes indirect. You end up mapping between two systems rather than working in one.

Harper's Resource API closes that gap. In v5, it does so more directly than any previous version.

The Uniform Interface, Directly Expressed

The Resource API is a JavaScript class interface where static methods map one-to-one to HTTP verbs. A Resource class represents a collection; a Resource instance represents a single record. To define how your application responds to HTTP requests, you extend the base Resource class and override the methods that correspond to the HTTP verbs you care about.

class Product extends Resource {
    static async get(target) {
        // called for HTTP GET /Product/:id
        return super.get(target);
    }

    static async put(target, data) {
        // called for HTTP PUT /Product/:id
        return super.put(target, { ...(await data), status: 'active' });
    }

    static async delete(target) {
        // called for HTTP DELETE /Product/:id
        return super.delete(target);
    }
}

This is not a coincidence of naming. The static methods are the HTTP handlers. Harper routes incoming requests directly to these methods. There is no separate routing layer, no middleware registration, no controller-to-method mapping to maintain. The class structure encodes the URL hierarchy; the method names encode the HTTP verbs.

v5 formalizes this further by explicitly encouraging use of static methods as the primary interaction pattern, and by pre-parsing the RequestTarget before it reaches any static method. That second point matters more than it might seem.

The RequestTarget as Parsed URL

In v5, when a request arrives, Harper parses the URL into a RequestTarget object before calling your static method. RequestTarget is a subclass of URLSearchParams, so it carries the full query string as a native iterable interface, plus additional properties that Harper derives from the URL structure:

  • target.id — the primary key extracted from the path
  • target.pathname — the path portion relative to the resource
  • target.isCollectiontrue when the request targets a collection (e.g. /Product/ with query conditions) rather than a single record
  • target.conditions, target.limit, target.offset, target.sort, target.select — query parameters parsed from Harper's extended URL query syntax

The consequence is that your get method does not need to parse anything. The URL has already been translated into a structured object that reflects the semantics of the request.

static get(target) {
    const id = target.id;
    const filter = target.get('status');  // standard URLSearchParams access
    const limit = target.limit;           // parsed by Harper from URL syntax
    return super.get(target);
}

Compare this to the alternative: receiving a raw URL string, manually extracting path segments, manually parsing query parameters, and then constructing some internal query object from what you found. The RequestTarget design eliminates that translation layer. What HTTP says about the request is what your method receives.

Data Modeling Through the Resource Interface

The Resource API is also the primary modeling interface for data. Tables in Harper extend Resource, which means the same methods that handle HTTP requests are the methods you use to interact with data from application code. There is one interface, not two.

Static methods handle reads and writes at the collection level:

// retrieve a single record
const product = await Product.get(34);

// partial update
await Product.patch(34, { description: 'Updated description' });

// full replacement
await Product.put({ id: 34, name: 'New Product Name', price: 49.99 });

// create with auto-generated key
const created = await Product.create({ name: 'New Product', price: 29.99 });

// delete
await Product.delete(34);

Instance methods, retrieved via update(), allow mutable access to a single record within a transaction:

const product = await Product.update(32);
product.status = 'active';
product.subtractFrom('quantity', 1);  // CRDT-safe decrement
product.save();                        // explicit save within transaction

The save() method is new in v5. Previously, pending changes were committed only when the transaction completed. save() makes those changes visible to subsequent reads and queries within the same transaction — useful when you need to write a record and then query against the updated state before the transaction closes.

The get() method in v5 returns a RecordObject: a frozen plain object containing the record's properties plus getUpdatedTime() and getExpiresAt(). It is immutable. This is intentional. The returned object represents the committed state of the record in the database; mutation belongs to the update() flow. If you need a modified version of the record for a response, the spread operator is the explicit path:

static async get(target) {
    const record = await super.get(target);
    return { ...record, computedField: derive(record) };
}

The consistency here — a frozen read result, a distinct mutable update path — reflects the underlying transaction model. Reads are snapshots. Writes are transactions. The API makes that distinction explicit.

Caching and Sourcing: Where the Model Gets Interesting

The Resource API's treatment of caching is where the data modeling story becomes most useful. Harper supports the concept of a source resource: a resource that a caching table delegates to when a record is not found locally. This is configured with sourcedFrom():

ProductCache.sourcedFrom(ExternalProductAPI, {
    expiration: 300,   // TTL in seconds
    eviction: 3600     // eviction time in seconds
});

Once configured, reads against ProductCache transparently check local storage first, fall back to ExternalProductAPI if there is a miss, cache the result, and serve subsequent requests from local storage until expiration. Writes are delegated upstream.

The v5 changes to how source resources work make this pattern significantly more practical.

Returning a Response Object from Source

A source resource's get method can now return a standard Response object — the same type you get from a fetch() call. Harper will stream the response body and save the headers into the cached record automatically.

class ExternalProductAPI extends Resource {
    static async get(target) {
        const response = await fetch(`https://api.example.com/products/${target.id}`);
        return response;  // Harper handles streaming and header caching
    }
}

This matters because most upstream data sources are HTTP APIs. Previously, you had to extract the body, parse it, and return a plain object — discarding headers that might carry semantic information (caching directives, content type, custom metadata). Now the full response is preserved. If the upstream API returns Cache-Control or ETag headers, those are stored and can inform downstream caching behavior.

The getResponse() function, available as a named export from the harper module, provides access to the response object from within a resource method when you need it. You can use wasLoadedFromSource() on the instance to determine whether the current request was a cache miss — useful when you want to behave differently on cold cache versus warm cache.

Stale-While-Revalidate

The allowStaleWhileRevalidate() method gives you control over cache freshness policy at the record level:

static allowStaleWhileRevalidate(entry, id) {
    // serve the stale entry immediately while revalidation runs in the background
    return (Date.now() - entry.localTime) < 60_000;  // stale for less than 60s
}

When this returns true, Harper serves the cached value immediately and triggers a background refresh. When it returns false, the request waits for the fresh value. The entry object includes version, localTime, expiresAt, and value, giving you the information needed to make this decision per-record rather than globally.

The combination of source delegation, Response object handling, and stale-while-revalidate control gives you a caching layer that is not bolted on but is expressed through the same method interface as everything else in the API.

Context Without Threading

One of the practical complications in multi-request server environments is getting access to the current request from code that is not the immediate handler. If you have a utility function five calls deep in a call chain, and you need the request's authentication token or user context, the common solution is to pass the request object as a parameter through every intermediate function. This works but it is noise — most functions do not need the request, they just relay it to the next level.

v5 solves this with asynchronous context tracking. getContext(), available as a named export from the harper module or as a global, returns the current Request object — the one associated with whatever async execution context is currently running. No parameter passing required.

import { getContext } from 'harper';

function someUtilityDeepInTheCallChain() {
    const context = getContext();
    const userRole = context.user.role;
    // act on user authorization without having received request as a parameter
}

When triggered by HTTP, the context is the Request object with properties for method, headers, responseHeaders, url, ip, host, body, and data. You can set response headers through context.responseHeaders.set() from anywhere in the async context — another case where the alternative is passing state through intermediate layers.

This is the Node.js AsyncLocalStorage API applied consistently throughout Harper's request lifecycle. The mechanism is not novel, but the fact that Harper exposes it through the standard getContext() export means you do not have to set it up yourself.

The Benefits, in Summary

The Resource API's design choices compound. Because static methods map directly to HTTP verbs, there is no translation layer between what the HTTP client requests and what your code handles. Because RequestTarget arrives pre-parsed, URL interpretation is not your responsibility. Because the same methods work for both HTTP handling and programmatic data access, there is one mental model for both concerns. Because source resources can return Response objects, the caching layer preserves upstream HTTP semantics rather than discarding them. Because getContext() uses async context tracking, request state is accessible without explicit parameter threading.

None of these are individually unprecedented. What the Resource API does is combine them into a consistent interface where each decision reinforces the others. The HTTP uniform interface — which is precise and well-specified — becomes the application's data interface as well. That alignment reduces the surface area for inconsistency, and it reduces the amount of glue code that does nothing but translate between layers.

I am most interested to see how teams use the source resource pattern as systems scale. The caching and sourcing model is expressive enough to handle a range of architectures — local caching of remote APIs, tiered storage, conditional revalidation based on record-level logic — and I think the combinations people reach for in practice will be more varied than what we have anticipated.

The v5 Resource API documentation covers the full method signatures and options.

There is a gap that shows up consistently in application frameworks between what HTTP actually specifies and what the framework exposes to developers. HTTP's uniform interface — GET, PUT, PATCH, POST, DELETE — is precise and well-reasoned. The semantics are defined. The contract is clear. But most frameworks layer their own routing and handler abstractions on top, and the correspondence between what HTTP says and what your code does becomes indirect. You end up mapping between two systems rather than working in one.

Harper's Resource API closes that gap. In v5, it does so more directly than any previous version.

The Uniform Interface, Directly Expressed

The Resource API is a JavaScript class interface where static methods map one-to-one to HTTP verbs. A Resource class represents a collection; a Resource instance represents a single record. To define how your application responds to HTTP requests, you extend the base Resource class and override the methods that correspond to the HTTP verbs you care about.

class Product extends Resource {
    static async get(target) {
        // called for HTTP GET /Product/:id
        return super.get(target);
    }

    static async put(target, data) {
        // called for HTTP PUT /Product/:id
        return super.put(target, { ...(await data), status: 'active' });
    }

    static async delete(target) {
        // called for HTTP DELETE /Product/:id
        return super.delete(target);
    }
}

This is not a coincidence of naming. The static methods are the HTTP handlers. Harper routes incoming requests directly to these methods. There is no separate routing layer, no middleware registration, no controller-to-method mapping to maintain. The class structure encodes the URL hierarchy; the method names encode the HTTP verbs.

v5 formalizes this further by explicitly encouraging use of static methods as the primary interaction pattern, and by pre-parsing the RequestTarget before it reaches any static method. That second point matters more than it might seem.

The RequestTarget as Parsed URL

In v5, when a request arrives, Harper parses the URL into a RequestTarget object before calling your static method. RequestTarget is a subclass of URLSearchParams, so it carries the full query string as a native iterable interface, plus additional properties that Harper derives from the URL structure:

  • target.id — the primary key extracted from the path
  • target.pathname — the path portion relative to the resource
  • target.isCollectiontrue when the request targets a collection (e.g. /Product/ with query conditions) rather than a single record
  • target.conditions, target.limit, target.offset, target.sort, target.select — query parameters parsed from Harper's extended URL query syntax

The consequence is that your get method does not need to parse anything. The URL has already been translated into a structured object that reflects the semantics of the request.

static get(target) {
    const id = target.id;
    const filter = target.get('status');  // standard URLSearchParams access
    const limit = target.limit;           // parsed by Harper from URL syntax
    return super.get(target);
}

Compare this to the alternative: receiving a raw URL string, manually extracting path segments, manually parsing query parameters, and then constructing some internal query object from what you found. The RequestTarget design eliminates that translation layer. What HTTP says about the request is what your method receives.

Data Modeling Through the Resource Interface

The Resource API is also the primary modeling interface for data. Tables in Harper extend Resource, which means the same methods that handle HTTP requests are the methods you use to interact with data from application code. There is one interface, not two.

Static methods handle reads and writes at the collection level:

// retrieve a single record
const product = await Product.get(34);

// partial update
await Product.patch(34, { description: 'Updated description' });

// full replacement
await Product.put({ id: 34, name: 'New Product Name', price: 49.99 });

// create with auto-generated key
const created = await Product.create({ name: 'New Product', price: 29.99 });

// delete
await Product.delete(34);

Instance methods, retrieved via update(), allow mutable access to a single record within a transaction:

const product = await Product.update(32);
product.status = 'active';
product.subtractFrom('quantity', 1);  // CRDT-safe decrement
product.save();                        // explicit save within transaction

The save() method is new in v5. Previously, pending changes were committed only when the transaction completed. save() makes those changes visible to subsequent reads and queries within the same transaction — useful when you need to write a record and then query against the updated state before the transaction closes.

The get() method in v5 returns a RecordObject: a frozen plain object containing the record's properties plus getUpdatedTime() and getExpiresAt(). It is immutable. This is intentional. The returned object represents the committed state of the record in the database; mutation belongs to the update() flow. If you need a modified version of the record for a response, the spread operator is the explicit path:

static async get(target) {
    const record = await super.get(target);
    return { ...record, computedField: derive(record) };
}

The consistency here — a frozen read result, a distinct mutable update path — reflects the underlying transaction model. Reads are snapshots. Writes are transactions. The API makes that distinction explicit.

Caching and Sourcing: Where the Model Gets Interesting

The Resource API's treatment of caching is where the data modeling story becomes most useful. Harper supports the concept of a source resource: a resource that a caching table delegates to when a record is not found locally. This is configured with sourcedFrom():

ProductCache.sourcedFrom(ExternalProductAPI, {
    expiration: 300,   // TTL in seconds
    eviction: 3600     // eviction time in seconds
});

Once configured, reads against ProductCache transparently check local storage first, fall back to ExternalProductAPI if there is a miss, cache the result, and serve subsequent requests from local storage until expiration. Writes are delegated upstream.

The v5 changes to how source resources work make this pattern significantly more practical.

Returning a Response Object from Source

A source resource's get method can now return a standard Response object — the same type you get from a fetch() call. Harper will stream the response body and save the headers into the cached record automatically.

class ExternalProductAPI extends Resource {
    static async get(target) {
        const response = await fetch(`https://api.example.com/products/${target.id}`);
        return response;  // Harper handles streaming and header caching
    }
}

This matters because most upstream data sources are HTTP APIs. Previously, you had to extract the body, parse it, and return a plain object — discarding headers that might carry semantic information (caching directives, content type, custom metadata). Now the full response is preserved. If the upstream API returns Cache-Control or ETag headers, those are stored and can inform downstream caching behavior.

The getResponse() function, available as a named export from the harper module, provides access to the response object from within a resource method when you need it. You can use wasLoadedFromSource() on the instance to determine whether the current request was a cache miss — useful when you want to behave differently on cold cache versus warm cache.

Stale-While-Revalidate

The allowStaleWhileRevalidate() method gives you control over cache freshness policy at the record level:

static allowStaleWhileRevalidate(entry, id) {
    // serve the stale entry immediately while revalidation runs in the background
    return (Date.now() - entry.localTime) < 60_000;  // stale for less than 60s
}

When this returns true, Harper serves the cached value immediately and triggers a background refresh. When it returns false, the request waits for the fresh value. The entry object includes version, localTime, expiresAt, and value, giving you the information needed to make this decision per-record rather than globally.

The combination of source delegation, Response object handling, and stale-while-revalidate control gives you a caching layer that is not bolted on but is expressed through the same method interface as everything else in the API.

Context Without Threading

One of the practical complications in multi-request server environments is getting access to the current request from code that is not the immediate handler. If you have a utility function five calls deep in a call chain, and you need the request's authentication token or user context, the common solution is to pass the request object as a parameter through every intermediate function. This works but it is noise — most functions do not need the request, they just relay it to the next level.

v5 solves this with asynchronous context tracking. getContext(), available as a named export from the harper module or as a global, returns the current Request object — the one associated with whatever async execution context is currently running. No parameter passing required.

import { getContext } from 'harper';

function someUtilityDeepInTheCallChain() {
    const context = getContext();
    const userRole = context.user.role;
    // act on user authorization without having received request as a parameter
}

When triggered by HTTP, the context is the Request object with properties for method, headers, responseHeaders, url, ip, host, body, and data. You can set response headers through context.responseHeaders.set() from anywhere in the async context — another case where the alternative is passing state through intermediate layers.

This is the Node.js AsyncLocalStorage API applied consistently throughout Harper's request lifecycle. The mechanism is not novel, but the fact that Harper exposes it through the standard getContext() export means you do not have to set it up yourself.

The Benefits, in Summary

The Resource API's design choices compound. Because static methods map directly to HTTP verbs, there is no translation layer between what the HTTP client requests and what your code handles. Because RequestTarget arrives pre-parsed, URL interpretation is not your responsibility. Because the same methods work for both HTTP handling and programmatic data access, there is one mental model for both concerns. Because source resources can return Response objects, the caching layer preserves upstream HTTP semantics rather than discarding them. Because getContext() uses async context tracking, request state is accessible without explicit parameter threading.

None of these are individually unprecedented. What the Resource API does is combine them into a consistent interface where each decision reinforces the others. The HTTP uniform interface — which is precise and well-specified — becomes the application's data interface as well. That alignment reduces the surface area for inconsistency, and it reduces the amount of glue code that does nothing but translate between layers.

I am most interested to see how teams use the source resource pattern as systems scale. The caching and sourcing model is expressive enough to handle a range of architectures — local caching of remote APIs, tiered storage, conditional revalidation based on record-level logic — and I think the combinations people reach for in practice will be more varied than what we have anticipated.

The v5 Resource API documentation covers the full method signatures and options.

Harper v5's Resource API maps JavaScript class methods directly to HTTP verbs, eliminating routing and translation layers. Tables extend the same Resource class, unifying HTTP handling and data access into one interface. Key v5 additions include pre-parsed RequestTarget objects, Response-aware source caching with stale-while-revalidate support, and async context tracking via getContext().

Download

White arrow pointing right
Harper v5's Resource API maps JavaScript class methods directly to HTTP verbs, eliminating routing and translation layers. Tables extend the same Resource class, unifying HTTP handling and data access into one interface. Key v5 additions include pre-parsed RequestTarget objects, Response-aware source caching with stale-while-revalidate support, and async context tracking via getContext().

Download

White arrow pointing right
Harper v5's Resource API maps JavaScript class methods directly to HTTP verbs, eliminating routing and translation layers. Tables extend the same Resource class, unifying HTTP handling and data access into one interface. Key v5 additions include pre-parsed RequestTarget objects, Response-aware source caching with stale-while-revalidate support, and async context tracking via getContext().

Download

White arrow pointing right

Explore Recent Resources

Livestream
GitHub Logo

2 Hour Build - Live Stream for Non-Developers

A non-developer's live stream walkthrough of building Flow State, a Colorado river-flow app for rafters, in two hours using ChatGPT dictation, Claude Code, Claude Design, and Harper. Scaffold with npm create harper@latest and deploy to Harper Fabric. No coding background required.
Livestream
A non-developer's live stream walkthrough of building Flow State, a Colorado river-flow app for rafters, in two hours using ChatGPT dictation, Claude Code, Claude Design, and Harper. Scaffold with npm create harper@latest and deploy to Harper Fabric. No coding background required.
Person with short dark hair and moustache, wearing a colorful plaid shirt, smiling outdoors in a forested mountain landscape.
Aleks Haugom
Senior Manager of GTM
Livestream

2 Hour Build - Live Stream for Non-Developers

A non-developer's live stream walkthrough of building Flow State, a Colorado river-flow app for rafters, in two hours using ChatGPT dictation, Claude Code, Claude Design, and Harper. Scaffold with npm create harper@latest and deploy to Harper Fabric. No coding background required.
Aleks Haugom
May 2026
Livestream

2 Hour Build - Live Stream for Non-Developers

A non-developer's live stream walkthrough of building Flow State, a Colorado river-flow app for rafters, in two hours using ChatGPT dictation, Claude Code, Claude Design, and Harper. Scaffold with npm create harper@latest and deploy to Harper Fabric. No coding background required.
Aleks Haugom
Livestream

2 Hour Build - Live Stream for Non-Developers

A non-developer's live stream walkthrough of building Flow State, a Colorado river-flow app for rafters, in two hours using ChatGPT dictation, Claude Code, Claude Design, and Harper. Scaffold with npm create harper@latest and deploy to Harper Fabric. No coding background required.
Aleks Haugom
Tutorial
GitHub Logo

Production Quality at Vibe Code Velocity: Dispatched Agent Teams with Harper

Harper enables production-grade agentic engineering by collapsing database, cache, runtime, and messaging into one process, reducing agent complexity and review burden. A multi-model dispatch workflow lets specialized agents plan, code, QA, and review in parallel while humans retain control over critical decisions.
Tutorial
Harper enables production-grade agentic engineering by collapsing database, cache, runtime, and messaging into one process, reducing agent complexity and review burden. A multi-model dispatch workflow lets specialized agents plan, code, QA, and review in parallel while humans retain control over critical decisions.
Person with very short hair and a goatee wearing a plaid button‑up shirt over a white undershirt, smiling outdoors with leafy greenery behind.
Jeff Darnton
SVP, Professional Services & Customer Success
Tutorial

Production Quality at Vibe Code Velocity: Dispatched Agent Teams with Harper

Harper enables production-grade agentic engineering by collapsing database, cache, runtime, and messaging into one process, reducing agent complexity and review burden. A multi-model dispatch workflow lets specialized agents plan, code, QA, and review in parallel while humans retain control over critical decisions.
Jeff Darnton
May 2026
Tutorial

Production Quality at Vibe Code Velocity: Dispatched Agent Teams with Harper

Harper enables production-grade agentic engineering by collapsing database, cache, runtime, and messaging into one process, reducing agent complexity and review burden. A multi-model dispatch workflow lets specialized agents plan, code, QA, and review in parallel while humans retain control over critical decisions.
Jeff Darnton
Tutorial

Production Quality at Vibe Code Velocity: Dispatched Agent Teams with Harper

Harper enables production-grade agentic engineering by collapsing database, cache, runtime, and messaging into one process, reducing agent complexity and review burden. A multi-model dispatch workflow lets specialized agents plan, code, QA, and review in parallel while humans retain control over critical decisions.
Jeff Darnton
Tutorial
GitHub Logo

Change Data Capture Into a Runtime: One Pipeline for Pages, Search, and AI Agents

Learn how Harper turns CDC streams into real-time workflows that refresh cached pages, update search indexes, and keep AI agent context current. See why landing changes in an application runtime beats warehouses, queues, and traditional CDNs.
Tutorial
Learn how Harper turns CDC streams into real-time workflows that refresh cached pages, update search indexes, and keep AI agent context current. See why landing changes in an application runtime beats warehouses, queues, and traditional CDNs.
Person with very short hair and a goatee wearing a plaid button‑up shirt over a white undershirt, smiling outdoors with leafy greenery behind.
Jeff Darnton
SVP, Professional Services & Customer Success
Tutorial

Change Data Capture Into a Runtime: One Pipeline for Pages, Search, and AI Agents

Learn how Harper turns CDC streams into real-time workflows that refresh cached pages, update search indexes, and keep AI agent context current. See why landing changes in an application runtime beats warehouses, queues, and traditional CDNs.
Jeff Darnton
May 2026
Tutorial

Change Data Capture Into a Runtime: One Pipeline for Pages, Search, and AI Agents

Learn how Harper turns CDC streams into real-time workflows that refresh cached pages, update search indexes, and keep AI agent context current. See why landing changes in an application runtime beats warehouses, queues, and traditional CDNs.
Jeff Darnton
Tutorial

Change Data Capture Into a Runtime: One Pipeline for Pages, Search, and AI Agents

Learn how Harper turns CDC streams into real-time workflows that refresh cached pages, update search indexes, and keep AI agent context current. See why landing changes in an application runtime beats warehouses, queues, and traditional CDNs.
Jeff Darnton
Tutorial
GitHub Logo

Harper + Vertex AI: The Architecture Every Agent Builder Should Know

Production agents bleed tokens and latency on repeated queries. Pair a managed model layer with a vector-indexed data layer at the edge, and an 80% cache hit rate cuts LLM spend by 80% while delivering sub-100ms responses on semantically similar requests.
Tutorial
Production agents bleed tokens and latency on repeated queries. Pair a managed model layer with a vector-indexed data layer at the edge, and an 80% cache hit rate cuts LLM spend by 80% while delivering sub-100ms responses on semantically similar requests.
Person with styled reddish‑brown hair and a full beard wearing a gray suit with a light blue shirt and dark green tie, posing outdoors with a blurred pathway and greenery behind.
Drew Chambers
CMO
Tutorial

Harper + Vertex AI: The Architecture Every Agent Builder Should Know

Production agents bleed tokens and latency on repeated queries. Pair a managed model layer with a vector-indexed data layer at the edge, and an 80% cache hit rate cuts LLM spend by 80% while delivering sub-100ms responses on semantically similar requests.
Drew Chambers
May 2026
Tutorial

Harper + Vertex AI: The Architecture Every Agent Builder Should Know

Production agents bleed tokens and latency on repeated queries. Pair a managed model layer with a vector-indexed data layer at the edge, and an 80% cache hit rate cuts LLM spend by 80% while delivering sub-100ms responses on semantically similar requests.
Drew Chambers
Tutorial

Harper + Vertex AI: The Architecture Every Agent Builder Should Know

Production agents bleed tokens and latency on repeated queries. Pair a managed model layer with a vector-indexed data layer at the edge, and an 80% cache hit rate cuts LLM spend by 80% while delivering sub-100ms responses on semantically similar requests.
Drew Chambers
Blog
GitHub Logo

Why Harper is the Definitive Platform for Enterprise Citizen Developers

Harper bridges the gap between business agility and IT security. Utilizing a unified runtime, Harper Fabric guarantees data sovereignty across any environment, from public clouds to air-gapped facilities. Empower users with secure, compliant AI application development and robust governance.
Blog
Harper bridges the gap between business agility and IT security. Utilizing a unified runtime, Harper Fabric guarantees data sovereignty across any environment, from public clouds to air-gapped facilities. Empower users with secure, compliant AI application development and robust governance.
A smiling man with a beard and salt-and-pepper hair stands outdoors with arms crossed, wearing a white button-down shirt.
Stephen Goldberg
CEO & Co-Founder
Blog

Why Harper is the Definitive Platform for Enterprise Citizen Developers

Harper bridges the gap between business agility and IT security. Utilizing a unified runtime, Harper Fabric guarantees data sovereignty across any environment, from public clouds to air-gapped facilities. Empower users with secure, compliant AI application development and robust governance.
Stephen Goldberg
May 2026
Blog

Why Harper is the Definitive Platform for Enterprise Citizen Developers

Harper bridges the gap between business agility and IT security. Utilizing a unified runtime, Harper Fabric guarantees data sovereignty across any environment, from public clouds to air-gapped facilities. Empower users with secure, compliant AI application development and robust governance.
Stephen Goldberg
Blog

Why Harper is the Definitive Platform for Enterprise Citizen Developers

Harper bridges the gap between business agility and IT security. Utilizing a unified runtime, Harper Fabric guarantees data sovereignty across any environment, from public clouds to air-gapped facilities. Empower users with secure, compliant AI application development and robust governance.
Stephen Goldberg
Comparison
GitHub Logo

Harper vs. Vercel + Supabase

Harper offers a unified application platform alternative to Vercel + Supabase, combining database, cache, app logic, messaging, vectors, and real-time capabilities in one globally distributed runtime to reduce latency, operational complexity, and total cost of ownership.
Comparison
Harper offers a unified application platform alternative to Vercel + Supabase, combining database, cache, app logic, messaging, vectors, and real-time capabilities in one globally distributed runtime to reduce latency, operational complexity, and total cost of ownership.
Colorful geometric illustration of a dog's head resembling folded paper art in shades of teal and pink.
Harper
Comparison

Harper vs. Vercel + Supabase

Harper offers a unified application platform alternative to Vercel + Supabase, combining database, cache, app logic, messaging, vectors, and real-time capabilities in one globally distributed runtime to reduce latency, operational complexity, and total cost of ownership.
Harper
May 2026
Comparison

Harper vs. Vercel + Supabase

Harper offers a unified application platform alternative to Vercel + Supabase, combining database, cache, app logic, messaging, vectors, and real-time capabilities in one globally distributed runtime to reduce latency, operational complexity, and total cost of ownership.
Harper
Comparison

Harper vs. Vercel + Supabase

Harper offers a unified application platform alternative to Vercel + Supabase, combining database, cache, app logic, messaging, vectors, and real-time capabilities in one globally distributed runtime to reduce latency, operational complexity, and total cost of ownership.
Harper