Usage¶
This page covers day-to-day usage of the foundry CLI as backed
by the kiln target. For a walkthrough of a brand-new project, see
Getting started. For the complete config schema, see
Reference.
Install¶
pip install kiln-generator # or: uv add kiln-generator
Installing kiln-generator ships both the generic foundry CLI
and the kiln target it discovers at startup.
The CLI¶
foundry generate¶
foundry generate --config PATH [--out DIR] [--target NAME] [--clean]
--config / -c(required)Path to the
.jsonor.jsonnetconfig file.--out / -o(optional)Output root directory. Defaults to the target’s own policy – kiln writes into the config’s
package_prefixvalue (e.g._generated). Setpackage_prefix: ""in the config to write directly into the current directory.--target / -t(optional)Which registered target to use. Optional when exactly one target is installed (
kiln, when only kiln-generator is installed).--cleanRun
foundry cleanbefore generating. Useful when you remove a resource from the config – without--cleanthe previously generated files for that resource stay on disk.
Re-running foundry generate is always safe: every generated file is
overwritten. Never edit files under the output directory – the next
run will discard your changes.
foundry clean¶
foundry clean --config PATH [--out DIR] [--target NAME]
Deletes the output directory. Resolves --out the same way
foundry generate does, so pointing the two commands at the same
config produces matching paths. The current working directory is
never deleted.
Config format¶
kiln accepts .json and .jsonnet files. Jsonnet is
recommended: imports, variables, and array concatenation make it much
more ergonomic for sharing common patterns across resources.
Minimal single-resource config¶
{
version: "1",
module: "myapp",
resources: [{
model: "myapp.models.Article",
operations: ["get", "list", "create", "update", "delete"],
}],
}
Full config with auth and multiple databases¶
{
version: "1",
module: "blog",
package_prefix: "_generated",
auth: {
type: "jwt",
secret_env: "JWT_SECRET",
verify_credentials_fn: "blog.auth.verify_credentials",
},
databases: [
{ key: "primary", url_env: "DATABASE_URL", default: true },
{ key: "analytics", url_env: "ANALYTICS_URL", echo: true },
],
operations: ["get", "list", "create", "update", "delete"],
resources: [
{
model: "blog.models.Article",
require_auth: true,
generate_tests: true,
},
{
model: "blog.models.ReadStat",
db_key: "analytics",
operations: ["get", "list"],
},
],
}
Notes on inheritance:
operationsat the root is the default applied to every resource that does not set its ownoperationslist.databasesis configured once at the root; resources choose one viadb_key(or fall back to the database withdefault: true).authis configured once at the root; each resource opts in viarequire_auth(defaults toTrue).
Operations¶
Each entry in a resource’s operations list is either:
A string, the operation name – invokes the operation with default options.
operations: ["get", "list", "create", "update", "delete"]
An object, carrying per-operation options:
{ name: "create", fields: [ { name: "title", type: "str" }, { name: "body", type: "str" }, ], }
Extra keys (
fields,max_items, …) are parsed by the operation’sOptionsPydantic model, so validation errors surface during config load rather than at generation time.An action, invoking a custom Python function as a POST endpoint:
{ name: "publish", fn: "blog.actions.publish", params: [{ name: "at", type: "datetime" }], }
foundry generates a
POST /{id}/publishhandler that callsblog.actions.publish.
Built-in operations¶
Name |
Scope |
Output |
|---|---|---|
|
project |
|
|
resource |
GET /{pk} route handler + response schema |
|
resource |
GET / route handler + list/filter/sort/paginate schemas |
|
resource |
POST / route handler + request schema |
|
resource |
PATCH /{pk} route handler + request schema |
|
resource |
DELETE /{pk} route handler |
|
resource |
POST /{pk}/{slug} or POST /{slug} handler for a custom action |
|
resource |
Augments handlers with |
|
app |
App-level |
|
project |
Project-level |
Multi-app projects¶
Wrap each app’s config in an apps entry:
// project.jsonnet
{
version: "1",
auth: { ... },
databases: [ ... ],
apps: [
{ config: import "blog.jsonnet", prefix: "/blog" },
{ config: import "inventory.jsonnet", prefix: "/inventory" },
],
}
Top-level auth, databases, and operations are merged into
each app config during generation. Individual apps can still override
these by defining their own blocks.
Jsonnet stdlib¶
kiln bundles a small Jsonnet stdlib that is importable from any config
file without a path prefix (the kiln prefix resolves to the stdlib
directory shipped inside the package).
See Reference for the full stdlib list. The most common:
kiln/auth/jwt.libsonnet–auth.jwt(...)preset for JWT.kiln/db/databases.libsonnet–db.postgres(...)constructor.
Testing the generated code¶
Setting generate_tests: true on a resource emits a pytest file
under _generated/.../tests/test_{name}.py. The file contains one
test per generated operation; run them with pytest as usual:
uv run pytest _generated/
API versioning¶
kiln has no built-in --version flag. To maintain multiple API
versions, run foundry generate against separate configs into separate
output trees and mount each at a different prefix:
foundry generate --config v1.jsonnet --out _generated_v1/
foundry generate --config v2.jsonnet --out _generated_v2/
from _generated_v1.myapp.routes import router as v1_router
from _generated_v2.myapp.routes import router as v2_router
app.include_router(v1_router, prefix="/v1")
app.include_router(v2_router, prefix="/v2")
Extending kiln¶
To add your own operations, swap renderers, or build an entirely new target, see Extending kiln. For the underlying architecture, see Architecture.