Skip to content

Context

Context lets you pass per-request data (current user, tenant, locale, etc.) into computed and derived field resolvers. It is fully typed.

Defining the context type

Pass the context type when creating the Relayer client:

interface AppContext {
currentUserId: number;
tenantId: string;
}
const r = createRelayerDrizzle({
db,
schema,
context: {} as AppContext,
entities: { users: User },
});

The context: {} as AppContext parameter defines the shape. The empty object is just a type hint, context values are provided per-query.

Using context in computed fields

The resolve function receives context as part of its argument:

const UserEntity = createRelayerEntity(schema, 'users');
class User extends UserEntity {
@UserEntity.computed({
resolve: ({ table, sql, context }) =>
sql`CASE WHEN ${table.id} = ${(context as AppContext).currentUserId} THEN true ELSE false END`,
})
isMe!: boolean;
}

Using context in derived fields

The query function also receives context:

class User extends UserEntity {
@UserEntity.derived({
query: ({ db, schema: s, sql, context, field }) =>
db
.select({ [field()]: sql`count(*)::int`, userId: s.orders.userId })
.from(s.orders)
.where(sql`${s.orders.createdAt} > ${(context as AppContext & { since: Date }).since}`)
.groupBy(s.orders.userId),
on: ({ parent, derived, eq }) => eq(parent.id, derived.userId),
})
recentOrderCount!: number;
}

Passing context per-query

Provide context values in each query call:

const users = await r.users.findMany({
select: { id: true, firstName: true, isMe: true },
context: { currentUserId: 42, tenantId: 'acme' },
});
// [
// { id: 42, firstName: 'John', isMe: true },
// { id: 43, firstName: 'Jane', isMe: false },
// ]

Typical use cases

Current user

class User extends UserEntity {
@UserEntity.computed({
resolve: ({ table, sql, context }) =>
sql`CASE WHEN ${table.createdBy} = ${(context as any).userId} THEN true ELSE false END`,
})
isOwner!: boolean;
}

Multi-tenancy

class User extends UserEntity {
@UserEntity.derived({
query: ({ db, schema: s, sql, context, field }) =>
db
.select({ [field()]: sql`count(*)::int`, userId: s.orders.userId })
.from(s.orders)
.where(sql`${s.orders.tenantId} = ${(context as any).tenantId}`)
.groupBy(s.orders.userId),
on: ({ parent, derived, eq }) => eq(parent.id, derived.userId),
})
tenantOrderCount!: number;
}

Time-based filtering

class User extends UserEntity {
@UserEntity.derived({
query: ({ db, schema: s, sql, context, field }) =>
db
.select({ [field()]: sql`count(*)::int`, userId: s.events.userId })
.from(s.events)
.where(sql`${s.events.createdAt} >= ${(context as any).since}`)
.groupBy(s.events.userId),
on: ({ parent, derived, eq }) => eq(parent.id, derived.userId),
})
recentActivity!: number;
}