Entity relations and inverse lookups
The term "entity relation" refers to the situation when an entity instance contains an instance of another entity within one of its fields. Type-wise this means that some entity (called the owning entity) has a field of a type that is some other, non-owning entity. Within the database, this is implemented as an (automatically indexed) foreign key column within the table mapped to the owning entity. A fieldName
entity-typed field will map to a column named field_name_id
.
One-to-one and one-to-many relations are supported by Typeorm. The "many" side of the one-to-many relations is always the owning side. Many-to-many relations are modeled as two one-to-many relations with an explicit join table.
An entity relation is always unidirectional, but it is possible to request the data on the owning entity from the non-owning one. To do so, define a field decorated @derivedFrom
in the schema. Doing so will cause the Typeorm code generated by squid-typeorm-codegen
and the GraphQL API served by squid-graphql-server
to show a virtual (that is, not mapping to a database column) field populated via inverse lookup queries.
The following examples illustrate the concepts.
One-to-one relations
type Account @entity {
id: ID!
balance: BigInt!
user: User @derivedFrom(field: "account")
}
type User @entity {
id: ID!
account: Account! @unique
username: String!
creation: DateTime!
}
The User
entity references Account
and owns the one-to-one relation. This is implemented as follows:
- On the database side: the
account
property of theUser
entity maps to theaccount_id
foreign key column of theuser
table referencing theaccount
table. - On the TypeORM side: the
account
property of theUser
entity gets decorated with@OneToOne
and@JoinColumn
. - On the GraphQL side: sub-selection of the
account
property is made available inuser
-related queries. Sub-selection of theuser
property is made available inaccount
-related queries.
Unlike for the many-to-one case, the codegen will not add a virtual reverse lookup property to the TypeORM code for one-to-one relations. You can add it manually:
import {OneToOne as OneToOne_} from "typeorm"
@Entity_()
export class Account {
// ...
@OneToOne_(() => User, e => e.account)
user: User
}
If you are using this feature, please let us know at the SquidDevs Telegram channel.
Many-to-one/One-to-many relations
type Account @entity {
"Account address"
id: ID!
transfersTo: [Transfer!] @derivedFrom(field: "to")
transfersFrom: [Transfer!] @derivedFrom(field: "from")
}
type Transfer @entity {
id: ID!
to: Account!
from: Account!
amount: BigInt!
}
Here Transfer
defines owns the two relations and Account
defines the corresponding inverse lookup properties. This is implemented as follows:
- On the database side: the
from
andto
properties of theTransfer
entity map tofrom_id
andto_id
foreign key columns of thetransfer
table referencing theaccount
table. - On the TypeORM side: properties
to
andfrom
of theTransfer
entity class get decorated with@ManyToOne
. PropertiestransfersTo
andtransfersFrom
decorated with@OneToMany
get added to theAccount
entity class. - On the GraphQL side: sub-selection of all relation-defined properties is made available in the schema.
Many-to-many relations
Many-to-many entity relations should be modeled as two one-to-many relations with an explicitly defined join table. Here is an example:
# an explicit join table
type TradeToken @entity {
id: ID! # This is required, even if useless
trade: Trade!
token: Token!
}
type Token @entity {
id: ID!
symbol: String!
trades: [TradeToken!]! @derivedFrom(field: "token")
}
type Trade @entity {
id: ID!
tokens: [TradeToken!]! @derivedFrom(field: "trade")
}