Skip to content
Docs

Schema Generation

BifrostQL generates its entire GraphQL schema from your database metadata. There are no mapping files, no code generation steps, and no manual type definitions. The database is the single source of truth.

At startup, BifrostQL:

  1. Connects to your database using the configured provider
  2. Reads all tables, columns, primary keys, and foreign key relationships
  3. Maps SQL types to GraphQL types using the dialect’s type mapper
  4. Generates query fields (one per table, paged), mutation fields (one table field with insert, update, upsert, delete arguments), batch mutation fields, and input types
  5. Applies metadata rules to hide tables/columns, configure modules, and adjust behavior
  6. Caches the schema for the lifetime of the application

When you add a table or column to the database, restart the application and the corresponding GraphQL field appears with the correct type and nullability.

Each SQL dialect defines its own type mapper. The general mapping follows this pattern:

SQL TypeGraphQL Type
int, bigint, smallintInt
decimal, numeric, moneyDecimal
float, real, doubleFloat
varchar, nvarchar, textString
bit, boolean, tinyint(1)Boolean
datetime, timestamp, dateDateTime
uniqueidentifier, uuidString

Columns that are non-nullable in the database produce non-nullable (!) GraphQL fields. Primary key columns generate the appropriate ID-style handling in mutations.

For a table named orders, BifrostQL generates:

type database {
orders(
limit: Int
offset: Int
sort: [ordersSortEnum!]
filter: TableFilterordersInput
_primaryKey: [String]
): orders_paged
}
type orders_paged {
data: [orders]
total: Int!
offset: Int
limit: Int
}

The sort enum contains one entry per column in both ascending and descending variants:

enum ordersSortEnum {
orderId_asc
orderId_desc
customerId_asc
customerId_desc
total_asc
total_desc
}

For each table with a primary key, BifrostQL generates one mutation field with four operation arguments, plus a batch field:

type databaseInput {
orders(
insert: Insert_orders
update: Update_orders
upsert: Upsert_orders
delete: Delete_orders
_primaryKey: [String]
): Int
orders_batch(actions: [batch_orders!]!): Int
}

Insert inputs exclude auto-increment and computed columns. Update and delete inputs use primary-key values to locate rows. Table mutation fields return scalar values: inserted identity, updated primary key, affected row count, or applied batch count.

You can control schema generation with metadata rules in your configuration:

{
"BifrostQL": {
"Metadata": [
"dbo.sys* { visibility: hidden; }",
"dbo.*.__* { visibility: hidden; }",
"dbo.*.password_hash { visibility: hidden; }"
]
}
}

Hidden tables and columns are excluded from the generated schema entirely. See Configuration for the full metadata rule syntax.

The schema is cached per endpoint path using PathCache<Inputs>. This means the schema is built once and reused for all requests to the same path. To pick up database changes, restart the application.

BifrostQL preserves your database naming conventions in the GraphQL schema. Table names become query fields (with optional de-pluralization). Column names become field names as-is. This means your GraphQL queries match your database structure directly — no surprises, no name translation layer.

If you need de-pluralized table names (e.g., users table exposed as user query), set the de-pluralize metadata property:

"dbo.* { de-pluralize: true; }"