Take a gander!
// Run this code in a console, or from any app:
fetch("https://placegoose.mies.workers.dev/geese/1")
.then((response) => response.json())
.then((json) => console.log(json));
What's it good for?
Placegoose is a free online REST API for moments when you just need some honkin data! More than that, it's a learning resource for building blazing-fast REST APIs with the HO(N)C stack. You're welcome to use it as a reference, or clone the repo and modify the schema to fit your use-case.
How does the API work?
Placegoose comes with 3 goose-themed resources, supporting up to all 5 common HTTP verbs. We don't actually make updates to the DB in response to write requests, but we do validate payloads and verify the target exists. If something's not right, we'll return an error with a helpful message.
To learn more about making requests, or what different errors mean, check out our guide!
Resources
/gaggles | 10 gaggles |
/geese | 100 geese, each part of a gaggle |
/honks | 500 honks, each made by a goose |
Flying solo
You might want to build your own (mock?) data service, or may just be curious about the stack. This project was built using Hono and Drizzle ORM to highlight core features and implementation patterns. For more examples, or to get going with a template, check out our repo.
Take a deep dive into the development process and key features of the stack in our blog series. You'll need a basic understanding of TypeScript REST APIs and Cloudflare, but we tried to cover everything you'll need to get started!
Making requests
- No updates are made to the DB. Responses to write operations are simulated by merging the validated request payload with the stored data.
- We use integer IDs for convenience. Production DBs should use an ID system that obscures database architecture, like UUIDs.
- We validate. Write resources expect a JSON payload, and malformed payloads will result in a
400
, making it possible to demo error flows. - Write operations against records that do not exist will return a
404
.
Reference
Get All | Create |
Get All (By Relation) | Modify |
Get By Id | Delete |
Update | Errors |
Get All Records
Filtering, sorting, and pagination are not currently supported.
fetch("https://placegoose.mies.workers.dev/honks")
.then((response) => response.json())
.then((json) => console.log(json))
.catch((error) => console.error(error));
Status: 200
[
{
"id": 1,
"gooseId": 16,
"decibels": 104
},
// ...
{
"id": 100,
"gooseId": 17,
"decibels": 82
},
]
Get All Records (Filtered by relation)
This feature is only available for the /honks
resource at this time.
fetch("https://placegoose.mies.workers.dev/honks?gooseId=1")
.then((response) => response.json())
.then((json) => console.log(json))
.catch((error) => console.error(error));
Status: 200
[
{
"id": 78,
"gooseId": 17,
"decibels": 110
},
{
"id": 100,
"gooseId": 17,
"decibels": 82
}
]
Create Record
Geese cannot be created by API request.
fetch("https://placegoose.mies.workers.dev/honks", {
method: "POST",
body: JSON.stringify({
gooseId: 42,
decibels: 64,
}),
headers: {
"Content-Type": "application/json; charset=UTF-8",
},
})
.then((response) => response.json())
.then((json) => console.log(json))
.catch((error) => console.error(error));
Status: 201
{
"id": 325,
"gooseId": 42,
"decibels": 64
}
Get Record By ID
fetch("https://placegoose.mies.workers.dev/honks/1")
.then((response) => response.json())
.then((json) => console.log(json))
.catch((error) => console.error(error));
Status: 200
{
"id": 1,
"gooseId": 16,
"decibels": 104
}
Update Record
In most cases, PUTs update the whole record, but honks are read-only properties.
fetch("https://placegoose.mies.workers.dev/honks/1", {
method: "PUT",
body: JSON.stringify({
decibels: 299,
}),
headers: {
"Content-Type": "application/json; charset=UTF-8",
},
})
.then((response) => response.json())
.then((json) => console.log(json))
.catch((error) => console.error(error));
Status: 200
{
"id": 1,
"gooseId": 16,
"decibels": 299
}
Modify Record
PATCHes accept partial updates.
fetch("https://placegoose.mies.workers.dev/honks/1", {
method: "PATCH",
body: JSON.stringify({
decibels: 36,
}),
headers: {
"Content-Type": "application/json; charset=UTF-8",
},
})
.then((response) => response.json())
.then((json) => console.log(json))
.catch((error) => console.error(error));
Status: 200
{
"id": 1,
"gooseId": 16,
"decibels": 36
}
Delete Record
DELETes don't return a body.
fetch("https://placegoose.mies.workers.dev/honks", {
method: "DELETE",
})
.then((response) => response.json())
.then((json) => console.log(json))
.catch((error) => console.error(error));
Status: 204
Errors
Whens something goes wrong, we'll try and share a helpful message! If you're running your own instance, use Fiberplane Studio to inspect logs and the request timeline.
type ErrorData = {
message: string;
/** Only included for validation errors */
issues?: Record<string, string[]>;
}
Status | Issue |
---|---|
400 | Request Error: Make sure the request is properly formatted, and that you're using a valid ID and payload (if applicable). |
403 | Forbidden: The darkness beacons. |
404 | Not Found: Verify you're using an ID within the resource limits or try a Get All request to double-check. |
500 | Something went terribly wrong. If you're using our deployed service, please create an issue to let us know. |
Routes
Gaggles
type Gaggle = {
id: number;
name: string;
territory: string | null;
}
GET | /gaggles |
POST | /gaggles |
GET | /gaggles/1 |
GET | /gaggles/1/geese |
PUT | /gaggles/1 |
DELETE | /gaggles/1 |
Geese
type Goose = {
id: number;
gaggleId: number | null;
name: string;
isMigratory: boolean;
mood: "hangry", "waddling", "stoic", "haughty", "alarmed" | null;
}
GET | /geese |
GET | /geese/:id |
GET | /geese/:id/honks |
Honks
type Honk = {
id: number;
// Note: honks cannot be reassigned to a different goose
gooseId: number;
decibels: number;
}
GET | /honks |
GET | /honks?gooseId=1 |
POST | /honks |
GET | /honks/1 |
PATCH | /honks/1 |
PUT | /honks/1 |
DELETE | /honks/1 |