From d339a1fdf065a176b7c9f518835815d0b695b738 Mon Sep 17 00:00:00 2001 From: bailey Date: Mon, 24 Nov 2025 12:17:36 -0700 Subject: [PATCH 1/2] add more complexity to the template --- README.md | 94 +++++++++++++++++++++---- resources.js | 140 ++++++++++++++++++++++++++++++------- schema.graphql | 25 +++++-- web/hdb-logo.png | Bin 0 -> 52270 bytes web/index.html | 123 ++++++++++++++++++++++++++++----- web/index.js | 164 +++++++++++++++++++++++++++++++++++++++---- web/styles.css | 176 ++++++++++++++++++++++++++++++++++++++--------- 7 files changed, 619 insertions(+), 103 deletions(-) create mode 100644 web/hdb-logo.png diff --git a/README.md b/README.md index 9198641..80fef0d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,20 @@ # Your New Harper Fabric App -This is a template for building [Harper](https://www.harper.fast/) applications. You can download this repository as a starting point for building applications with Harper. +This repository is a template for building applications with [Harper](https://www.harper.fast/). This template includes: ++ 2 tables: Owner, Dog ++ 1 computed field: owner_dog_count ++ 1 relationship: Owners → Dogs ++ 1 custom resource: /OwnerHasBreed + +This template is designed to help you quickly learn Harper’s application model and best practices. + +**Explore the project:** +- `config.yaml`: Main application configuration (routes, settings, etc.) +- `schema.graphql`: Defines tables, fields, relationships, and computed attributes. +- `resources.js`: Implements custom resource classes and endpoint logic. +- `web/`: Contains the frontend web application (HTML, JS, CSS). + +For more information about getting started with HarperDB and building applications, see our [getting started guide](https://docs.harperdb.io/docs). ## Installation @@ -17,29 +31,83 @@ Then you can start your app: npm run dev ``` -Test your application works by querying the `/Greeting` endpoint: +Harper will start at: -```sh -curl http://localhost:9926/Greeting +```arduino +http://localhost:9926/ ``` -You should see the following: +All tables and resources in this project are automatically available when the app starts. Visit http://localhost:9926/ in your browser to explore the web application and interact with the REST API endpoints. -```json -{"greeting":"Hello, world!"} +## Project Structure + +```graphql +. +├── config.yaml # App configuration +├── schema.graphql # Table definitions, computed fields, relationships +├── resources.js # Custom logic for tables + resources +├── web/ # Client-side web app +│ ├── index.html +│ ├── index.js +│ └── styles.css +└── README.md ``` -Navigate to [http://localhost:9926](http://localhost:9926) in a browser and view the functional web application. +## Database Schema -For more information about getting started with HarperDB and building applications, see our [getting started guide](https://docs.harperdb.io/docs). +All tables are defined in schema.graphql. This template includes two tables: Owner and Dog. Harper automatically exposes REST endpoints for both tables: + +```python-repl +GET /Dog +POST /Dog +PUT /Dog +PATCH /Dog +DELETE /Dog +GET /Owner +... etc. +``` + +## Example Workflow -For more information on Harper Components, see the [Components documentation](https://docs.harperdb.io/docs/reference/components). +### 1. Create Dog -Take a look at the [default configuration](./config.yaml), which specifies how files are handled in your application. +```sh +curl -X POST http://localhost:9926/Dog \ + -H "Content-Type: application/json" \ + -d '{ + "id": "123", + "name": "Willow", + "breed": "Great Pyrenees" + }' +``` + +### 2. Create Owner + +```sh +curl -X POST http://localhost:9926/Owner \ + -H "Content-Type: application/json" \ + -d '{ + "id": "456", + "name": "Bailey", + "dogIds": ["123"] + }' +``` -The [schema.graphql](./schema.graphql) is the table schema definition. This is the main starting point for defining your database schema, specifying which tables you want and what attributes/fields they should have. +### 3. Use the Custom Resource -The [resources.js](./resources.js) provides a template for defining JavaScript resource classes, for customized application logic in your endpoints. +```sh +curl "http://localhost:9926/OwnerHasBreed?ownerName=Bailey&breed=Great%20Pyrenees" +``` + +Response: +```json +{ + "statusCode": 200, + "ownerName": "Bailey", + "breed": "Great Pyrenees", + "hasBreed": true +} +``` ## Deployment diff --git a/resources.js b/resources.js index d51dd3e..876129b 100644 --- a/resources.js +++ b/resources.js @@ -1,24 +1,118 @@ -/** Here we can define any JavaScript-based resources and extensions to tables - -export class MyCustomResource extends tables.TableName { - // we can define our own custom POST handler - post(content) { - // do something with the incoming content; - return super.post(content); - } - // or custom GET handler - get() { - // we can modify this resource before returning - return super.get(); - } +import { tables, Resource } from 'harperdb'; + +const OwnerTable = tables.Owner + +// Computed field example +// +// This shows how to implement a JS-backed computed attribute for a table. +// In schema.graphql, Owner has: +// +// owner_dog_count: Int @computed(version: 1) +// +// The JS implementation below runs whenever Owner records are read or written. +// Computed values are not stored; they are generated on demand by Harper. + +OwnerTable.setComputedAttribute('owner_dog_count', (owner) => { + if (!owner || !Array.isArray(owner.dogIds)) { + return 0; + } + return owner.dogIds.length; +}); + +// Example: extending a table +// +// When you want custom behavior on a table-level REST endpoint, you extend +// the table class produced by Harper's table definition. This allows you to +// override GET, POST, PUT, DELETE, etc. while still inheriting all built-in +// behavior from Harper's table implementation. + +export class OwnerResource extends OwnerTable { + static loadAsInstance = false; + + async post(target, data) { + const record = { ...data }; + // Validate all required fields + const missingFields = []; + if (!record.id) missingFields.push('id'); + if (!record.name) missingFields.push('name'); + if (!Array.isArray(record.dogIds)) missingFields.push('dogIds (must be array)'); + + if (missingFields.length > 0) { + return { + status: 400, + message: `Missing or invalid fields: ${missingFields.join(', ')}`, + }; + } + + const created = await OwnerTable.create(record, this); + return created; + } +} + + +// Example: custom resource using relationships +// +// This resource is not tied to a specific table. It demonstrates how to: +// +// 1. Accept query parameters (?ownerName=...&breed=...) +// 2. Query a table using a relationship-aware select structure +// 3. Iterate through owners and their related dogs (Owner → Dogs relationship) +// 4. Return a boolean indicating whether the owner has any dog of the +// specified breed + +export class OwnerHasBreed extends Resource { + static loadAsInstance = false; + + async get(target) { + const { ownerName, breed } = target.get('ownerName') && target.get('breed') + ? { ownerName: target.get('ownerName'), breed: target.get('breed') } + : {}; + + if (!ownerName || !breed) { + console.warn('[OwnerHasBreed] Missing required query parameters:', { ownerName, breed }); + return { + statusCode: 400, + message: "Missing required query parameters: ownerName and breed", + }; + } + + const query = { + select: [ + "id", + "name", + "dogIds", + { + name: "dogs", + select: ["id", "name", "breed"], + }, + ], + conditions: [{ attribute: "name", comparator: "eq", value: ownerName }], + limit: 1, + }; + + let owner = null; + for await (const o of OwnerTable.search(query)) { + owner = o; + } + + // No owner found → 404 + if (!owner) { + console.warn('[OwnerHasBreed] No owner found for name:', ownerName); + return { + statusCode: 404, + message: `No owner found with name "${ownerName}"`, + }; + } + + const dogs = Array.isArray(owner.dogs) ? owner.dogs : []; + const hasBreed = dogs.some((dog) => dog && dog.breed === breed); + + return { + statusCode: 200, + ownerName, + breed, + hasBreed, + }; + } } - */ -// we can also define a custom resource without a specific table -export class Greeting extends Resource { - // a "Hello, world!" handler - static loadAsInstance = false; // use the updated/newer Resource API - - get() { - return { greeting: 'Hello, world!' }; - } -} \ No newline at end of file + diff --git a/schema.graphql b/schema.graphql index 5ad5d48..8c6ad68 100644 --- a/schema.graphql +++ b/schema.graphql @@ -1,7 +1,24 @@ ## Here we can define any tables in our database. This example shows how we define a type as a table using ## the type name as the table name and specifying it is an "export" available in the REST and other external protocols. -type TableName @table @export { - id: ID @primaryKey # Here we define primary key (must be one) - name: String # we can define any other attributes here - tag: String @indexed # we can specify any attributes that should be indexed +## This example uses Owners and Dogs, with a relationship and a computed value. + +type Owner @table(database: "application") @export { + id: ID @primaryKey # Here we define primary key (must be one) + name: String @indexed # we can specify any attributes that should be indexed + dogIds: [ID] @indexed + + # Computed: how many dogs this owner has, based on dogIds. + owner_dog_count: Int @computed(version: 1) + + # Relationship: resolve dogIds into Dog records. + dogs: [Dog] @relationship(from: dogIds) +} + +type Dog @table(database: "application") @export { + id: ID @primaryKey + name: String! @indexed + breed: String! @indexed + + # Relationship back to Owner using the dogIds on Owner. + owner: [Owner] @relationship(to: dogIds) } diff --git a/web/hdb-logo.png b/web/hdb-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..4ec961178d9239cf2fe1f95d38048dd836633d08 GIT binary patch literal 52270 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 - Harper Application Template + Harper Owners & Dogs -
-

