1.0.2 • Published 1 year ago

crud-controller v1.0.2

Weekly downloads
-
License
MIT
Repository
github
Last release
1 year ago

CrudController

CrudController is a library indended for serverless environments to make basic CRUD (Create, Read, Update, and Delete) endpoints quick and easy. It currently assumes you are using the Kysely database query builder but it is possible that dependency will be moved to the adapter level so that it can support more databases in the future.

CrudController generates functions that map an input context (usually an HTTP request) into database queries and a result value. For example, the CrudController.show function generates a function that takes a context and finds the first result that matches it on the given table:

const crud = new CrudController(models, adapter);
const showThing = crud.show("things"); // generates a show function for the "things" table
await showThing({ params: { show_id: 1 } }); // does a SELECT * FROM things WHERE id = 1 and returns the result as an object

Context Adapter

The first thing you will need to set up is a context adapter. This object is used to interface between CrudController and whatever context your code is running in.

  • useDatabase: (c: C) => Kysely<Models>

This function returns an instance of Kysely. In a Cloudflare Worker, for example, you might pull out a D1Database from the env and use that to make a Kysely instance. Or you might pull out a DATABASE_URL from the env to setup a Planetscale database connection. If your database connection can be used between multiple requests, this function could return a saved instance of Kysely instead.

  • set(c: C, key: string, value: any): void

This function sets some arbitrary key-value in the context. In Honojs, this maps to the Context.set function. In Hapi.js, there's request.app. In Express.js, it's response locals.

  • get(c: C, key: string): any

This function retrieves a value previously set using set.

  • getRequestParams(c: C): AnyObject

This function returns an object of all the request params given as URL parameters in the router. For example, given a route organizations/:organization_id/users/:user_id/things/:thing_id, this function should return an object with keys organization_id, user_id, and thing_id with whatever values are in the URL.

  • getRequestBody(c: C): any

This function returns an object of the request body. For example, in the Fetch API, this function should return the equivalent of calling await response.json().

  • response(c: C, body: any, statusCode?: number): any

This function converts the response body and statusCode generated by the CrudController with a proper Response object for your environment.

CrudController

The CrudController is a class with a constructor that takes two parameters: the first is a Models object and the second is a context adapter (described above. The models object is an object with a key for each database table in your system and a Zod schema describing that table's schema as the value. Think the Database type parameter you would give to Kysely, but with a Zod schemas instead of the interface of the table.

index

This function lists all records for the given context and table.

create

This function inserts a record for the given context and table. It requires an attribute picker describing how the context maps to each column of the table:

// given this model:
const models = {
  things: z.object({
    thing_id: z.string().uuid(),
    name: z.string(),
    description: z.string().nullish(),
    created_at: z.string(),
    updated_at: z.string(),
  }),
};

// given this create function:

const createThing = crud.create("things", {
  attributes: {
    thing_id: "uuid",
    name: "payload",
    description: "payload",
    created_at: "inserted_at",
    updated_at: "updated_at",
  },
});

// calling it with this body

await createThing({
  body: {
    name: "My thing",
    description: null,
  },
});

// would run this SQL:

// INSERT INTO things (thing_id, name, description, created_at, updated_at)
    VALUES (randomUUID(), 'My thing', NULL, NOW(), NOW())

The attributes picker has several options you can use:

  • payload - take the value from the body of the request
  • param - take the value from the params (where the param name matches this column's name)
  • get - take the value from the context (using adapter.get)
  • arg - take the value from the argument object

For example,

const createThing = crud.create("things", {
  attributes: {
    thing_id: "uuid",
    name: "payload",
    description: "arg",
    created_at: "inserted_at",
    updated_at: "updated_at",
  },
});

// The second parameter for the generated create function is the argument object
await createThing({
  body: {
    name: "My thing"
  },
}, { description: "description" });

This is useful for doing nested CRUD operations.

  • uuid - generate a uuid for this column
  • inserted_at - generates the current timestamp for this column
  • updated_at - generates the current timestamp for this column
  • null - sets this column to NULL
  • default - use the database default value for this column

show

This function returns the first record for the given context and table. This should be limited via a route parameter with the primary key of the table.

update

This function updates first record for the given context and table. It also takes an attributes picker like the create function.

destroy

This function deleetes first record for the given context and table.

Example Usage

See the examples in examples/.