@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
hatchedKoa
instance 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 ofHatchifyKoa
with 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 theSequelize
instance 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
hatchedKoa
instance 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/:id
This 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.