Links — ASL
Links define foreign-key relationships between types.
Single link (FK column)
Section titled “Single link (FK column)”type Post { required link author: User; # adds an FK column named "author"}The column is named after the field, not the target type, and carries no _id suffix — link author: User gives you post.author, referencing user.id. Filters and shapes still use the field
name (.author, author: { … }), so the column name only matters when you read the generated SQL.
Multi link (junction table)
Section titled “Multi link (junction table)”type Post { multi link tags: Tag; # creates post_tags junction table}The junction table name is {source}_{link} in snake_case (e.g. post_tags), and its two FK
columns are named after the tables they reference:
CREATE TABLE "post_tags" ( "post" UUID NOT NULL, "tag" UUID NOT NULL, CONSTRAINT "pk_post_tags" PRIMARY KEY ("post", "tag"), CONSTRAINT "fk_post_tags_post" FOREIGN KEY ("post") REFERENCES "post"("id") ON DELETE CASCADE, CONSTRAINT "fk_post_tags_tag" FOREIGN KEY ("tag") REFERENCES "tag"("id") ON DELETE CASCADE);Self-referential multi links
Section titled “Self-referential multi links”When a multi link points back at its own type, naming both sides after the referenced table would
produce two columns called product, which Postgres rejects (column "product" appears twice in primary key constraint). The target side falls back to the link name:
type Product { required id: uuid { constraint pk; }; multi link addons: Product; # product_addons("product", "addons")}Nothing about querying changes — select Product { id, addons: { id } } works as it does for any
other multi link.
Multi scalars are not links
Section titled “Multi scalars are not links”multi on a scalar field means something different from multi link: it declares an array
column on the row, not a junction table.
type User { multi link teams: Team; # junction table "user_teams" multi roles: UserType; # a single TEXT[] column "roles"}The distinction shows up in two places:
-
Membership.
inagainst a multi scalar compiles to= ANY(...), because PostgresINtakes a parenthesised list rather than an array. Against a multi link it compiles to anEXISTSover the junction table.select User { id } filter UserType.Admin in .roles; # → 'Admin' = ANY(u.roles)select User { id } filter $team<uuid> in .teams; # → EXISTS (SELECT 1 FROM "user_teams" …) -
Assignment. Delta assignment (
{ "+": …, "-": … }) applies only to a multi link. A multi scalar is assigned as a whole array — see Updating links.
Required links
Section titled “Required links”type Comment { required link post: Post; # column "post", NOT NULL required link author: User; # column "author", NOT NULL}Adding a required link to a table that already has rows needs a backfill, exactly like adding a
required column — see axel diff.