# Gadget metadata files  Gadget uses TypeScript metadata files to define the structure of your application. These metadata files are the source of truth for your models, permissions, and app settings. There are three types of metadata files: * **Model schemas** (`api/models/**//schema.gadget.ts`) - Define the fields and structure of each model * **Permissions** (`accessControl/permissions.gadget.ts`) - Define role-based access control for your app * **Settings** (`settings.gadget.ts`) - Configure app-level settings, connections, and authentication All metadata file types are exported from `gadget-server` and can be imported for type safety: ```typescript import type { GadgetModel, GadgetPermissions, GadgetSettings } from "gadget-server"; ``` Metadata files are not visible in the web editor. They are used for source control when you [use `ggt dev`](https://docs.gadget.dev/guides/development-tools/cli#getting-started) to sync your project locally. ## Model schemas  Each model in your Gadget app has a `schema.gadget.ts` file that defines its fields and configuration. The schema exports a `GadgetModel` type that Gadget uses to generate your database tables, API, and type definitions. ```typescript import type { GadgetModel } from "gadget-server"; export const schema: GadgetModel = { type: "gadget/model-schema/v2", storageKey: "Ye9OpF2gBjPk", fields: { title: { type: "string", storageKey: "Zf1QrH4iDlRm", validations: { required: true, stringLength: { min: 1, max: 255 }, }, }, body: { type: "richText", storageKey: "Ag3StJ6kFnTo", }, publishedAt: { type: "dateTime", storageKey: "Bh5UvL8mHpVq", includeTime: true, }, }, }; ``` ### Model-level properties  | Property | Type | Required | Description | | --- | --- | --- | --- | | `type` | `"gadget/model-schema/v1"` \| `"gadget/model-schema/v2"` | Yes | Schema version. Use `v2` for new models to access `searchIndex` and `filterIndex` features. Requires framework version 1.5 or later. | | `storageKey` | `string` | Yes | The storage key addressing this model's data in the database. This is an auto-generated identifier. | | `comment` | `string` | No | A comment describing this model to other developers. | | `searchIndex` | `boolean` | No | (v2 only) Whether this model can be searched directly by full text search queries. Defaults to `true` when omitted. If `false`, the model can still be searched through related models. | | `fields` | `object` | Yes | An object mapping field API identifiers to field definitions. | | `shopify` | `object` | No | Configuration for this model's connection to Shopify. See . | ### Field types  Gadget supports many field types, each with specific properties and validations. Every field must have a `type` and `storageKey` property. #### Common field properties  These properties are available on most field types: | Property | Type | Description | | --- | --- | --- | | `storageKey` | `string` | Required. The storage key addressing this field's data in the database. | | `default` | varies | The default value for newly created records. Type depends on field type. | | `validations` | `object` | Validation rules to apply when saving records. | | `shopifyMetafield` | `object` | Connect this field to a Shopify metafield. See . | | `filterIndex` | `boolean` | (v2 only) Whether sorting and filtering are enabled for this field. Defaults to `true` when omitted. | | `searchIndex` | `boolean` | (v2 only) Whether this field can be searched by full text search queries. Defaults to `true` when omitted. | ### string  Editor tag: string Stores strings of UTF-8 characters. ```typescript import type { GadgetModel } from "gadget-server"; export const schema: GadgetModel = { type: "gadget/model-schema/v2", storageKey: "4wsXHT6K37US", fields: { name: { type: "string", storageKey: "Hs8KqL2mNpRt", default: "Untitled", validations: { required: true, stringLength: { min: 1, max: 500 }, regex: ["^[A-Za-z]+$"], unique: { scopeByField: "category", caseSensitive: true }, }, }, }, }; ``` | Property | Type | Description | | --- | --- | --- | | `default` | `string` | Default value for new records. | | `validations.required` | `boolean` | Validate that this field has a value. | | `validations.stringLength` | `{ min, max }` | Validate the string length is within range. Both `min` and `max` are `number` \| `null`. | | `validations.regex` | `(string` \| `null)[]` | Ensure values match one of the regular expressions. | | `validations.unique` | `boolean` \| `{ scopeByField?, caseSensitive? }` | Ensure values are unique. Can scope by another field and configure case sensitivity. | | `validations.run` | `(string` \| `null)[]` | Paths to TypeScript/JavaScript files that implement custom validations. | ### number  Editor tag: number Stores numeric values with optional decimal precision. ```typescript import type { GadgetModel } from "gadget-server"; export const schema: GadgetModel = { type: "gadget/model-schema/v2", storageKey: "Jw3XvB9cYdFg", fields: { price: { type: "number", storageKey: "Km5ZnD7eAhUi", default: 0, decimals: 2, validations: { required: true, numberRange: { min: 0, max: 10000 }, unique: { scopeByField: "category" }, }, }, }, }; ``` | Property | Type | Description | | --- | --- | --- | | `default` | `number` | Default value for new records. | | `decimals` | `number` | Number of decimal places to store. | | `validations.required` | `boolean` | Validate that this field has a value. | | `validations.numberRange` | `{ min, max }` | Validate the number is within range. | | `validations.unique` | `boolean` \| `{ scopeByField? }` | Ensure values are unique, optionally scoped by another field. | | `validations.run` | `(string` \| `null)[]` | Paths to TypeScript/JavaScript files that implement custom validations. | ### boolean  Editor tag: boolean Stores `true` or `false` values. ```typescript import type { GadgetModel } from "gadget-server"; export const schema: GadgetModel = { type: "gadget/model-schema/v2", storageKey: "Lo9PrF1gCjWk", fields: { isPublished: { type: "boolean", storageKey: "Mq2TsH4iElXm", default: false, validations: { required: true, }, }, }, }; ``` | Property | Type | Description | | --- | --- | --- | | `default` | `boolean` | Default value for new records. | | `validations.required` | `boolean` | Validate that this field has a value. | | `validations.run` | `(string` \| `null)[]` | Paths to TypeScript/JavaScript files that implement custom validations. | ### dateTime  Editor tag: date / time Stores timestamps with millisecond precision in UTC, or dates with day precision if `includeTime` is `false`. ```typescript import type { GadgetModel } from "gadget-server"; export const schema: GadgetModel = { type: "gadget/model-schema/v2", storageKey: "Nr6UvJ8kGnYo", fields: { publishedAt: { type: "dateTime", storageKey: "Ot4VwL0mIpZq", includeTime: true, default: "2024-01-01T00:00:00.000Z", validations: { required: true, unique: { scopeByField: "category" }, }, }, }, }; ``` | Property | Type | Description | | --- | --- | --- | | `includeTime` | `boolean` | Whether this field includes time. When `false`, stores only the date. | | `default` | `string` | Default value as an ISO 8601 string. | | `validations.required` | `boolean` | Validate that this field has a value. | | `validations.unique` | `boolean` \| `{ scopeByField? }` | Ensure values are unique. | | `validations.run` | `(string` \| `null)[]` | Paths to TypeScript/JavaScript files that implement custom validations. | ### email  Editor tag: email Stores a well-formatted email address as a string. Gadget automatically validates email format. ```typescript import type { GadgetModel } from "gadget-server"; export const schema: GadgetModel = { type: "gadget/model-schema/v2", storageKey: "Pu8XyN2oKrAs", fields: { email: { type: "email", storageKey: "Qv0ZaP4qMtBu", validations: { required: true, unique: { caseSensitive: false }, }, }, }, }; ``` | Property | Type | Description | | --- | --- | --- | | `default` | `string` | Default value for new records. | | `validations.required` | `boolean` | Validate that this field has a value. | | `validations.stringLength` | `{ min, max }` | Validate the length is within range. | | `validations.regex` | `(string` \| `null)[]` | Additional regex validation. | | `validations.unique` | `boolean` \| `{ scopeByField?, caseSensitive? }` | Ensure values are unique. | | `validations.run` | `(string` \| `null)[]` | Paths to TypeScript/JavaScript files that implement custom validations. | ### url  Editor tag: url Stores a well-formatted URL as a string. Gadget automatically validates URL format. ```typescript import type { GadgetModel } from "gadget-server"; export const schema: GadgetModel = { type: "gadget/model-schema/v2", storageKey: "Rw3AbR6sOvCw", fields: { website: { type: "url", storageKey: "Sx5CdT8uQxDy", default: "https://example.com", validations: { required: true, }, }, }, }; ``` | Property | Type | Description | | --- | --- | --- | | `default` | `string` | Default value for new records. | | `validations.required` | `boolean` | Validate that this field has a value. | | `validations.stringLength` | `{ min, max }` | Validate the length is within range. | | `validations.regex` | `(string` \| `null)[]` | Additional regex validation. | | `validations.unique` | `boolean` \| `{ scopeByField?, caseSensitive? }` | Ensure values are unique. | | `validations.run` | `(string` \| `null)[]` | Paths to TypeScript/JavaScript files that implement custom validations. | ### color  Editor tag: color Stores a well-formatted hex color as a string (for example, `#FF5733`). ```typescript import type { GadgetModel } from "gadget-server"; export const schema: GadgetModel = { type: "gadget/model-schema/v2", storageKey: "Ty7EfV0wSzFa", fields: { brandColor: { type: "color", storageKey: "Ua9GhX2yUbHc", default: "#000000", validations: { required: true, color: true, }, }, }, }; ``` | Property | Type | Description | | --- | --- | --- | | `default` | `string` | Default hex color value. | | `validations.required` | `boolean` | Validate that this field has a value. | | `validations.stringLength` | `{ min, max }` | Validate the length is within range. | | `validations.regex` | `(string` \| `null)[]` | Additional regex validation. | | `validations.color` | `boolean` | Validate that the value is a valid hex color string. | | `validations.run` | `(string` \| `null)[]` | Paths to TypeScript/JavaScript files that implement custom validations. | ### richText  Editor tag: rich text Stores markdown content for human consumption. ```typescript import type { GadgetModel } from "gadget-server"; export const schema: GadgetModel = { type: "gadget/model-schema/v2", storageKey: "Vb1IjZ4aWdJe", fields: { content: { type: "richText", storageKey: "Wc3KlB6cYfLg", default: { markdown: "# Welcome\n\nStart writing here." }, validations: { required: true, stringLength: { min: null, max: 50000 }, }, }, }, }; ``` | Property | Type | Description | | --- | --- | --- | | `default` | `{ markdown: string }` | Default markdown content. | | `validations.required` | `boolean` | Validate that this field has a value. | | `validations.stringLength` | `{ min, max }` | Validate the markdown length is within range. | | `validations.regex` | `(string` \| `null)[]` | Ensure values match a regular expression. | | `validations.unique` | `boolean` \| `{ scopeByField?, caseSensitive? }` | Ensure values are unique. | | `validations.run` | `(string` \| `null)[]` | Paths to TypeScript/JavaScript files that implement custom validations. | ### json  Editor tag: json Stores arbitrary JSON, including objects, arrays, and primitive values. ```typescript import type { GadgetModel } from "gadget-server"; export const schema: GadgetModel = { type: "gadget/model-schema/v2", storageKey: "Xd5MnD8eZhNi", fields: { metadata: { type: "json", storageKey: "Ye7OpF0gBjPk", default: { tags: [], settings: {} }, validations: { required: true, }, }, }, }; ``` | Property | Type | Description | | --- | --- | --- | | `default` | `any` | Default JSON value for new records. | | `defaultAsString` | `string` | Alternative to `default`: the default value as a JSON string. Mutually exclusive with `default`. | | `validations.required` | `boolean` | Validate that this field has a value. | | `validations.unique` | `boolean` \| `{ scopeByField? }` | Ensure values are unique. | | `validations.run` | `(string` \| `null)[]` | Paths to TypeScript/JavaScript files that implement custom validations. | ### enum  Editor tag: enum Stores a single string or an array of strings constrained to a list of valid options. ```typescript import type { GadgetModel } from "gadget-server"; export const schema: GadgetModel = { type: "gadget/model-schema/v2", storageKey: "Zf9QrH2iDlRm", fields: { status: { type: "enum", storageKey: "Ag1StJ4kFnTo", options: ["draft", "published", "archived"], default: "draft", acceptMultipleSelections: false, acceptUnlistedOptions: false, validations: { required: true, }, }, }, }; ``` | Property | Type | Description | | --- | --- | --- | | `options` | `string[]` | Required. The list of available options for this field. | | `default` | `string` \| `string[]` | Default value(s) for new records. | | `acceptMultipleSelections` | `boolean` | Whether this field stores an array or a single string selection. | | `acceptUnlistedOptions` | `boolean` | Whether this field accepts any option or only the listed options. | | `validations.required` | `boolean` | Validate that this field has a value. | | `validations.unique` | `boolean` \| `{ scopeByField? }` | Ensure values are unique. | | `validations.run` | `(string` \| `null)[]` | Paths to TypeScript/JavaScript files that implement custom validations. | ### vector  Editor tag: vector Stores a list of floats suitable for vector similarity operations, commonly used for AI embeddings. ```typescript import type { GadgetModel } from "gadget-server"; export const schema: GadgetModel = { type: "gadget/model-schema/v2", storageKey: "Bh3UvL6mHpVq", fields: { embedding: { type: "vector", storageKey: "Ci5WxN8oJrXs", validations: { required: true, dimensionCount: 1536, }, }, }, }; ``` | Property | Type | Description | | --- | --- | --- | | `validations.required` | `boolean` | Validate that this field has a value. | | `validations.dimensionCount` | `number` \| `null` | Validate vectors have a specific number of dimensions. | | `validations.run` | `(string` \| `null)[]` | Paths to TypeScript/JavaScript files that implement custom validations. | Vector fields are commonly used with AI embeddings. The `dimensionCount` should match your embedding model's output dimensions (for example, 1536 for OpenAI's `text-embedding-ada-002`). ### file  Editor tag: file Stores a reference to a single file uploaded to cloud storage. ```typescript import type { GadgetModel } from "gadget-server"; export const schema: GadgetModel = { type: "gadget/model-schema/v2", storageKey: "Dj7YzP0qLtZu", fields: { avatar: { type: "file", storageKey: "Ek9AbR2sNvBw", allowPublicAccess: true, validations: { required: true, fileSizeRange: { min: null, max: 5242880 }, imagesOnly: { allowAnimatedImages: true }, }, }, }, }; ``` | Property | Type | Description | | --- | --- | --- | | `allowPublicAccess` | `boolean` | Whether this field allows public access to uploaded files. | | `validations.required` | `boolean` | Validate that this field has a value. | | `validations.fileSizeRange` | `{ min, max }` | Validate file size in bytes. | | `validations.imagesOnly` | `boolean` \| `{ allowAnimatedImages }` | Restrict uploads to [image files](https://docs.gadget.dev/guides/models/fields#image-validation) only. | | `validations.run` | `(string` \| `null)[]` | Paths to TypeScript/JavaScript files that implement custom validations. | File fields do not support `filterIndex` or `searchIndex`. ### encryptedString  Editor tag: encrypted string Stores a string that is encrypted at rest. Use this for sensitive data that needs to be retrieved in its original form. ```typescript import type { GadgetModel } from "gadget-server"; export const schema: GadgetModel = { type: "gadget/model-schema/v2", storageKey: "Fl1CdT4uPxDy", fields: { apiKey: { type: "encryptedString", storageKey: "Gm3EfV6wRzFa", validations: { required: true, }, }, }, }; ``` | Property | Type | Description | | --- | --- | --- | | `validations.required` | `boolean` | Validate that this field has a value. | | `validations.run` | `(string` \| `null)[]` | Paths to TypeScript/JavaScript files that implement custom validations. | Encrypted string fields cannot be filtered, sorted, or searched because the values are encrypted. ### password  Editor tag: password Stores a hashed and salted bcrypt string. Values cannot be retrieved; they can only be verified. ```typescript import type { GadgetModel } from "gadget-server"; export const schema: GadgetModel = { type: "gadget/model-schema/v2", storageKey: "Hn5GhX8yTbHc", fields: { password: { type: "password", storageKey: "Io7IjZ0aVdJe", validations: { required: true, strongPassword: true, }, }, }, }; ``` | Property | Type | Description | | --- | --- | --- | | `validations.required` | `boolean` | Validate that this field has a value. | | `validations.strongPassword` | `boolean` | Validate that the password meets strength requirements. | Password fields are one-way hashed. You cannot retrieve the original value, only verify it using the `verifyPassword` helper. ### roleList  Editor tag: role list Stores a list of role names from your app's role list. Commonly used on User models for role-based access control. ```typescript import type { GadgetModel } from "gadget-server"; export const schema: GadgetModel = { type: "gadget/model-schema/v2", storageKey: "Jp9KlB2cXfLg", fields: { roles: { type: "roleList", storageKey: "Kq1MnD4eZhNi", default: ["signed-in"], validations: { required: true, }, }, }, }; ``` | Property | Type | Description | | --- | --- | --- | | `default` | `string[]` | Default roles for new records. | | `validations.required` | `boolean` | Validate that this field has a value. | | `validations.run` | `(string` \| `null)[]` | Paths to TypeScript/JavaScript files that implement custom validations. | ### computed  Stores a value computed using a [Gelly](https://docs.gadget.dev/reference/gelly) expression. Computed fields are read-only and automatically updated. ```typescript import type { GadgetModel } from "gadget-server"; export const schema: GadgetModel = { type: "gadget/model-schema/v2", storageKey: "Lr3OpF6gBjPk", fields: { fullName: { type: "computed", storageKey: "Ms5QrH8iDlRm", sourceFile: "api/models/user/fields/fullName.gelly", }, }, }; ``` | Property | Type | Description | | --- | --- | --- | | `sourceFile` | `string` \| `null` | Path to the Gelly file containing the computation expression. | Computed fields do not accept `validations` or `default` values since their values are derived from other fields. ### Relationship fields  Gadget supports four types of relationship fields to connect models together. #### belongsTo  Editor tag: belongs to Stores an ID pointing to a record of the parent model. This is the "many" side of a one-to-many relationship. ```typescript import type { GadgetModel } from "gadget-server"; export const schema: GadgetModel = { type: "gadget/model-schema/v2", storageKey: "Nt7StJ0kFnTo", fields: { author: { type: "belongsTo", storageKey: "Ou9UvL2mHpVq", parent: { model: "user" }, validations: { required: true, unique: { scopeByField: "organization" }, }, }, }, }; ``` | Property | Type | Description | | --- | --- | --- | | `parent.model` | `string` \| `null` | The API identifier of the parent model. | | `validations.required` | `boolean` | Validate that this field has a value. | | `validations.unique` | `boolean` \| `{ scopeByField? }` | Ensure values are unique (creates a one-to-one relationship). | | `validations.run` | `(string` \| `null)[]` | Paths to TypeScript/JavaScript files that implement custom validations. | #### hasOne  Editor tag: has one Fetches a single record from a child model, powered by a `belongsTo` field on the child model with a `unique` validation. ```typescript import type { GadgetModel } from "gadget-server"; export const schema: GadgetModel = { type: "gadget/model-schema/v2", storageKey: "Pv1WxN4oJrXs", fields: { profile: { type: "hasOne", storageKey: "Qw3YzP6qLtZu", child: { model: "profile", belongsToField: "user", }, }, }, }; ``` | Property | Type | Description | | --- | --- | --- | | `child.model` | `string` \| `null` | The API identifier of the child model. | | `child.belongsToField` | `string` \| `null` | The API identifier of the `belongsTo` field on the child model that powers this relationship. | #### hasMany  Editor tag: has many Fetches a list of records from a child model, powered by a `belongsTo` field on the child model. ```typescript import type { GadgetModel } from "gadget-server"; export const schema: GadgetModel = { type: "gadget/model-schema/v2", storageKey: "Rx5AbR8sNvBw", fields: { posts: { type: "hasMany", storageKey: "Sy7CdT0uPxDy", children: { model: "post", belongsToField: "author", }, }, }, }; ``` | Property | Type | Description | | --- | --- | --- | | `children.model` | `string` \| `null` | The API identifier of the child model. | | `children.belongsToField` | `string` \| `null` | The API identifier of the `belongsTo` field on the child model that powers this relationship. | #### hasManyThrough  Editor tag: has many through Fetches a list of sibling records through an intermediate join model. This creates a many-to-many relationship. ```typescript import type { GadgetModel } from "gadget-server"; export const schema: GadgetModel = { type: "gadget/model-schema/v2", storageKey: "Tz9EfV2wRzFa", fields: { tags: { type: "hasManyThrough", storageKey: "Ua1GhX4yTbHc", sibling: { model: "tag", relatedField: "posts", }, join: { model: "postTag", belongsToSelfField: "post", belongsToSiblingField: "tag", }, }, }, }; ``` | Property | Type | Description | | --- | --- | --- | | `sibling.model` | `string` \| `null` | The API identifier of the sibling model. | | `sibling.relatedField` | `string` \| `null` | The API identifier of the inverse `hasManyThrough` field on the sibling model. | | `join.model` | `string` \| `null` | The API identifier of the intermediate join model. | | `join.belongsToSelfField` | `string` \| `null` | The API identifier of the `belongsTo` field on the join model pointing to this model. | | `join.belongsToSiblingField` | `string` \| `null` | The API identifier of the `belongsTo` field on the join model pointing to the sibling model. | `hasOne`, `hasMany`, and `hasManyThrough` fields do not store data themselves. They are virtual fields that fetch related records based on other fields. ### Shopify configuration  If your model is connected to Shopify, you can configure which Shopify fields to sync. ```typescript import type { GadgetModel } from "gadget-server"; export const schema: GadgetModel = { type: "gadget/model-schema/v2", storageKey: "DataModel-Shopify-Product", fields: {}, shopify: { fields: { title: true, handle: true, description: { filterIndex: true, searchIndex: true, }, status: { fetchData: "onWebhook", }, }, }, }; ``` | Property | Type | Description | | --- | --- | --- | | `shopify.fields` | `object` | An object mapping Shopify field names to `true` (use defaults) or an object with optional `filterIndex`, `searchIndex`, and `fetchData` options. | | `shopify.fields..fetchData` | `"onWebhook"` \| `"later"` | [Framework v1.6.0+](https://docs.gadget.dev/guides/gadget-framework) When to fetch this field's data. `"later"` (default) fetches only during sync or reconciliation. `"onWebhook"` fetches when the model's webhook is processed. Only applies to non-webhook fields and to has many relationships that can be configured to fetch on webhook. Requires Shopify API version 2025-04 or later. For behavior details, see [non-webhook fields and models](https://docs.gadget.dev/guides/plugins/shopify/non-webhook-fields-and-models). | #### Shopify configuration `gadget/model-schema/v1`  Shopify configuration on apps on framework versions prior to framework version `v1.5.0` have a list of Shopify field API identifiers, excluding the `id` field. ```typescript import type { GadgetModel } from "gadget-server"; export const schema: GadgetModel = { type: "gadget/model-schema/v1", storageKey: "DataModel-Shopify-Product", fields: {}, shopify: { fields: [ "body", "category", "compareAtPriceRange", "handle", "images", "productCategory", "productType", "publishedAt", "shop", "shopifyCreatedAt", "shopifyUpdatedAt", "status", "tags", "templateSuffix", "title", "vendor", ], }, }; ``` | Property | Type | Description | | --- | --- | --- | | `shopify.fields` | `string[]` | An array of Shopify field API identifiers. | ### Shopify metafields  Many field types can be connected to Shopify metafields for two-way sync. ```typescript import type { GadgetModel } from "gadget-server"; export const schema: GadgetModel = { type: "gadget/model-schema/v2", storageKey: "Wc5KlB8cXfLg", fields: { customLabel: { type: "string", storageKey: "Xd7MnD0eZhNi", shopifyMetafield: { privateMetafield: false, namespace: "custom", key: "label", metafieldType: "single_line_text_field", allowMultipleEntries: false, }, }, }, }; ``` | Property | Type | Description | | --- | --- | --- | | `privateMetafield` | `boolean` | Whether this metafield is private (only accessible by your app). | | `namespace` | `string` | The namespace of the metafield. | | `key` | `string` | The key of the metafield. | | `metafieldType` | `string` | The Shopify metafield type. | | `allowMultipleEntries` | `boolean` | Whether this metafield allows multiple entries (list metafields). | ## Permissions  The `accessControl/permissions.gadget.ts` file defines role-based access control for your entire application. It specifies which roles can read records and run actions on each model. ```typescript import type { GadgetPermissions } from "gadget-server"; export const permissions: GadgetPermissions = { type: "gadget/permissions/v1", roles: { unauthenticated: { storageKey: "unauthenticated", default: { read: false, action: false, }, models: { post: { read: true, actions: { create: false, update: false, delete: false, }, }, }, }, "signed-in": { storageKey: "signed-in", default: { read: true, action: true, }, models: { user: { read: { filter: "accessControl/filters/user/tenant.gelly", }, actions: { update: { filter: "accessControl/filters/user/tenant.gelly", }, delete: { filter: "accessControl/filters/user/tenant.gelly", }, }, }, }, actions: { sendNewsletter: true, }, }, }, }; ``` ### Permissions structure  | Property | Type | Required | Description | | --- | --- | --- | --- | | `type` | `"gadget/permissions/v1"` | Yes | Schema version identifier. | | `roles` | `object` | Yes | An object mapping role keys to role configurations. | ### Role configuration  Each role is defined with the following properties: | Property | Type | Required | Description | | --- | --- | --- | --- | | `storageKey` | `string` | Yes | The storage key for this role. | | `default` | `object` | No | Default permissions for all models. | | `default.read` | `boolean` | No | Default read permission for all models. | | `default.action` | `boolean` | No | Default action permission for all models. | | `models` | `object` | No | Per-model permission overrides. | | `actions` | `object` | No | Permissions for global actions (not associated with a model). | ### Model permissions  For each model, you can specify: | Property | Type | Description | | --- | --- | --- | | `read` | `boolean` \| `{ filter: string` \| `null }` | Read permission. Use `true`/`false` for simple access, or provide a filter file path for row-level security. | | `actions` | `object` | An object mapping action names to permissions. | | `actions.[actionName]` | `boolean` \| `{ filter: string` \| `null }` | Permission for a specific action. Use a filter for row-level security. | ### Filter files  Filters are [Gelly](https://docs.gadget.dev/reference/gelly) expressions that restrict which records a role can access. They are stored in separate files and referenced by path. ```gelly // in accessControl/filters/user/tenant.gelly filter ($session: Session) on User [ where id == $session.userId ] ``` Filters provide row-level security by limiting which records a role can read or act upon. The filter expression runs in the context of the current session. ## Settings  The `settings.gadget.ts` file at the root of your `api` directory configures app-level settings including the framework version, connections, and authentication. ```typescript import type { GadgetSettings } from "gadget-server"; export const settings: GadgetSettings = { type: "gadget/settings/v1", frameworkVersion: "v1.5.0", plugins: { connections: { shopify: { apiVersion: "2025-01", enabledModels: ["shopifyShop", "shopifyProduct", "shopifyCollection"], type: "partner", scopes: ["read_products", "write_products"], }, openai: true, sentry: true, }, authentications: { settings: { redirectOnSignIn: "/", signInPath: "/sign-in", unauthorizedUserRedirect: "signInPath", defaultSignedInRoles: ["signed-in"], }, methods: { emailPassword: true, googleOAuth: { offlineAccess: false, scopes: ["email", "profile"], }, }, }, }, }; ``` ### Settings structure  | Property | Type | Required | Description | | --- | --- | --- | --- | | `type` | `"gadget/settings/v1"` | Yes | Schema version identifier. | | `frameworkVersion` | `string` | Yes | The Gadget framework version. See . | | `plugins` | `object` | Yes | Configuration for connections and authentication. | ### Framework versions  The `frameworkVersion` determines which Gadget features are available. See the [framework version changelog](https://docs.gadget.dev/guides/gadget-framework#framework-version-changelog) for more information. ### Connections  The `plugins.connections` object configures third-party service integrations. We do not recommend editing connection configuration manually. Instead, use the web editor to configure your connections. #### Shopify connection  ```typescript import type { GadgetSettings } from "gadget-server"; export const settings: GadgetSettings = { type: "gadget/settings/v1", frameworkVersion: "v1.5.0", plugins: { connections: { shopify: { apiVersion: "2025-01", enabledModels: ["shopifyShop", "shopifyProduct"], type: "partner", scopes: ["read_products", "write_products"], customerAuthenticationEnabled: true, }, }, }, }; ``` | Property | Type | Description | | --- | --- | --- | | `apiVersion` | `string` | Shopify API version (for example, `"2025-01"`). | | `enabledModels` | `string[]` | List of Shopify models to sync. | | `type` | `"partner"` \| `"admin"` | Connection type. Use `"partner"` for public apps with OAuth, `"admin"` for private/custom apps. `"admin"` connections have been deprecated by Shopify, all new apps will use `"partner"` connections. | | `scopes` | `string[]` | (Partner only) OAuth scopes to request. | | `customerAuthenticationEnabled` | `boolean` | (Partner only) Enable Shopify customer authentication. | These settings are configured in the Gadget web editor. See the [Shopify plugin guide](https://docs.gadget.dev/guides/plugins/shopify) for more information. #### OpenAI connection  ```typescript import type { GadgetSettings } from "gadget-server"; export const settings: GadgetSettings = { type: "gadget/settings/v1", frameworkVersion: "v1.5.0", plugins: { connections: { openai: true, }, }, }; ``` Set to `true` to enable the OpenAI connection. Configure API keys in the Gadget web editor. See the [OpenAI plugin guide](https://docs.gadget.dev/guides/plugins/openai) for more information. #### Sentry connection  ```typescript import type { GadgetSettings } from "gadget-server"; export const settings: GadgetSettings = { type: "gadget/settings/v1", frameworkVersion: "v1.5.0", plugins: { connections: { sentry: true, }, }, }; ``` Set to `true` to enable Sentry error tracking. Configure the Sentry DSN (Data Source Name) in the Gadget web editor. See the [Sentry plugin guide](https://docs.gadget.dev/guides/plugins/sentry) for more information. #### BigCommerce connection  ```typescript import type { GadgetSettings } from "gadget-server"; export const settings: GadgetSettings = { type: "gadget/settings/v1", frameworkVersion: "v1.5.0", plugins: { connections: { bigcommerce: { type: "singleClick", }, }, }, }; ``` | Property | Type | Description | | --- | --- | --- | | `type` | `"singleClick"` | BigCommerce app type. | These settings are configured in the Gadget web editor. See the [BigCommerce plugin guide](https://docs.gadget.dev/guides/plugins/bigcommerce) for more information. #### ChatGPT connection  ```typescript import type { GadgetSettings } from "gadget-server"; export const settings: GadgetSettings = { type: "gadget/settings/v1", frameworkVersion: "v1.5.0", plugins: { connections: { chatgpt: { authorizationPath: "/chatgpt/authorize", }, }, }, }; ``` | Property | Type | Description | | --- | --- | --- | | `authorizationPath` | `string` | The path for ChatGPT authorization flow. | See the [ChatGPT connection guide](https://docs.gadget.dev/guides/plugins/chatgpt) for more information. ### Authentication  The `plugins.authentications` object configures user authentication for your app. #### Authentication settings  ```typescript import type { GadgetSettings } from "gadget-server"; export const settings: GadgetSettings = { type: "gadget/settings/v1", frameworkVersion: "v1.5.0", plugins: { authentications: { settings: { redirectOnSignIn: "/dashboard", signInPath: "/sign-in", unauthorizedUserRedirect: "signInPath", accessControlForSignedInUsers: ["admin", "editor"], defaultSignedInRoles: ["signed-in"], }, methods: {}, }, }, }; ``` | Property | Type | Description | | --- | --- | --- | | `redirectOnSignIn` | `string` | URL to redirect to after successful sign-in. | | `signInPath` | `string` | Path to the sign-in page. | | `unauthorizedUserRedirect` | `"redirect"` \| `"signInPath"` \| `"show-403-error"` \| `"403Error"` | Behavior when an unauthorized user accesses a protected route. | | `accessControlForSignedInUsers` | `string[]` | Roles required for signed-in access (optional). | | `defaultSignedInRoles` | `string[]` | Roles automatically assigned to new users (optional). | #### Authentication methods  ```typescript import type { GadgetSettings } from "gadget-server"; export const settings: GadgetSettings = { type: "gadget/settings/v1", frameworkVersion: "v1.5.0", plugins: { authentications: { settings: { redirectOnSignIn: "/", signInPath: "/sign-in", unauthorizedUserRedirect: "signInPath", }, methods: { emailPassword: true, googleOAuth: { offlineAccess: true, scopes: ["email", "profile", "https://www.googleapis.com/auth/calendar"], }, }, }, }, }; ``` | Method | Type | Description | | --- | --- | --- | | `emailPassword` | `boolean` | Enable email/password authentication. | | `googleOAuth` | `object` | Enable Google OAuth authentication. | | `googleOAuth.offlineAccess` | `boolean` | Request offline access (refresh tokens). | | `googleOAuth.scopes` | `string[]` | OAuth scopes to request from Google. |