|
1 | 1 | # Create Your First Application
|
2 |
| -Now that you've set up Harper, let's build a simple Book API. Start by cloning the template: |
| 2 | +Now that you've set up Harper, let's build a simple API. Harper lets you build powerful APIs with minimal effort. In just a few minutes, you'll have a functional REST API with automatic validation, indexing, and querying—all without writing a single line of code. |
| 3 | + |
| 4 | +## Setup Your Project |
| 5 | +Start by cloning the Harper application template: |
3 | 6 |
|
4 | 7 | ```bash
|
5 | 8 | git clone https://github.com/HarperDB/application-template my-book-app
|
6 | 9 | cd my-book-app
|
7 | 10 | ```
|
8 | 11 |
|
9 |
| -The template includes: |
10 |
| -- **schema.graphql** – Your GraphQL schema definition |
11 |
| -- **resources.js** – For custom application logic |
12 |
| -- **config.yaml** – Application-level settings |
| 12 | +## Create a Complete API with Just a Schema |
| 13 | +Harper's power comes from its schema-first approach. Edit your `schema.graphql` file to define a Book table: |
13 | 14 |
|
14 |
| -## Define a GraphQL Schema |
15 |
| -Edit your `schema.graphql` file to create a Book table: |
16 | 15 | ```graphql
|
17 | 16 | type Book @table @export {
|
18 | 17 | id: ID @primaryKey
|
19 |
| - title: String! |
20 |
| - author: String! |
| 18 | + title: String! @indexed |
| 19 | + author: String! @indexed |
21 | 20 | publishedYear: Int
|
22 |
| - genre: String |
| 21 | + genre: String @indexed |
23 | 22 | }
|
24 | 23 | ```
|
25 | 24 |
|
26 |
| -The schema above does several important things: |
27 |
| -- Creates a `Book` table with the `@table` directive |
28 |
| -- Makes the table accessible via REST and GraphQL using the `@export` directive |
29 |
| -- Defines an `id` field as the primary key with the `@primaryKey` directive |
30 |
| -- Requires `title` and `author` fields (marked with `!`) |
31 |
| -- Adds optional `publishedYear` and `genre` fields |
| 25 | +That's it! This simple schema gives you: |
32 | 26 |
|
33 |
| -When you start your application, Harper will automatically create this table resource. |
| 27 | +- Automatic table creation with the `@table` directive |
| 28 | +- Full REST API with the `@export` directive (GET, POST, PUT, DELETE) |
| 29 | +- Data validation (title and author are required with `!`) |
| 30 | +- Fast querying with `@indexed` fields |
| 31 | +- Auto-generated primary keys with `@primaryKey` |
34 | 32 |
|
35 |
| -## Extend the Resource Class |
36 |
| -Now let's create the business logic for our Book API by implementing a custom resource class. Update your `resources.js` file: |
37 | 33 |
|
38 |
| -```js |
39 |
| -export class Books extends Resource { |
40 |
| - // Get a book by ID or list all books |
41 |
| - get() { |
42 |
| - const id = this.getId(); |
43 |
| - |
44 |
| - if (id) { |
45 |
| - // Return a single book |
46 |
| - return this.table.get(id); |
47 |
| - } else { |
48 |
| - // Return all books |
49 |
| - return this.table.get(); |
50 |
| - } |
51 |
| - } |
52 |
| - |
53 |
| - // Create a new book |
54 |
| - post(data) { |
55 |
| - // Validate required fields |
56 |
| - if (!data.title || !data.author) { |
57 |
| - return { error: "Title and author are required" }; |
58 |
| - } |
59 |
| - |
60 |
| - // Prevent primary key overriding |
61 |
| - if (data.id) delete data.id; |
62 |
| - |
63 |
| - try { |
64 |
| - return this.table.post(data); |
65 |
| - } catch (error) { |
66 |
| - return { error: "Error creating book", details: error.message }; |
67 |
| - } |
68 |
| - } |
69 |
| -} |
| 34 | +## Start Your Application |
| 35 | +```bash |
| 36 | +harperdb dev . |
70 | 37 | ```
|
71 |
| -Let's break down what this JavaScript file is doing: |
| 38 | +Harper automatically creates your Book table and REST endpoints. You now have a complete API at `http://localhost:9926/Book/` |
72 | 39 |
|
73 |
| -### Resource Class Extension |
74 |
| -```js |
75 |
| -export class Books extends Resource { |
| 40 | +## Test Your API |
| 41 | +### Create a book: |
| 42 | +```bash |
| 43 | +curl -X POST -H "Content-Type: application/json" \ |
| 44 | + -d '{"title": "The Great Gatsby", "author": "F. Scott Fitzgerald", "publishedYear": 1925, "genre": "Fiction"}' \ |
| 45 | + http://localhost:9926/Book |
76 | 46 | ```
|
77 | 47 |
|
78 |
| -This line creates a custom `Books` class that extends Harper's built-in `Resource` class. By doing this, we inherit all the standard functionality while allowing us to customize behavior. |
| 48 | +### Get all books: |
| 49 | +```bash |
| 50 | +curl http://localhost:9926/Book |
| 51 | +``` |
79 | 52 |
|
80 |
| -### GET Method |
81 |
| -```js |
82 |
| -get() { |
83 |
| - const id = this.getId(); |
84 |
| - |
85 |
| - if (id) { |
86 |
| - return this.table.get(id); |
87 |
| - } else { |
88 |
| - return this.table.get(); |
89 |
| - } |
90 |
| -} |
| 53 | +### Query by indexed fields: |
| 54 | +```bash |
| 55 | +curl "http://localhost:9926/Book?author=F. Scott Fitzgerald" |
| 56 | +curl "http://localhost:9926/Book?genre=Fiction" |
| 57 | +curl "http://localhost:9926/Book?publishedYear=1925&select(title,author)" |
| 58 | +``` |
| 59 | + |
| 60 | +### Get a specific book: |
| 61 | +```bash |
| 62 | +curl http://localhost:9926/Book/BOOK_ID_HERE |
91 | 63 | ```
|
92 | 64 |
|
93 |
| -The `get()` method handles HTTP GET requests to our Book endpoint. It: |
94 |
| -- Logs that a request was received (helpful for debugging) |
95 |
| -- Checks if an ID was provided in the URL path |
96 |
| -- If an ID exists, returns a single book by that ID |
97 |
| -- If no ID exists, checks for a `genre` query parameter |
98 |
| -- Returns all books, filtered by genre if specified |
| 65 | +## What You Get Automatically |
| 66 | +Harper provides enterprise-grade features out of the box: |
| 67 | + |
| 68 | +- **Content negotiation** (JSON, CBOR, MessagePack, CSV) |
| 69 | +- **Authentication** (Basic, JWT, Cookie) |
| 70 | +- **Caching headers** and ETags |
| 71 | +- **Data validation** based on your schema |
| 72 | +- **Automatic indexing** for fast queries |
| 73 | +- **Error handling** and proper HTTP status codes |
| 74 | + |
| 75 | +## Add Custom Logic (Optional) |
| 76 | +Only when you need custom business logic should you add code. Create a `resources.js` file to extend the default behavior: |
99 | 77 |
|
100 |
| -### POST Method |
101 | 78 | ```js
|
102 |
| -post(data) { |
103 |
| - // Validate required fields |
104 |
| - if (!data.title || !data.author) { |
105 |
| - return { error: "Title and author are required" }; |
| 79 | +export class Books extends Resource { |
| 80 | + // Add custom validation or computed fields |
| 81 | + post(data) { |
| 82 | + if (!data.title || !data.author) { |
| 83 | + return { error: "Title and author are required" }; |
| 84 | + } |
| 85 | + |
| 86 | + // Add publication decade for analytics |
| 87 | + if (data.publishedYear) { |
| 88 | + data.decade = Math.floor(data.publishedYear / 10) * 10; |
| 89 | + } |
| 90 | + |
| 91 | + return this.table.post(data); |
106 | 92 | }
|
107 |
| - |
108 |
| - // Prevent primary key overriding |
109 |
| - if (data.id) delete data.id; |
110 |
| - |
111 |
| - return this.table.post(data); |
112 | 93 | }
|
113 | 94 | ```
|
114 | 95 |
|
115 |
| -The `post()` method handles HTTP POST requests to create new books. It: |
116 |
| -- Handles both string and object data formats |
117 |
| -- Validates that required fields (`title` and `author`) are present |
118 |
| -- Removes any provided `id` field to prevent primary key conflicts |
119 |
| -- Creates the book record in the database |
120 |
| -- Returns the result or an error message if something goes wrong |
121 |
| -
|
122 |
| -## Run and Test |
123 |
| -1. Start your application: |
124 |
| - ```bash |
125 |
| - harperdb dev . |
126 |
| - ``` |
127 |
| -2. Create a book: |
128 |
| - ```bash\ |
129 |
| - curl -X POST -H "Content-Type: application/json" \ -d '{"title": "The Great Gatsby", "author": "F. Scott Fitzgerald", "publishedYear": 1925, "genre": "Fiction"}' \ http://localhost:9926/Books |
130 |
| - ``` |
131 |
| -3. Retrieve a book by ID: |
132 |
| - ```bash |
133 |
| - curl http://localhost:9926/Book/BOOK_ID_HERE |
134 |
| - ``` |
135 |
| -4. Get all books in a genre: |
136 |
| - ```bash |
137 |
| - curl http://localhost:9926/Book?genre=Fiction |
138 |
| - ``` |
139 |
| -
|
140 |
| -This simple Book API demonstrates Harper's key features: |
141 |
| -- Schema-based table definitions with GraphQL |
142 |
| -- Custom resource extensions for business logic |
143 |
| -- Automatic REST endpoint generation |
144 |
| -- Built-in data validation and error handling |
145 |
| -
|
146 |
| -You can build on this foundation by adding more fields, implementing additional methods, or creating relationships to other tables. |
| 96 | +## Key Takeaway |
| 97 | +Harper's schema-driven approach means you can build production-ready APIs in minutes, not hours. Start with pure schema definitions to get 90% of your functionality, then add custom code only where needed. This gives you the best of both worlds: rapid development with the flexibility to customize when required. |
0 commit comments