Harper Application Template

-

- This is an example of serving static assets using the built-in - static component.
- You can use this component to serve web applications! -

-
-

0

- - -
-
+
+
+ +
+

Harper Owners & Dogs

+

Interact with your Harper endpoints from the browser.

+
+
+ +
+ +
+

Table API: Owner & Dog

+

Send requests to /Owner or /Dog.

+ +
+ Resource +
+ + +
+
+ +
+ Method +
+ + + + + +
+
+ +
+ + + + + +
+
+ + +
+

Custom Resource: OwnerHasBreed

+

+ Sends GET /OwnerHasBreed?ownerName=...&breed=.... +

+ +
+ + + +
+ +
+ No check performed yet. +
+
+ + +
+

Latest API Response

+

Shows the last JSON response from any request.

+
// Ready. Submit a request above.
+
+
+ +
+ Powered by Harper + + Use this page to explore your local API. +
+
diff --git a/web/index.js b/web/index.js index 0a48a84..cdf0c15 100644 --- a/web/index.js +++ b/web/index.js @@ -1,18 +1,156 @@ -let count = 0; -const counterDisplay = document.getElementById('count'); -const decrementButton = document.getElementById('decrement'); -const incrementButton = document.getElementById('increment'); +// Elements +const apiOutput = document.getElementById('api-output'); +const breedResult = document.getElementById('breed-result'); -function updateDisplay() { - counterDisplay.textContent = count; +const tableForm = document.getElementById('table-form'); +const tableIdInput = document.getElementById('table-id'); +const tableBodyInput = document.getElementById('table-body'); +const tableSubmitButton = document.getElementById('table-submit'); + +const resourceToggle = document.getElementById('resource-toggle'); +const methodToggle = document.getElementById('method-toggle'); + +const breedForm = document.getElementById('breed-form'); +const breedOwnerInput = document.getElementById('breed-owner-name'); +const breedNameInput = document.getElementById('breed-name'); +const breedSubmitButton = document.getElementById('breed-submit'); + +// State +let currentResource = 'Owner'; +let currentMethod = 'GET'; + +// Helpers +function showResponse(label, data, isError = false) { + const time = new Date().toLocaleTimeString(); + apiOutput.textContent = + `${label} (${time})${isError ? ' [ERROR]' : ''}\n\n` + + JSON.stringify(data, null, 2); +} + +function updateBreedBadge(result) { + if (!result || typeof result.hasBreed === 'undefined') { + breedResult.className = 'badge badge-muted'; + breedResult.textContent = 'No check performed yet.'; + return; + } + + if (result.hasBreed) { + breedResult.className = 'badge badge-success'; + breedResult.textContent = `${result.ownerName} has a dog of breed "${result.breed}".`; + } else { + breedResult.className = 'badge badge-error'; + breedResult.textContent = `${result.ownerName} does not have a dog of breed "${result.breed}".`; + } } -decrementButton.addEventListener('click', () => { - count--; - updateDisplay(); +async function sendRequest(path, method, body, button, label) { + const original = button.textContent; + button.disabled = true; + button.textContent = 'Working...'; + + const options = { method }; + + if (body !== undefined) { + options.headers = { 'content-type': 'application/json' }; + options.body = JSON.stringify(body); + } + + try { + const res = await fetch(path, options); + const data = await res.json().catch(() => ({})); + showResponse(`${label} ${method} ${path}`, data, !res.ok); + return { ok: res.ok, data }; + } catch (err) { + showResponse(`${label} ${method} ${path}`, { message: err.message }, true); + return { ok: false, data: null }; + } finally { + button.disabled = false; + button.textContent = original; + } +} + +// Toggle handling +resourceToggle.addEventListener('click', (event) => { + if (!(event.target instanceof HTMLElement)) return; + const value = event.target.getAttribute('data-resource'); + if (!value) return; + + currentResource = value; + Array.from(resourceToggle.querySelectorAll('.toggle')).forEach((btn) => + btn.classList.toggle('active', btn.getAttribute('data-resource') === value) + ); +}); + +methodToggle.addEventListener('click', (event) => { + if (!(event.target instanceof HTMLElement)) return; + const value = event.target.getAttribute('data-method'); + if (!value) return; + + currentMethod = value; + Array.from(methodToggle.querySelectorAll('.toggle')).forEach((btn) => + btn.classList.toggle('active', btn.getAttribute('data-method') === value) + ); +}); + +// Table form +tableForm.addEventListener('submit', (event) => { + event.preventDefault(); + + const id = tableIdInput.value.trim(); + const rawBody = tableBodyInput.value.trim(); + + // Route to /Dog or /Owner for default table handlers + let basePath; + if (currentResource === 'Owner') { + basePath = '/Owner/'; + } else if (currentResource === 'Dog') { + basePath = '/Dog/'; + } else { + basePath = `/${currentResource}/`; + } + const path = id ? `${basePath}${encodeURIComponent(id)}` : basePath; + + let body; + const hasBody = ['POST', 'PUT', 'PATCH'].includes(currentMethod); + + if (hasBody) { + if (rawBody) { + try { + body = JSON.parse(rawBody); + } catch (err) { + showResponse('Invalid JSON body', { message: err.message }, true); + return; + } + } else { + body = {}; + } + } + + sendRequest(path, currentMethod, hasBody ? body : undefined, tableSubmitButton, 'Table API'); +}); + +// OwnerHasBreed form +breedForm.addEventListener('submit', (event) => { + event.preventDefault(); + + const ownerName = breedOwnerInput.value.trim(); + const breed = breedNameInput.value.trim(); + + const url = new URL('/OwnerHasBreed', window.location.origin); + if (ownerName) url.searchParams.set('ownerName', ownerName); + if (breed) url.searchParams.set('breed', breed); + + sendRequest(url.toString(), 'GET', undefined, breedSubmitButton, 'OwnerHasBreed') + .then((result) => { + if (!result) return; + if (!result.ok) { + updateBreedBadge(null); + } else { + updateBreedBadge(result.data); + } + }); }); -incrementButton.addEventListener('click', () => { - count++; - updateDisplay(); -}); \ No newline at end of file +// Initial state +showResponse('Ready', { message: 'Use the controls above to talk to your Harper API.' }); +updateBreedBadge(null); diff --git a/web/styles.css b/web/styles.css index d533e9f..54f2ad1 100644 --- a/web/styles.css +++ b/web/styles.css @@ -1,57 +1,167 @@ +:root { + --bg: #120224; + --bg-card: #1b0534; + --border: #3e245f; + --purple: #a855f7; + --purple-soft: rgba(168, 85, 247, 0.15); + --purple-strong: #7c3aed; + --purple-light: #e9d5ff; + --text: #f9f9ff; + --text-muted: #c8b5ff; + --success: #4ade80; + --danger: #f87171; + --radius: 12px; + --font: system-ui, -apple-system, Inter, sans-serif; +} + +* { + box-sizing: border-box; +} + body { - font-family: Arial, sans-serif; + margin: 0; + font-family: var(--font); + background: radial-gradient(circle at top, #4c1d95 0, #0d0017 45%, #000 100%); + color: var(--text); +} + +.app { + min-height: 100vh; + display: flex; + flex-direction: column; +} + +/* Header */ + +.header { + display: flex; + align-items: center; + gap: 1rem; + padding: 1rem 1.5rem; + border-bottom: 1px solid var(--border); + background: rgba(16, 0, 32, 0.9); +} + +.logo { + width: 38px; + height: 38px; + background: radial-gradient(circle, #d946ef, #a855f7); + border-radius: 50%; display: flex; justify-content: center; align-items: center; - height: 100vh; + font-weight: 800; + color: #17002a; + font-size: 1.3rem; +} + +.header h1 { margin: 0; - background-color: #f5f5f5; + font-size: 1.2rem; } -main { - text-align: center; - background-color: white; - padding: 2rem; - border-radius: 8px; - box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); +.header p { + margin: 0.15rem 0 0; + font-size: 0.85rem; + color: var(--text-muted); +} + +/* Layout */ + +.grid { + flex: 1; + max-width: 960px; + margin: 1.5rem auto 2rem; + padding: 0 1.25rem; + display: grid; + gap: 1.3rem; + grid-template-columns: repeat(2, minmax(0, 1fr)); +} + +.card { + background: var(--bg-card); + border: 1px solid var(--border); + border-radius: var(--radius); + padding: 1.1rem 1.25rem; } -.count { - font-size: 4rem; - margin: 1rem 0; - color: #333; +.card-wide { + grid-column: 1 / -1; } -.counter-controls { +h2 { + margin: 0 0 0.5rem; +} + +.hint { + font-size: 0.85rem; + color: var(--text-muted); + margin-bottom: 0.8rem; +} + +/* Toggles */ + +.toggle-row { display: flex; - justify-content: center; - gap: 1rem; + align-items: center; + justify-content: space-between; + gap: 0.75rem; + margin-bottom: 0.7rem; } -button { - font-size: 1.5rem; - width: 3rem; - height: 3rem; - border: none; - border-radius: 50%; +.toggle-row .label { + font-size: 0.85rem; + color: var(--text-muted); +} + +.toggle-group { + display: flex; + gap: 0.35rem; +} + +.toggle { + border: 1px solid var(--border); + border-radius: 999px; + background: #10001e; + color: var(--text-muted); + padding: 0.25rem 0.6rem; + font-size: 0.8rem; cursor: pointer; - transition: background-color 0.2s; } -.decrement { - background-color: #ff6b6b; - color: white; +.toggle.active { + background: var(--purple-soft); + color: var(--purple-light); + border-color: var(--purple); } -.decrement:hover { - background-color: #ff5252; +/* Forms */ + +.form { + display: flex; + flex-direction: column; + gap: 0.7rem; } -.increment { - background-color: #4ecdc4; - color: white; +label { + font-size: 0.9rem; + display: flex; + flex-direction: column; + gap: 0.25rem; } -.increment:hover { - background-color: #39b2a9; +input, +textarea { + padding: 0.45rem 0.6rem; + background: #10001e; + color: var(--text); + border-radius: 8px; + border: 1px solid var(--border); + font-family: var(--font); + font-size: 0.9rem; + resize: vertical; } + +input:focus, +textarea:focus { + outline: none; From 092dd0163ea35cef2a03e23f1b26a3a5d88bd1bc Mon Sep 17 00:00:00 2001 From: bailey Date: Mon, 24 Nov 2025 12:27:57 -0700 Subject: [PATCH 2/2] rm --- README.md | 2 +- web/index.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 80fef0d..9f951b7 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ npm run dev Harper will start at: -```arduino +``` http://localhost:9926/ ``` diff --git a/web/index.html b/web/index.html index 0610268..475c51f 100644 --- a/web/index.html +++ b/web/index.html @@ -9,7 +9,7 @@
- +

Harper Owners & Dogs

Interact with your Harper endpoints from the browser.