# Global and model-scoped actions  Gadget supports two different kinds of actions: and . ## Model-scoped actions  Model-scoped actions are actions that run in the context of specific records within a model. Model-scoped actions are invoked on a particular `record` object from their model, and are passed that record each time they are run. Actions that center around creating, updating, or deleting a specific record in the database are best expressed as model-scoped actions. For example, in a CRM application, we might have a model representing a `contact`. If a salesperson wants to remind themselves to contact that person later, a developer might implement a model-scoped action called `scheduleFollowUp` on the `contact` model. Because `scheduleFollowUp` concerns following up with a _specific_ contact, this action should be a model-scoped action. ### Model-scoped action `id`s  Because model-scoped actions operate on specific records, they often take an `id` parameter in the auto-generated API. This `id` parameter is the `id` of the record that the action is being invoked on. Gadget will load the record with this id, and pass it as the `record` object in the action's context. Create actions don't accept an `id`, as they are creating a new record. Instead, they get passed an unsaved record populated with the default values for each field. ## Globally-scoped actions  Globally-scoped actions are actions that do not operate in the context of a specific record or model. Globally-scoped actions aren't passed any `record` objects and don't accept any `id` parameters by default. Actions that touch many records from many models, or that mainly interface with other systems are often best expressed as globally-scoped actions. If there isn't a clear central record, globally-scoped actions are generally recommended. For example, in a CRM system, a developer might need to implement an action to send a weekly report to all the executives at a company. This report doesn't concern one particular user or one particular contact and instead relates to many different records. This globally-scoped action can still read data from other records, but because it doesn't concern one central one, it's easiest to implement as a globally-scoped action. ## Globally-scoped actions vs model-scoped actions  Globally-scoped actions are similar to model-scoped actions in that they operate using the action framework. However, model-scoped actions are tied to a specific model and are used when you want to operate on a specific instance of that model. For instance, if you have a User model and you want to create an action that changes the user's password, you would use a model-scoped action. This action would be tied to a specific user and would operate on that user's data. On the other hand, globally-scoped actions are not tied to any specific model, and are used when you want to perform an operation that doesn't necessarily relate to a specific instance of a model. For example, if you wanted to create an action that sends a newsletter email to all users, you would use a globally-scoped action. This action isn't tied to a specific user but rather operates on all users. Consider the table below to summarize the key differences between model and global scoped actions. | Features | Model-scoped actions | Globally-scoped actions | | --- | --- | --- | | **actionType** | Can be `create`, `update`, `delete`, or `custom` | No action type | | **default returnType** | Defaults to false | Defaults to true | | **default params** | `params`, `record`, `logger`, `api`, `connections` | `params`, `logger`, `api`, `connections` | | **Scheduling** | Cannot be scheduled | Can be scheduled | | **Sync trigger** | Executes model-scoped actions on Shopify models | Cannot be used | | **Record in context** | Record is passed as a parameter | Record is not passed | | **Strict type support** | | | The choice between a model-scoped action and a globally-scoped action depends on the specific requirements of the operation you want to perform. The suggestions above are a recommended convention. That being said, globally-scoped and model-scoped actions can mostly do the same things, so there is no restriction to follow this convention. However, it can be useful to follow these conventions, since some functions are easier to perform with a record or without. To further illustrate this, consider this globally-scoped function that can update a model called `file`: ```typescript import { applyParams, save, ActionOptions } from "gadget-server"; export const params = { recordId: { type: "string" }, fields: { type: "object", properties: { fileName: { type: "string" }, }, additionalProperties: true, }, }; export const run: ActionRun = async ({ params, logger, api, connections }) => { const record = await api.file.findOne(params.recordId || ""); if (params.fields) { Object.entries(params.fields).forEach(([key, value]) => { record[key] = value; }); } await save(record); }; ``` This action should clearly be a model-scoped action, as it requires an id to function. In fact, the `update` action generated by the file model requires much less code as a result of this. ```typescript import { applyParams, save, ActionOptions } from "gadget-server"; import { preventCrossUserDataAccess } from "gadget-server/auth"; export const run: ActionRun = async ({ params, record, logger, api, connections, }) => { applyParams(params, record); await preventCrossUserDataAccess(params, record); await save(record); }; export const options: ActionOptions = { actionType: "update", }; ``` Similarly, a model-scoped action could be used in place of a globally-scoped one, but it would be more expensive. For example, we could create a model-scoped action to send newsletters to all active subscribers. This would involve setting up a global action to trigger the model action, and the model action would need to run on every single user instance to determine if they are active before sending the newsletter. This is much more expensive than a globally-scoped action, which would filter for active users and then send the email. ## Actions vs HTTP routes  In addition to Actions, you can define HTTP Routes in your application. Both can execute backend logic and interact with your app’s API, but they serve different purposes. | Feature | Actions | HTTP Routes | | --- | --- | --- | | **Database Transactions** | Supports transactions | No transaction support | | **Scheduling** | Global actions be scheduled | Cannot be scheduled | | **GraphQL API** | Included automatically | Not included | | **Response Type** | JSON only | Any content (JSON, HTML, images, files, etc.) | | **Error Handling** | Built-in | Must be handled manually | | **Authentication** | Handled automatically | Must be handled manually | | **Frontend Usage** | Called with `useAction` or `useGlobalAction` | Called with `useFetch` | | **Auto-documented** | Provided for all your actions | Not provided for HTTP Routes | Gadget recommends using globally-scoped actions when possible because of the added benefits of being included in the Gadget app's GraphQL API. ### Actions working together  To better illustrate when to use model-scoped actions, globally-scoped actions, and HTTP routes, consider the following scenario: You are building a website to inform users of new products from various online retailers and to provide reviews. The schema for this application includes the model `ProductReview`, which stores reviews for records from `Product`. Users are also able to sign up for the website and view reviews. If you create a review for one of these products, a `create` model-scoped action could be used. Now suppose that users could sign up to be emailed when a new product is available. To email all subscribed users, you would need to use a globally-scoped action to access multiple user records at once. Finally, suppose you would like to implement a way for users to cancel subscriptions through text. To do this, you could implement an HTTP route, which listens for a Twilio webhook. The incoming message and user's phone number can be extracted from the webhook, and a model action can be called to cancel the user's subscription.