crud-controller
v1.0.2
Published
Generate crud functions for Kysely databases
Downloads
4
Readme
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/
.