@hatchifyjs/koa is an NPM package that takes a Schema and produces:
- Sequelize models,
- an expressive JSON:API restful middleware, and
- utilities for building custom restful endpoints.
The following uses hatchifyKoa to create POST, GET, PATCH, and DELETE endpoints at /api/todos.
import { hatchifyKoa } from "@hatchifyjs/koa"
import { datetime, string, PartialSchema } from "@hatchifyjs/core"
import Koa from "koa"
// Define the schema
const schemas = {
Todo: {
name: "Todo",
attributes: {
name: string(),
dueDate: datetime(),
},
},
} satisfies Record<string, PartialSchema>
const app = new Koa()
// Pass schemas and other settings to configure hatchify
const hatchedKoa = hatchifyKoa(schemas, {
prefix: "/api",
database: { uri: "sqlite://localhost/:memory" },
})
;(async () => {
// Update the database to match the schema
await hatchedKoa.modelSync({ alter: true })
// Create CRUD endpoints for all schemas
app.use(hatchedKoa.middleware.allModels.all)
app.listen(3000, () => {
console.log("Started on http://localhost:3000")
})
})()- Exports
- hatchifyKoa - Creates a
hatchedKoainstance with middleware and sequelize ORMs - HatchifyKoa - A type for TypeScript usage
- JSONAPIDocument - A type for JSON:API document that can be used as a request/response body
- errorHandlerMiddleware - An error handle that produces JSON:API formatted errors
- hatchifyKoa - Creates a
hatchedKoa- An instance ofHatchifyKoawith middleware and sequelize ORMshatchedKoa.everything[schemaName]- Methods to parse a request, fetch data and serialize to a JSON:API response.hatchedKoa.middleware[schemaName|allModels]- Middleware to call the matching everything method.hatchedKoa.modelSync- A utility function to make sure your schemas are always synced with the database.hatchedKoa.orm- A reference to theSequelizeinstance when more control is needed.hatchedKoa.parse[schemaName|allModels]- Methods to parse a JSON:API request and return options that can be passed to the models to CRUD data.hatchedKoa.printEndpoints- Prints a list of endpoints generated by Hatchify.hatchedKoa.schema[schemaName]- All the Hatchify final schemas.hatchedKoa.serialize[schemaName]- methods to transform the result of models back into a JSON:API response.
@hatchifyjs/koa provides three named exports:
- hatchifyKoa - Creates a
hatchedKoainstance with middleware and Sequelize ORM - HatchifyKoa - A type for TypeScript fans
- errorHandlerMiddleware - A middleware to catch any Hatchify error and transform it to a proper JSON:API response
import { hatchifyKoa, HatchifyKoa, errorHandlerMiddleware } from "@hatchifyjs/koa"hatchifyKoa(schemas: Schemas, options: KoaOptions) is a Function that constructs a hatchedKoa instance with middleware and Sequelize ORM:
import { hatchifyKoa } from "@hatchifyjs/koa";
const schemas = { ... }
const app = new Koa()
const hatchedKoa = hatchifyKoa(schemas, {
prefix: "/api",
database: { uri: "sqlite://localhost/:memory" },
})Parameters
| Property | Type | Default | Details |
|---|---|---|---|
| schemas | Record<string, PartialSchema> | {} | A collection of Hatchify Schemas. |
| options.uri | string | sqlite://localhost/:memory | The database URI / connection string of the relational database. Ex. postgres://user:password@host:port/database?ssl=true |
| options.logging | (sql: string, timing?: number) => void | undefined | A function that gets executed every time Sequelize would log something. |
| options.additionalOptions | object | undefined | An object of additional options, which are passed directly to the underlying connection library (example: pg) |
See Using Postgres for instructions on how to set up HatchifyJS with postgres.
Returns
Returns a HatchifyKoa instance which is documented below.
HatchifyKoa is the constructor function used to create a hatchedKoa instance. This TypeScript type typically isn't used directly (it's exported to support implicit typing of the return from the hatchifyKoa constructor); however, it can be useful when defining a custom type that may reference hatchedKoa.
import type { HatchifyKoa } from "@hatchifyjs/koa"
import { hatchifyKoa } from "@hatchifyjs/koa"
type Globals = {
hatchedKoa: HatchifyKoa
}
const globals: Globals = {
hatchedKoa: hatchifyKoa(schemas, options);
}A type for JSON:API document that can be used as a request/response body.
A "flattened" JavaScript object with the record's data and associated record's data as child RecordObjects. See RecordObject for more details.
{
id: string,
name: string,
complete: boolean,
user: {
id: string,
email: string,
},
}errorHandlerMiddleware is a middleware to catch any Hatchify error and transform it to a proper JSON:API response. For example, the following shows a middleware that throws a fake error, preceded by errorHandlerMiddleware:
import { errorHandlerMiddleware } from "@hatchifyjs/koa"
app.use(errorHandlerMiddleware)
app.use(() => {
throw [new NotFoundError({ detail: "Fake error" })]
})so any request will throw and handled to return an error similar to
{
"jsonapi": {
"version": "1.0"
},
"errors": [
{
"status": 404,
"code": "not-found",
"detail": "Fake error",
"title": "Resource not found."
}
]
}hatchedKoa is an instance of HatchifyKoa that is returned by the hatchifyKoa function. It provides:
- Sequelize orm models,
- an expressive JSON:API restful middleware, and
- utilities for building custom restful endpoints.
The following show some of the methods available given a SalesPerson schema:
import { hatchifyKoa } from "@hatchifyjs/koa";
const hatchedKoa = hatchifyKoa({SalesPerson: {...}, {prefix: "/api"})
hatchedKoa.schema.SalesPerson // The full schemas
hatchedKoa.modelSync() // Sync the database with the schema
hatchedKoa.printEndpoints() // Prints a list of endpoints generated by Hatchify
// JSONAPI Middleware for CRUD operations
app.use(hatchedKoa.middleware.allModels.all);
app.use(hatchedKoa.middleware.SalesPerson.findAndCountAll)
app.use(hatchedKoa.middleware.SalesPerson.findOne)
app.use(hatchedKoa.middleware.SalesPerson.create)
app.use(hatchedKoa.middleware.SalesPerson.update)
app.use(hatchedKoa.middleware.SalesPerson.destroy)
// Methods that do "everything" the middleware does
await hatchedKoa.everything.SalesPerson.findAll("filter[name]=Jane")
await hatchedKoa.everything.SalesPerson.findAndCountAll("filter[name]=Baking")
await hatchedKoa.everything.SalesPerson.findOne("filter[name]=Baking")
await hatchedKoa.everything.SalesPerson.create({jsonapi: {...}, data: {...}})
await hatchedKoa.everything.SalesPerson.update({jsonapi: {...}, data: {...}}, UUID)
await hatchedKoa.everything.SalesPerson.destroy(UUID)
// Parse JSONAPI requests into arguments for sequelize
hatchedKoa.parse.SalesPerson.findAll("filter[name]=Jane")
hatchedKoa.parse.SalesPerson.findAndCountAll("filter[name]=Baking")
hatchedKoa.parse.SalesPerson.findOne("filter[name]=Baking")
hatchedKoa.parse.SalesPerson.create({jsonapi: {...}, data: {...}})
hatchedKoa.parse.SalesPerson.update({jsonapi: {...}, data: {...}}, UUID)
hatchedKoa.parse.SalesPerson.destroy(UUID)
// Use the underlying sequelize methods
await hatchedKoa.orm.models.SalesPerson.findAll({where: {name: "Jane"}})
await hatchedKoa.orm.models.SalesPerson.create({name: "Justin"})
await hatchedKoa.orm.models.SalesPerson.update({name: "Roye"},{where: {id: UUID}})
await hatchedKoa.orm.models.SalesPerson.destroy({where: {id: UUID}})
// Serialize sequelize data back to JSONAPI responses
hatchedKoa.serialize.SalesPerson.findAll([{ id: UUID, name: "Roye" }])
hatchedKoa.serialize.SalesPerson.findAndCountAll({rows: [{id: UUID, ...}], count: 1})
hatchedKoa.serialize.SalesPerson.findOne({ id: UUID, name: "Roye" })
hatchedKoa.serialize.SalesPerson.create({ id: UUID, name: "Roye" })
hatchedKoa.serialize.SalesPerson.update({ id: UUID, name: "Roye" })
hatchedKoa.serialize.SalesPerson.destroy()
hatchedKoa.everything[schemaName|allModels] functions very similar to the middleware export but is expected to be used more directly, usually when defining user-created middleware.
The everything functions takes the same properties as parse but goes further than just building the query options. This function will do a complete operation of parsing the request, performing the ORM query operation and then serializing the resulting data to JSON:API format.
For example hatchedKoa.everything.Todo.findAll takes the URL query params and directly returns JSON:API ready response data.
router.get("/todos", async (ctx: Context) => {
const serializedTodos = await hatchedKoa.everything.Todo.findAll(ctx.query)
ctx.body = serializedTodos
})hatchedKoa.middleware[schemaName|allModels]
All of the middleware functions export a Koa Middleware that can be passed directly to a Koa app.use or a Koa router[verb] function, mounted to a specific URL/path. The normal [schemaName] export expects to be used with:
- findAll
- findOne
- findAndCountAll
- create
- update
- destroy
hatchedKoa.modelSync({ alter: true } | { force: true } | undefined)
A utility function to make sure your schemas are always synced with the database.
If your database is created externally to Hatchify, you do not need to worry about it. Otherwise, Hatchify makes it simple by offering 3 syncing options:
hatchedKoa.modelSync()This creates the table if it does not exist (and does nothing if it already exists)
- Postgres: Namespaces (Postgres Schemas) are handled manually
hatchedKoa.modelSync({ alter: true })This checks what is the current state of the table in the database (which columns it has, what are their data types, etc), and then performs the necessary changes in the table to make it match the model.
- Postgres: Namespaces (Postgres Schemas) are created
hatchedKoa.modelSync({ force: true })This creates the table, dropping it first if it already existed
- Postgres: Namespaces (Postgres Schemas) and their tables are dropped and recreated
A reference to the Sequelize instance when more control is needed.
hatchedKoa.orm.models.Todo.findAll()hatchedKoa.parse[schemaName|allModels] has methods to parse a JSON:API request and return options that can be passed to the models to CRUD data.
hatchedKoa.printEndpoints()
Prints a list of endpoints generated by Hatchify. This can be useful for debugging 404 errors.
Example output:
Hatchify endpoints:
GET /api/todos
POST /api/todos
GET /api/todos/:id
PATCH /api/todos/:id
DELETE /api/todos/:id
GET /api/users
POST /api/users
GET /api/users/:id
PATCH /api/users/:id
DELETE /api/users/:idThis exports a single middleware function that based on the method and the URL will call the right everything function. It is useful as a default handler to handle all Hatchify GET/POST/PATCH/DELETE endpoints.
app.use(hatchedKoa.middleware.allModels.all)hatchedKoa.schema[schemaName]
The schema export provides access to all the Hatchify final schemas. This can be useful for debugging the schemas you provided.
console.log(hatchedKoa.schema)
// {
// Todo: {
// name: "Todo",
// namespace: "Admin",
// pluralName: "Todos",
// attributes: { ... },
// relationships: {
// user: { ... }
// }
// },
// ...
// }hatchedKoa.serialize[schemaName] has methods to transform the result of models back into a JSON:API response.