Skip to content

Latest commit

 

History

History
361 lines (265 loc) · 15.9 KB

README.md

File metadata and controls

361 lines (265 loc) · 15.9 KB

@hatchifyjs/koa

@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

@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

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

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);
}

JSONAPIDocument

A type for JSON:API document that can be used as a request/response body.

Read more on the type

RecordObject

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

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

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]

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]

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

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

hatchedKoa.orm

A reference to the Sequelize instance when more control is needed.

hatchedKoa.orm.models.Todo.findAll()

hatchedKoa.parse[schemaName|allModels]

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

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

hatchedKoa.middleware.allModels.all

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]

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]

hatchedKoa.serialize[schemaName] has methods to transform the result of models back into a JSON:API response.