Skip to content

Query scope

Scope functions add server-controlled filters before generated root queries reach MongoDB. Use them to restrict records by tenant, owner, or another field in your GraphQL model.

Separate access decisions
IdentityApplication authenticationTrusted context

Your server verifies the caller.

PermissionEnvelop rulesOperation / field access

Decide which operations and fields that caller may use.

Read scopeServer-owned filtersMatching records

Root query scopes restrict reads. Authorize writes separately.

Define a shared scope

This example declares a tenantId field and applies the same restriction to all three root query operations. The tenant ID must come from authenticated server context.

javascript
import { GraphQLObjectType, GraphQLID, GraphQLString } from 'graphql';
import * as simfinity from '@simtlix/simfinity-js';

const tenantScope = async ({ args, context }) => {
  if (!context?.user?.tenantId) {
    throw new simfinity.auth.UnauthenticatedError();
  }
  args.tenantId = { operator: 'EQ', value: context.user.tenantId };
};

const SerieType = new GraphQLObjectType({
  name: 'Serie',
  extensions: {
    scope: {
      find: tenantScope,
      get_by_id: tenantScope,
      aggregate: tenantScope,
    },
  },
  fields: {
    id: { type: GraphQLID },
    name: { type: GraphQLString },
    tenantId: {
      type: GraphQLString,
      extensions: { readOnly: true },
    },
  },
});

simfinity.connect(null, SerieType, 'serie', 'series', {
  onSaving(doc, args, session, context) {
    if (!context?.user?.tenantId) {
      throw new simfinity.auth.UnauthenticatedError();
    }
    doc.tenantId = context.user.tenantId;
  },
});

The create hook sets the server-owned tenant field. Update and delete authorization must be enforced separately before changing a record.

Callback contract

javascript
async function scope({ type, args, operation, context }) {
  // Mutate args in place. The return value is not used as a filter.
}
PropertyMeaning
typeRegistered type metadata, including gqltype and model.
argsMutable query arguments used to construct the MongoDB pipeline.
operationfind, get_by_id, or aggregate.
contextThe application's GraphQL context.

Use GraphQL field names for filters. A scalar filter has { operator, value }. A related-object filter uses { terms: [{ path, operator, value }] }, as described in queries.

Operation behavior

OperationGenerated endpointScope arguments
findseries(...)Field filters, AND/OR, sorting, and pagination.
get_by_idserie(id: ...)The ID is normalized to args.id = { operator: 'EQ', value: id }.
aggregateseries_aggregate(...)Field filters plus the aggregation expression.

A scoped ID lookup returns null when no record matches both the requested ID and the added restriction. Preserve the normalized ID filter when adding a scope.

Scopes run after global middleware. Flat filters, including the filters added by a scope, are combined with user AND/OR conditions at the top level using AND. A user-provided OR does not remove that restriction.

Scope boundaries

Scope hooks apply to these generated root queries. They do not automatically run for generated relationship resolvers, direct Mongoose access, saveObject, or mutations. Keep relationships within the same authorization boundary, or supply custom relationship resolvers that apply the necessary restrictions.

Combine scope with authorization for operation and field permissions, and with controller checks for writes. A scope alone is not a complete tenant authorization policy.

Open source. Released under the Apache 2.0 License.