Models

Defining models

Use the @model directive on an object type definition to describe a data model. The table (table name) and pk (primary key) arguments are both required.

type Actor @model(table: "actor", pk: "id") {
id: ID!
firstName: String!
lastName: String!
}

Mapping fields to columns

Any fields on the type will be included in the model as columns. By default, the column names will be expected to match the field names, but you may configure a transformation to apply to the field name (like SNAKE_CASE) to generate the appropriate column name. The transformFieldNames option must be provided through the @sqlmancer directive. To explicitly associate a field with a differently named column, use the @col directive. To not have a field included in the model at all, use the @ignore directive.

type Film @model(table: "film", pk: "film_id") {
id: ID! @col(name: "film_id")
title: String!
description: String!
imdbInfo: ImdbInfo! @ignore
}

Virtual fields

It's possible to create virtual or computed fields by using the @depend directive. The columns passed to the directive's on argument will be added to the SQL query (if the field is requested) and can then be used inside the field's resolver. Note that the model may not include all of a table's columns, but any column can be passed to the on argument, even if it's not part of the model.

type Actor @model(table: "actor", pk: "actor_id") {
id: ID! @col(name: "actor_id")
firstName: String!
lastName: String!
fullName: String! @depend(on: ["first_name", "last_name"])
}

Private fields and models

Sometimes a field needs to be available through the client, but shouldn't be exposed as a field in the GraphQL schema. For example, we might want to access a password column but not actually expose this column in our API. You can use the @private directive to remove this fields from your schema while still enabling the client to write to them.

type User @model(table: "user", pk: "id") {
id: ID!
email: String!
password: String! @private
}

The @private directive can also be applied to object types, interfaces and unions, allowing you to add models to the database client without actually exposing these models as types in the schema.

Read-only models

Sqlmancer can model either tables or views in your database. Since views cannot be written to, it's helpful to indicate any models that utilize a view as being read-only. A read-only model has no methods for creating, updating or deleting its records.

type FamouseActor @model(
table: "famous_actor",
pk: "actor_id",
readOnly: true
) {
id: ID! @col(name: "actor_id")
firstName: String!
lastName: String!
}

You can also define inline views right in your schema using common-table expressions (CTEs). This allows you to create arbitrary views of your underlying data without having to write any migrations. Models for inline views still require a pk argument, but omit the table argument and instead provide a cte argument. These models are always read-only. Here's an example that combines multiple tables into a single view:

type Address
@model(
pk: "id"
cte: """
SELECT
address.address_id AS id,
address.address AS address_line,
address.address2 AS address_line_2,
address.postal_code AS postal_code,
city.city AS city,
country.country AS country,
address.last_update AS last_update
FROM address
INNER JOIN city ON address.city_id = city.city_id
INNER JOIN country ON city.country_id = country.country_id
"""
) {
id: ID!
addressLine: String!
addressLine2: String
postalCode: String
city: String!
country: String!
lastUpdate: DateTime!
}