--- llm: "Entity-level , row-level , and field-level authorization patterns." --- # Access Control ::: tip **Interactive Learning Available! πŸš€** Looking to get hands-on with this topic? Try out our new on Access Control, where you can explore and practice directly in the browser. This guided experience offers step-by-step lessons to help you master Access Control in Remult with practical examples and exercises. ::: Access control is essential for ensuring that users can only access resources they are authorized to in web applications. This article explores the various layers of access control, focusing on a framework that provides a granular approach to securing your application. ## Entity-Level Authorization Entity-level authorization governs CRUD operations at the entity level. Each entity can define permissions for these operations using the following options: - `allowApiRead`: Controls read access. - `allowApiInsert`: Controls insert access. - `allowApiUpdate`: Controls update access. - `allowApiDelete`: Controls delete access. Each option can be set to a boolean, a string role, an array of string roles, or an arrow function: ## Row-Level Authorization Row-level authorization allows control over which rows a user can access or modify. ### Authorization on Specific Rows The `allowApiUpdate`, `allowApiDelete`, and `allowApiInsert` options can also accept a function that receives the specific item as the first parameter, allowing row-level authorization: ### Filtering Accessible Rows To limit the rows a user has access to, use the `apiPrefilter` option: The `apiPrefilter` adds a filter to all CRUD API requests, ensuring that only authorized data is accessible through the API. ### Preprocessing Filters for API Requests For more complex scenarios, you can use `apiPreprocessFilter` to dynamically modify the filter based on the specific request and additional filter information: In this example, `apiPreprocessFilter` uses the `getPreciseValues` method to ensure that users must specify a valid `customerId` filter when querying tasks, allowing for more granular control over the data that is accessible through the API. **Note:** The `preciseValues` object includes the actual values that are used in the filter. For example, in the code sample above, if the `customerId` filter specifies the values `'1'`, `'2'`, and `'3'`, then `preciseValues.customerId` will be an array containing these values. This allows you to check and enforce specific filter criteria in your preprocessing logic. This added note explains the significance of the `preciseValues` property and how it includes the actual values used in the filter, providing an example for clarity. ### Warning: API Filters Do Not Affect Backend Queries It's important to note that `apiPrefilter` and `apiPreprocessFilter` only apply to API requests. They do not affect backend queries, such as those executed through backend methods or non-Remult routes. For instance, in a sign-in scenario, a backend method might need to check all user records to verify a user's existence without exposing all user data through the API. Once authenticated, the user should only have access to their own record for updates. ### Backend Filters for Consistent Access Control To apply similar filtering logic to backend queries, you can use `backendPrefilter` and `backendPreprocessFilter`: In this example, `backendPrefilter` and `backendPreprocessFilter` ensure that non-admin users can only access their own tasks in backend queries, providing consistent access control across both API and backend operations. ## Field-Level Authorization Field-level authorization allows control over individual fields within an entity: _Field level authorization happens after entity level authorization AND if it's allowed._ - `includeInApi`: Determines if the field is included in the API response. - `allowApiUpdate`: Controls if a field can be updated. If false, any change to the field is ignored. Examples: ### Field Masking To mask a field, combine a non-API field with a `serverExpression` that returns the masked value: ## BackendMethod Authorization Backend methods use the `allowed` option to determine authorization: The `allowed` option can receive a boolean, a string role, an array of role strings, or a function. ## Reusing Access Control Definitions in the Frontend Access control definitions set in entities can be reused as a single source of truth in the frontend. This allows for consistent and centralized management of access control logic across your application. For example, in a React component, you can conditionally render UI elements based on the access control rules defined in the entity: ::: code-group ::: ## Additional Resources Check out this informative . It discusses the concepts covered in this article and provides practical examples to help you understand how to implement robust access control in your applications. --- This article provides a comprehensive overview of the layers of access control in web applications, offering a granular approach to securing your application at the entity, row, field, and method levels. --- llm: "Active Record pattern via EntityBase/IdEntity - mutable instances with save/delete methods bound to rows." --- # Mutability and the Active Record Pattern The Active Record pattern is a concept in software architecture, particularly useful when working with mutable objects whose state may change over time. This design pattern facilitates direct interaction with the database through the object representing a row of the data table. In this article, we'll delve into the fundamentals of the Active Record pattern, contrasting it with immutable patterns, and exploring its implementation and advantages in software development. ### Immutable vs. Mutable Patterns In modern software development, handling data objects can generally be approached in two ways: immutable and mutable patterns. **Immutable objects** do not change once they are created. Any modification on an immutable object results in a new object. For example, in the React framework, immutability is often preferred: However, libraries like MobX offer the flexibility to work with mutable objects while still providing the reactivity that React components need. **Mutable objects**, on the other hand, allow for changes directly on the object itself: Mutable patterns are especially prevalent in scenarios where the state of objects changes frequently, making them a staple in many programming environments outside of React. ### The Role of Active Record Pattern The Active Record pattern embodies the concept of mutability by binding business logic to object data models. Typically, each model instance corresponds to a row in the database, with the class methods providing the functionality to create, read, update, and delete records. ### Warning: Mutable Objects in React Using mutable objects with the Active Record pattern in React requires careful handling. React’s rendering cycle is built around the premise of immutability; it typically relies on immutable state management to trigger re-renders. When mutable objects change state outside the scope of React's `useState` or `useReducer`, React does not automatically know to re-render the affected components. This can lead to issues where the UI does not reflect the current application state. These challenges can be mitigated by integrating state management tools that are designed to work well with mutable objects, such as MobX. MobX provides mechanisms to track changes in data and automatically re-render components when mutations occur. This aligns more naturally with the Active Record pattern within the context of React, ensuring that the UI stays in sync with the underlying data. #### Using EntityBase and IdEntity In practice, leveraging the Active Record pattern often involves inheriting from classes such as `EntityBase` or `IdEntity` . These base classes enrich models with methods that simplify manipulations of their attributes and their persistence in the database. **Explanation:** The `Person` class represents individuals in the 'people' table and inherits from `IdEntity`. This inheritance means that there is no need to explicitly define an `id` field for this class, as `IdEntity` automatically includes a UUID field . Consequently, `Person` benefits from all the functionalities of `EntityBase`, which include tracking changes and handling CRUD operations, while also automatically gaining a UUID as the identifier. ### Mutable vs EntityBase **Traditional approach without Active Record:** **Using Active Record with EntityBase:** This pattern also simplifies other operations: #### Helper Members in EntityBase EntityBase provides additional utility members like `_` and `$` to facilitate more complex interactions: - **`_` :** Allows performing operations on a specific instance of an entity. - **`$` :** Provides access to detailed information about each field in the current instance, such as their original and current values: ### Alternative Implementations Even without direct inheritance from `EntityBase`, similar functionalities can be achieved using helper functions such as `getEntityRef`, which encapsulates an entity instance for manipulation and persistence: ### Conclusion The Active Record pattern offers a straightforward and intuitive approach to interacting with database records through object-oriented models. It is particularly beneficial in environments where business logic needs to be tightly coupled with data manipulation, providing a clear and efficient way to handle data state changes. However, integrating the Active Record pattern with mutable objects in React can be challenging. --- outline: --- # Add Remult to your App ::: tip New Project? We suggest using one of the to have a fresh good start. ::: ::: tip Add to your existing project? You are at the right place, Remult is designed to be added to existing projects. Embark on the Remult journey at your own pace and start reaping the benefits from day one. Gradual adoption is the preferred route for many, allowing for a smooth integration into your workflow. ::: ## Installation **The _remult_ package is one and the same for both the frontend bundle and the backend server.** If you're using one `package.json` for both frontend and backend - **install Remult once** in the project's root folder. If you're using multiple `package.json` files - **install Remult in both server and client folders**. ## Server-side Initialization Remult is initialized on the server-side as a request handling middleware, with **a single line of code**. Here is the code for setting up the Remult middleware: ### Express ### Fastify ### Next.js Pages Router ### Next.js App Router ### Sveltekit ### Hapi ### Nest ### Koa ## Client-side Initialization On the client side, `remult` can use any standard javascript HTTP-client to call the data API. **By default, remult uses the browser's `fetch` API, and makes data API calls using the base URL `/api` .** Here is the code for setting up a Remult client instance: ### Using Fetch ### Using Axios ### Using Angular HttpClient ### Changing the default API base URL By default, remult makes data API calls to routes based at the `/api` route of the origin of the client-side app. To use a different base URL for API calls , set the remult object's `apiClient.url` property. ::: warning CORS Handling is outside the scope of Remult. ::: ## Database Initialization Got a database ready? Fantastic! Unleash the full potential of your existing setup by generating your entities directly from the database itself. Check out and see how it can one shot generate your entities in no time. --- llm: "Expose entities as a GraphQL schema via remultGraphql + graphql-yoga ." --- # Adding Graphql To add graphql to a `remult` application follow these steps: 1. Install the `graphql-yoga` packages: ## Express: In the `/src/server/index.ts` file add the following code: ## Next App Router ## Svelte `src/routes/api/graphql/+server.ts` --- llm: "Generate OpenAPI doc from entities and serve a Swagger UI for the REST API." --- # Adding Swagger and openApi In short, swagger provides a quick UI that describes the api which is exposed by the application. To add swagger to a `remult` application follow these steps: 1. Install the `swagger-ui-express` package: 2. In the `/src/server/index.ts` file add the following code: ## Adding Swagger UI to a NextJs App To add swagger UI to a `NextJs` application follow these steps: 1. Install the following packages: 2. Get the openApi document from remultApiServer: 3. Create a new page to render Swagger UI: 4. Navigate to `http://localhost:3000/api-doc` to see the Swagger UI. ! ## Adding Scalar UI to SvelteKit 1. Install to get a modern OpenAPI UI with a built-in interactive playground: 2. Export OpenAPI document schema from your Remult API in `src/server/api.ts` file: 3. Add a SvelteKit server route in `src/routes/api//openapi.json/+server.ts` to handle OpenAPI json file: 4. And finally create an endpoint for Scalar OpenAPI at `src/routes/api//docs/+server.ts` In this case the UI will be available at # Adding open api specific field options Checkout the following example project that demos how to add `openApi` specific options to field options --- llm: "Built-in /api/admin CRUD UI with entity diagram, gated by allow and configurable via the admin option." --- # Admin UI Enjoy a fully featured Admin UI for your entities, you can do CRUD operations on your entities, view their relationships via the Diagram entry, and ensure secure management with the same validations and authorizations as your application. ## Enabling the Admin UI Add the Admin UI to your application by setting the `admin` option to `true` in the remult configuration. ### Tunning the Admin UI You can pass some options to admin as well: - `allow`, using . `true`, `"admin"`, ... - `customHtmlHead`, to add custom html to the head of the admin page. It's a function that receives remult as an argument. - `requireAuthToken`, this will open settings dialog to set the bearer token directly. - `disableLiveQuery`, this will disable live query for the admin ui. Example: ## Accessing and Using the Admin UI Navigate to `/api/admin` to access the Admin UI. Here, you can perform CRUD operations on your entities, view their relationships via the Diagram entry, and ensure secure management with the same validations and authorizations as your application. ! ## Features - **Entity List**: On the left side of the screen you have the entity list, you can use the search field to search for entities. - **Entity Details**: Clicking on an entity in the menu will open the entity details screen , here you can view filter & paginate your data __. You can also see all relations of entity by clicking on the arrow on the left of each row. The last column is dedicated for actions where you can edit or delete an entity. On top left you can also add a new entity by clicking on the `+`. - **Entity Diagram**: Clicking on the Diagram entry will open the entity diagram screen, here you can see the entity relationships. ! - **Settings**: On top left, you have a menu __ where you can find various settings for your admin ui. - You want to confirm a delete all the time? - You want to display Labels or Keys? - Multiple options for automatic diagram layout - You don't use cookies? No problem, you can set your bearer token ## Demo in video Watch this quick demo to see the Remult Admin UI in action: This video showcases the key features and functionality of the Remult Admin UI, giving you a practical overview of how it can streamline your entity management process. --- llm: "The Allowed type - boolean, role, role array, or predicate - used by allowApi* and BackendMethod permissions." --- # Allowed Throughout the api you'll see methods that use the `Allowed` data type, for example `allowApiRead` etc... The `Allowed` data type can be set to one of the following value: - true/false - a Role - Checks if the current user has this role. or with a constant - An Array of Roles - checks if the current user has at least one of the roles in the array - A function that get's a `remult` object as a parameter and returns true or false or: # AllowedForInstance In some cases, the allowed can be evaluated with regards to a specific instance, for example `allowApiUpdate` can consider specific row values. The Allowed for Instance method accepts two parameters: 1. The relevant `remult` object 2. The relevant entity instance For Example: --- llm: "@BackendMethod - typed RPC calls that auto-expose REST endpoints and run server-only logic." --- # Backend Methods Backend methods run on the backend and are used to improve performance, execute server-only code , or perform operations not accessible through the API. ## Static Backend Methods Static backend methods represent the most straightforward type, transmitting their parameters to the backend and delivering their outcome to the frontend. 1. **Define the Backend Method:** Each controller can house one or more backend methods, each serving distinct purposes tailored to your application's needs. In the provided example, the `TasksController` class contains a single backend method named `setAll`, responsible for setting the completion status of all tasks. The method name, such as `setAll`, serves as the URL for the corresponding REST endpoint on the backend server. It's worth noting that, by default, the endpoints are not prefixed. This means that if you define the `setAll` method in `TasksController`, and `setAll` in a separate controller, you will have potentially unwanted behaviour - two handlers for one endpoint. You can fix this by configuring a prefix for these endpoints using the `apiPrefix` option, providing flexibility in structuring your backend API routes. The allowed: true parameter signifies that the backend method can be invoked by anyone. Alternatively, you can customize the authorization settings for finer control over who can access the method. For instance, setting allow: Allow.authenticated restricts access to authenticated users only, ensuring that only logged-in users can utilize the method. Similarly, specifying allow: 'admin' limits access to users with administrative privileges, granting access exclusively to administrators. These options offer granular control over authorization, allowing you to tailor access permissions based on your application's specific requirements and security considerations. 2. **Register the Controller:** 3. **Call from the Frontend:** This example demonstrates how to define and use a static backend method, `setAll`, within the `TasksController` class. When called from the frontend, this method sets the completion status of all tasks to the specified value . The method leverages Remult's `BackendMethod` decorator to handle the communication between the frontend and backend seamlessly. --- llm: "Scaffold a new project with npm init remult@latest - 180+ framework/server/DB/auth combinations." --- # Creating a Remult Project _The easiest way to start building a Remult app_ Yes, that's it! ::: tip Let us know how you liked the process! ::: ## Demo ! ## What you get ? ### 1. **Tailored Setup** Answer a few questions about your preferred tech stack and project requirements. `Project name`: The name of your project __ `Choose your Framework` `Choose your Web Server` __ `Choose your Database` `Authentication`: Do you want to add `auth.js` to your project directly ? including a complete implementation for `credentials` and `github` providers `Add CRUD demo`: A comprehensive example of how to use an entity. It will show you how to create, read, update and delete data. `Admin UI`: Will then be available at `/api/admin` ### 2. **Instant Configuration** Based on your answers, Remult will configure the project with the best-suited options. With all combinations of frameworks, servers, databases and authentication, we manage more than `180 different project flavors`! We are missing yours? Let us know ! ### 3. **Feature-Rich Demo** Once you run your project, you'll be greeted with a comprehensive dashboard that showcases all of Remult's powerful features. It will look like this: ! Each tile is a fully functional example of a feature that you selected. ### 4. **Easy Eject** Simply remove the demo folder to eject the demo components. # CRUD your first Entity ## Define an Entity Model Class Remult entity classes are shared between frontend and backend code. Alternatively, . ## Register the Entity on the Server All Remult server middleware options contain an `entities` array. Use it to register your Entity. ## Query and Mutate data in Front-end code --- llm: "Filter.createCustom - reusable, parameterized server-side filters callable from the frontend." --- # Leveraging Custom Filters for Enhanced Data Filtering In modern web applications, efficiently filtering data is essential for providing a seamless user experience. Whether it's an e-commerce platform filtering products, a task management system sorting tasks, or any other application that requires data manipulation, the ability to apply complex filters is crucial. Custom filters offer a powerful solution, enabling developers to create reusable, declarative, and versatile filters that are executed on the backend and easily utilized from the frontend. This article delves into the concept of custom filters, illustrating their advantages and practical applications. ## The Advantages of Custom Filters Custom filters provide several benefits that make them an attractive choice for handling data filtering in web applications: 1. **Declarative and Readable:** Custom filters allow you to express filtering logic in a clear, declarative manner. This improves code readability and maintainability, making it easier to understand and modify filtering criteria. 2. **Reusability:** By encapsulating filtering logic in custom filters, you can reuse the same filters across different parts of your application, reducing code duplication and ensuring consistency in filtering behavior. 3. **Backend Execution:** Custom filters are evaluated on the backend, leveraging the full capabilities of the underlying database or data provider. This enables more efficient data processing and allows you to perform complex operations that would be difficult or impossible to handle on the frontend. 4. **Composability:** Custom filters can be combined with other filters, both custom and standard, allowing you to build complex filtering logic in a modular and maintainable way. 5. **Flexibility with Data Providers:** Custom filters can be used with various data providers, including SQL databases, in-memory JSON arrays, and others. This flexibility allows you to apply custom filters in different contexts and with different data storage solutions. 6. **Enhanced Security:** When using custom filters with parameterized queries or data provider-specific filtering methods, you can mitigate the risk of injection attacks and ensure that user input is properly sanitized. ## Practical Example: Filtering Orders in an E-Commerce Application Consider an e-commerce application where you need to filter orders based on their status and creation year. Without custom filters, the filtering logic might be repetitive and scattered throughout the codebase. By using custom filters, you can encapsulate this logic in a reusable component, simplifying the code and making it more maintainable. In the following sections, we'll explore how to implement custom filters in this scenario, demonstrating their advantages and how they can be used to create more efficient and readable code. ## The Problem with Repetitive Filtering Consider a scenario where you have an `Order` entity, and you frequently need to filter orders that are considered "active" based on their status and creation year. Without custom filters, your code might look something like this: This code is not only repetitive but also clutters your application, making it harder to maintain. Moreover, it generates lengthy REST API calls, such as: ## Introducing Custom Filters Custom filters allow you to refactor your filtering logic into a reusable and declarative component. Here's how you can define a custom filter for active orders: - **First Generic Parameter :** This parameter specifies the entity class that the filter is associated with. In this case, it's the `Order` class. This is important because it ensures that the filter criteria you define are compatible with the fields and types of the `Order` entity. - **Second Generic Parameter :** This parameter defines the type of the argument that the filter will receive when executed. In this example, the filter expects an object with a single property `year` of type `number`. This allows you to pass dynamic values to the filter when you use it in a query, making the filter more flexible and reusable. - **Callback Function => { ... }`):** This function is where you define the actual filtering criteria. It receives an argument matching the type specified in the second generic parameter. Inside the function, you return an object representing the filter conditions. In this case, the conditions are based on the `status` and `createdAt` fields of the `Order` entity. Now, you can use this custom filter in your queries: This generates a much simpler REST API call: ## Composability of Custom Filters One of the key advantages of custom filters is their ability to be composed with other filters. This means you can combine custom filters with regular filters or even other custom filters to build complex filtering logic. Let's take a closer look at the example you provided: In this query, we're filtering orders based on two criteria: 1. The `customerId` should be "123". 2. The order should satisfy the conditions defined in the `activeOrders` custom filter for the specified year. By using the `$and` operator, we're able to combine the custom filter with a regular filter. This demonstrates the composability of custom filters, allowing you to build more complex and nuanced filtering logic while maintaining readability and reusability. ### More on Composability The power of composability doesn't stop there. You can also combine multiple custom filters to create even more specific filters. For example, suppose you have another custom filter called `highValueOrders` that filters orders based on their total value: You can then combine this with the `activeOrders` filter to find high-value active orders for a specific year: This ability to compose filters allows you to create modular and reusable filtering logic, which can significantly improve the maintainability and clarity of your code. ### Evaluating Custom Filters on the Backend One of the significant advantages of custom filters is that they are evaluated on the backend. This allows you to perform complex data-related operations that would be inefficient or impossible to do solely on the frontend. For instance, you can leverage database queries or other server-side logic to build your filtering criteria. Let's examine the example you provided: In this example, the custom filter `activeOrders` now takes an additional parameter `customerCity`. The filter performs a database query to fetch all customers from the specified city. It then uses the IDs of these customers to filter orders that belong to them. This is combined with the existing criteria of filtering orders based on their status and creation year. ::: tip Key Points - **Backend Evaluation:** The filter is evaluated on the backend, where it has access to the database and can perform efficient queries. This offloads complex data processing from the frontend to the backend, where it can be handled more effectively. - **Complex Filtering:** By leveraging backend capabilities, you can create filters that involve complex operations, such as fetching related data from other tables or entities . - **Asynchronous Operations:** Notice the use of `async` in the filter definition. This allows you to perform asynchronous operations, such as database queries, within your custom filter. ::: ## Leveraging Database Capabilities with Raw SQL in Custom Filters Since custom filters are **evaluated on the backend**, you have the opportunity to harness the raw capabilities of the underlying database. This can be particularly useful when you need to perform complex operations that are more efficiently handled by the database itself. For instance, you can use raw SQL queries to improve the performance or functionality of your custom filters. Let's modify the `activeOrders` custom filter to use a raw SQL query for filtering orders based on the customer's city: In this example, we've added a `$and` condition that uses `SqlDatabase.rawFilter` to include a raw SQL fragment in our filter. This SQL fragment selects the IDs of customers from the specified city and uses them to filter the orders. This generates the following sql: #### Important Notes - **Parameterized Queries:** It's crucial to use parameterized queries `) when incorporating user-supplied values into your SQL queries. This helps prevent SQL injection attacks by ensuring that user input is properly escaped. - **Performance Considerations:** Leveraging raw SQL can lead to significant performance improvements, especially for complex queries. However, it's important to ensure that your SQL queries are well-optimized to avoid potential performance issues. #### Usage Example Using the custom filter remains straightforward: ### Using `dbNamesOf` with Table Names and Aliases The `dbNamesOf` utility function can be customized to include the table name in the SQL queries. This is particularly useful for ensuring consistency between your entity definitions and your raw SQL queries. Here's an updated example of the `activeOrders` custom filter using `dbNamesOf` with table names and aliases: In this example: - The `Order` table is referenced with its full name. - The `Customer` table is aliased as `"c"`, and this alias is used in the SQL query. ### Explanation of `tableName` and Aliases - **`tableName: true`:** By setting `tableName: true`, you indicate that you want to include the table name when referring to fields, resulting in SQL expressions like `"customer"."id"`. - **Aliases:** You can use aliases for table names, which is particularly useful in complex join scenarios. For example, setting `tableName: "c"` would use the alias `"c"` for the table name in the SQL query. ### Resulting SQL Query Using the `activeOrders` custom filter with the enhancements mentioned above would generate the following SQL query: In this SQL query, the `Customer` table is aliased as `"c"`, and this alias is used throughout the query to ensure consistency with the entity definitions and to handle complex join scenarios effectively. ### SQL-Based Custom Filters: Unleashing the Power of Composability The greatest advantage of using SQL-based custom filters lies in their composability and the ability to handle complex situations. By breaking down filtering logic into smaller, atomic custom filters, developers can compose these filters to create more sophisticated and nuanced filtering criteria. This modular approach not only enhances the readability and maintainability of the code but also allows for greater flexibility in constructing complex queries. For instance, consider a scenario where you need to filter orders based on multiple criteria, such as status, creation year, customer location, and order value. By creating separate custom filters for each of these criteria, you can easily combine them to form a comprehensive filtering solution. This composability ensures that your filtering logic can adapt to various requirements without becoming convoluted or difficult to manage. Furthermore, the ability to handle complex situations is a significant advantage of SQL-based custom filters. By leveraging the raw power of SQL, you can perform advanced operations such as subqueries, joins, and aggregate functions directly within your filters. This opens up a wide range of possibilities for data analysis and manipulation, enabling you to tackle complex filtering scenarios with ease. SQL is a language that is widely recognized and understood by AI technologies such as ChatGPT, Copilot and others. This makes it possible to generate highly optimized queries with ease. These AI technologies can assist in writing SQL queries, ensuring they are efficient and effective. This is particularly beneficial when dealing with complex data structures and large datasets, where writing optimal queries can be challenging. With the assistance of AI, developers can focus more on the logic of their applications, while the AI handles the intricacies of SQL query optimization. In summary, the composability of SQL-based custom filters, coupled with their ability to handle complex situations, makes them an invaluable tool for developers seeking to create flexible, efficient, and powerful data filtering solutions in their web applications. ### Using Raw Filters with Different Data Providers Custom filters with raw filters are not limited to SQL databases. You can also use raw filters with other data providers, such as Knex or an in-memory JSON data provider. This flexibility allows you to leverage the power of raw filters in various contexts, depending on your application's needs. #### Knex Example Knex is a popular SQL query builder for Node.js. You can use Knex with custom filters to define complex filtering logic directly using the Knex query builder syntax. In this example, the `idBetween` custom filter uses Knex to filter `Task` entities whose `id` falls between the specified `from` and `to` values. #### JSON Example For applications that use an in-memory JSON data provider, you can define custom filters that operate directly on the JSON data. In this example, the `titleLengthFilter` custom filter filters `Task` entities based on the length of their `title` property, ensuring that it exceeds the specified `minLength`. ## Conclusion Custom filters represent a powerful tool in the arsenal of web developers, offering a flexible and efficient way to handle data filtering in web applications. By encapsulating filtering logic into reusable components, custom filters not only enhance code readability and maintainability but also enable the execution of complex filtering operations on the backend. This leads to improved performance and security, as well as the ability to compose intricate filtering criteria with ease. The versatility of custom filters extends to their compatibility with various data providers, from SQL databases to in-memory JSON arrays, allowing developers to leverage the most suitable data handling mechanisms for their specific use cases. Moreover, the declarative nature of custom filters ensures that the filtering logic remains clear and concise, facilitating easier debugging and future modifications. In conclusion, adopting custom filters in your web development projects can significantly streamline the process of data filtering, resulting in cleaner, more efficient, and more secure code. By embracing this approach, developers can focus on delivering a seamless user experience, confident in the knowledge that their data filtering logic is both robust and adaptable. --- tags: - options - bespoke options - customizing options - type augmentation - module augmentation - UserInfo - RemultContext - context llm: "Augment UserInfo, FieldOptions, EntityOptions, and RemultContext via TypeScript declaration merging." --- # Extensibility in TypeScript allows you to extend existing types with custom properties or methods. This enhances the functionality of third-party libraries like `remult` without altering their source code, enabling seamless integration of custom features while maintaining type safety. In Remult, you can use TypeScript's module augmentation to enhance your application with custom features. Here are some examples: 1. **Add more fields to the User object:** Extend the `UserInfo` interface to include additional fields like `email` and `phone`. 2. **Add custom options/metadata to fields and entities:** Extend the `FieldOptions` or `EntityOptions` interfaces to include custom properties such as `placeholderText` or `helpText`. 3. **Add fields/methods to the `remult.context` object:** Extend the `RemultContext` interface to include additional properties or methods that can be accessed throughout your code. ## Setting Up the types.d.ts File for Custom Type Extensions To set up the `types.d.ts` file for custom type extensions in Remult: 1. **Create a TypeScript Declaration File:** Add a file named `types.d.ts` in the `src` folder of your project. This file will be used to declare custom type extensions, such as additional user info fields. The `export {}` is required to indicate that this file is a module, as per the . 2. **Include the Declaration File in tsconfig:** Make sure that the `types.d.ts` file is included in the `include` section of your `tsconfig.json` file. If you have a separate `tsconfig` for the server, ensure that it's also added there. 3. **Utilize the Custom Fields in Your Code:** Once you've defined custom fields in the `types.d.ts` file and ensured they're included in your `tsconfig.json`, you can start using them throughout your application. For instance, if you've added `phone` and `email` to the `UserInfo` interface, you can access these properties in your code as follows: This enables you to seamlessly integrate the new fields into your application's logic and user interface. ## Enhancing Field and Entity Definitions with Custom Options One of the key motivations for adding custom options to `FieldOptions` or `EntityOptions` is to maintain consistency and centralize the definition of entities and fields in your application. By keeping these definitions close to the entity or field, you ensure a single source of truth for your application's data model. This approach enhances maintainability and readability, as all relevant information and metadata about an entity or field are located in one place. Additionally, it allows for easier integration with UI components, as custom options like `placeholderText` can be directly accessed and used in your frontend code. For adding custom options to `FieldOptions` or `EntityOptions`, such as `placeholderText`: 1. **Extend FieldOptions:** In your `types.d.ts` file, extend the `FieldOptions` interface to include your custom options. For example: 2. **Set Custom Option:** Specify the `placeholderText` in your entity field options: 3. **Use in UI:** Access the custom option in your UI components: By following these steps, you can extend `FieldOptions` with custom options that can be utilized throughout your project. ### Extending Remult's `context` Property for Request-Specific Information Augmenting Remult's `context` property is particularly useful because it allows you to store and access request-specific information throughout your code. This can be especially handy for including data from the request and utilizing it in entities or backend methods. For example, you can add a custom property `origin` to the `RemultContext` interface: Then, set the `origin` property in the `initRequest` option in the `api.ts` file: You can now use the `origin` property anywhere in your code, for example: or in an entity's saving event: By leveraging module augmentation, you can tailor Remult to your specific needs, adding custom options and extending interfaces to suit your application's requirements. --- llm: "Generate entity classes from an existing database schema using remult-kit." --- # Generate Entities from Existing Database ## Remult kit Want to use Remult for full-stack CRUD with your existing database? Check out this video to see how to connect http://remult.dev to your existing database and start building type-safe #fullstack apps with any #typescript frontend, backend, and any DB! Watch now πŸ‘‰ https://youtu.be/5QCzJEO-qQ0. --- llm: "The SSOT class - schema, REST API, validation, and permissions in one @Entity decorator." --- # Entities Entities are **THE** key concept of Remult - your **Single Source of Truth ** for data structure, api, validation, permissions, and more! In other languages, they can be called `schema` or `model`. --- # πŸ§™β€β™‚οΈ Remultor Real-time Remult entity builder. Try it out and copy the code to your project! --- llm: "@BackendMethod on entity instances - round-trips the full entity state to the server." --- # Entity Instance Backend Methods When leveraging the Active Record pattern, backend methods for entity instances offer a powerful way to integrate client-side behavior with server-side logic. These methods, when invoked, transport the entire entity's state from the client to the server and vice versa, even if the data has not yet been saved. This feature is particularly useful for executing entity-specific operations that require a round-trip to the server to maintain consistency and integrity. ## Overview of Entity Backend Methods Entity backend methods enable all the fields of an entity, including unsaved values, to be sent to and from the server during the method's execution. This approach is essential for operations that rely on the most current state of an entity, whether or not the changes have been persisted to the database. ### Defining a Backend Method To define a backend method, use the `@BackendMethod` decorator to annotate methods within an entity class. This decorator ensures that the method is executed on the server, taking advantage of server-side resources and permissions. Here is an example demonstrating how to define and use a backend method in an entity class: ### Calling the Backend Method from the Frontend Once the backend method is defined, it can be called from the client-side code. This process typically involves fetching an entity instance and then invoking the backend method as shown below: ### Security Considerations ::: danger It's important to note that backend methods bypass certain API restrictions that might be set on the entity, such as `allowApiUpdate=false`. This means that even if an entity is configured not to allow updates through standard API operations, it can still be modified through backend methods if they are permitted by their `allowed` setting. Consequently, developers must explicitly handle security and validation within these methods to prevent unauthorized actions. The principle here is that if a user has permission to execute the `BackendMethod`, then all operations within that method are considered authorized. It is up to the developer to implement any necessary restrictions within the method itself. ::: --- outline: llm: "@Relations.toOne / @Relations.toMany - define and navigate one-to-one, one-to-many, many-to-many associations." --- # Relations Between Entities ::: tip **Interactive Learning Available! πŸš€** Looking to get hands-on with this topic? Try out our new on Relations, where you can explore and practice directly in the browser. This guided experience offers step-by-step lessons to help you master relations in Remult with practical examples and exercises. ::: ### Understanding Entity Relations in Remult In Remult, entity relations play a useful role in modeling and navigating the complex relationships that exist within your data. To illustrate this concept, we will use two primary entities: `Customer` and `Order`. These entities will serve as the foundation for discussing various types of relations and how to define and work with them . To experiment with these entities online, you can access the following CodeSandbox link, which is preconfigured with these two entities and a postgres database: Feel free to explore and experiment with the provided entities and their relations in the CodeSandbox environment. #### Customer Entity The `Customer` entity represents individuals or organizations with attributes such as an ID, name, and city. Each customer can be uniquely identified by their `id`. #### Order Entity The `Order` entity represents transactions or purchases made by customers. Each order is associated with a `customer`, representing the customer who placed the order, and has an `amount` attribute indicating the total purchase amount. Throughout the following discussion, we will explore how to define and use relations between these entities, enabling you to create sophisticated data models and efficiently query and manipulate data using Remult. Whether you are dealing with one-to-one, one-to-many, or many-to-many relationships, understanding entity relations is essential for building robust and feature-rich applications with Remult. ## Simple Many-to-One In Remult, many-to-one relations allow you to establish connections between entities, where multiple records of one entity are associated with a single record in another entity. Let's delve into a common use case of a many-to-one relation, specifically the relationship between the `Order` and `Customer` entities. ### Defining the Relation To establish a many-to-one relation from the `Order` entity to the `Customer` entity, you can use the `@Relations.toOne` decorator in your entity definition: In this example, each `Order` is associated with a single `Customer`. The `customer` property in the `Order` entity represents this relationship. ### Fetching Relational Data When querying data that involves a many-to-one relation, you can use the `include` option to specify which related entity you want to include in the result set. In this case, we want to include the associated `Customer` when querying `Order` records. Here's how you can include the relation in a query using Remult: #### Resulting Data Structure The result of the query will contain the related `Customer` information within each `Order` record, creating a nested structure. Here's an example result of running `JSON.stringify` on the `orders` array: As shown in the result, each `Order` object contains a nested `customer` object, which holds the details of the associated customer, including their `id`, `name`, and `city`. This structured data allows you to work seamlessly with the many-to-one relationship between `Order` and `Customer` entities . ### Querying a Single Item To retrieve a single `Order` item along with its associated `Customer`, you can use the `findFirst` method provided by your repository . Here's an example of how to perform this query: ### Relation Loading In Remult, by default, a relation is not loaded unless explicitly specified in the `include` statement of a query. This behavior ensures that you only load the related data you require for a specific task, optimizing performance and minimizing unnecessary data retrieval. Here's an example: In the above query, the `customer` relation will not be loaded and have the value of `undefined` because it is not specified in the `include` statement. #### Overriding Default Behavior with `defaultIncluded` Sometimes, you may have scenarios where you want a relation to be included by default in most queries, but you also want the flexibility to exclude it in specific cases. Remult allows you to control this behavior by using the `defaultIncluded` setting in the relation definition. In this example, we set `defaultIncluded` to `true` for the `customer` relation in the `Order` entity. This means that, by default, the `customer` relation will be loaded in most queries unless explicitly excluded. #### Example: Excluding `customer` Relation in a Specific Query In this query, we override the default behavior by explicitly setting `customer: false` in the `include` statement. This instructs Remult not to load the `customer` relation for this specific query, even though it is set to be included by default. By combining the default behavior with the ability to override it in specific queries, Remult provides you with fine-grained control over relation loading, ensuring that you can optimize data retrieval based on your application's requirements and performance considerations. ## Advanced Many-to-One In certain scenarios, you may require more granular control over the behavior of relations and want to access specific related data without loading the entire related entity. Remult provides advanced configuration options to meet these requirements. Let's explore how to achieve this level of control through advanced relation configurations. ### Custom Relation Field In Remult, you can define custom relation fields that allow you to access the `id` without loading the entire related entity. To define a custom relation field, follow these steps: #### Step 1: Define a Custom Field in the Entity In your entity definition, define a custom field that will hold the identifier or key of the related entity. This field serves as a reference to the related entity without loading the entity itself. In this example, we define a custom field called `customerId`, which stores the identifier of the related `Customer` entity. #### Step 2: Define the Relation Using `toOne` Use the `@Relations.toOne` decorator to define the relation, specifying the types for the `fromEntity` and `toEntity` in the generic parameters. Additionally, provide the name of the custom field as the third argument. This configuration establishes a relation between `Order` and `Customer` using the `customerId` field as the reference. #### Migrating from a Simple `toOne` Relation to a Custom Field Relation with Existing Data When transitioning from a simple `toOne` relation to a custom field relation in Remult and you already have existing data, it's important to ensure a smooth migration. In this scenario, you need to make sure that the newly introduced custom field can access the existing data in your database. This is accomplished using the `dbName` option. Here's how to perform this migration: ##### 1. Understand the Existing Data Structure Before making any changes, it's crucial to understand the structure of your existing data. In the case of a simple `toOne` relation, there may be rows in your database where a field holds the identifier of the related entity. ##### 2. Define the Custom Field with `dbName` When defining the custom field in your entity, use the `dbName` option to specify the name of the database column where the related entity's identifier is stored. This ensures that the custom field correctly accesses the existing data in your database. In this example, we use the `dbName` option to specify that the `customerId` field corresponds to the `customer` column in the database. This mapping ensures that the custom field can access the existing data that uses the `customer` column for the related entity's identifier. #### Using the `field` Option for Custom Relation Configuration When you require additional customization for a relation field in Remult, you can utilize the field option to specify additional options for the related field. In this example, we use the `field` option to define a custom relation between the `Order` and `Customer` entities. Here are some key points to understand about using the `field` option: 1. **Custom Relation Field**: The `field` option allows you to specify a custom field name that represents the relationship between entities. This field can be used to access related data without loading the entire related entity. 2. **Additional Configuration**: In addition to specifying the `field`, you can include other options as well. In this example, we set the `label` option to provide a descriptive label for the relation field. Using the `field` option provides you with granular control over how the relation field is configured and accessed . You can customize various aspects of the relation to meet your specific requirements, enhance documentation, and improve the overall usability of your codebase. ### Relation Based on Multiple Fields In some scenarios, establishing a relation between entities requires considering multiple fields to ensure the correct association. Remult provides the flexibility to define relations based on multiple fields using the `fields` option. Here's how to create a relation based on multiple fields in Remult: #### Defining Entities Let's consider a scenario where both `Order` and `Customer` entities belong to specific branches, and we need also the `branchId` fields to ensure the correct association. First, define your entities with the relevant fields: In this example, we have two entities: `Customer` and `Order`. Both entities have a `branchId` field that represents the branch they belong to. To create a relation based on these fields, we specify the `fields` option in the relation configuration. #### Using the `fields` Option In the `@Relations.toOne` decorator, use the `fields` option to specify the mapping between fields in the related entity and your entity . Each entry in the `fields` object corresponds to a field in the related entity and maps it to a field in your entity. In this configuration: - `branchId` from the `Customer` entity is mapped to `branchId` in the `Order` entity. - `id` from the `Order` entity is mapped to `customerId` in the `Customer` entity. This ensures that the relation between `Order` and `Customer` is based on both the `branchId` and `customerId` fields, providing a comprehensive association between the entities. By utilizing the `fields` option, you can create relations that consider multiple fields, ensuring accurate and meaningful associations between your entities in Remult. ## One-to-Many In Remult, you can easily define a `toMany` relation to retrieve multiple related records. Let's consider a scenario where you want to retrieve a list of orders for each customer. We'll start with the basic `toOne` relation example and then add a `toMany` relation to achieve this: #### Basic `toOne` Relation Example First, let's define the `Customer` and `Order` entities with a basic `toOne` relation: In this initial setup: - The `Order` entity has a property `customer`, which is decorated with `@Relations.toOne => Customer)`. This establishes a relation between an order and its associated customer. ### Adding a `toMany` Relation Now, let's enhance this setup to include a `toMany` relation that allows you to retrieve a customer's orders: In this updated configuration: - The `Customer` entity has a property `orders`, which is decorated with `@Relations.toMany => Order)`. This indicates that a customer can have multiple orders. With this setup, you can use the `orders` property of a `Customer` entity to retrieve all the orders associated with that customer. This provides a convenient way to access and work with a customer's orders. By defining a `toMany` relation, you can easily retrieve and manage multiple related records, such as a customer's orders. ### Fetching Relational Data To retrieve customers along with their associated order in Remult, you can use the `include` option in your query. Let's see how to fetch customers with their orders using the `include` option: In this code snippet: - We first obtain a repository for the `Customer` entity using `repo`. - Next, we use the `find` method to query the `Customer` entity. Within the query options, we specify the `include` option to indicate that we want to include related records. - Inside the `include` option, we specify `orders: true`, indicating that we want to fetch the associated orders for each customer. As a result, the `customers` variable will contain an array of customer records, with each customer's associated orders included. This allows you to easily access and work with both customer and order data. #### Resulting Data Structure When you fetch customers along with their associated orders using the `include` option in Remult, the result will be an array that includes both customer and order data. Here's an example result of running `JSON.stringify` on the `customers` array: In this example: - Each customer is represented as an object with properties such as `id`, `name`, and `city`. - The `orders` property within each customer object contains an array of associated order records. - Each order record within the `orders` array includes properties like `id` and `amount`. This structured result allows you to easily navigate and manipulate the data . You can access customer information as well as the details of their associated orders, making it convenient to work with related records in your application's logic and UI. ### Specifying Reference Fields In Remult, you can specify a field or fields for `toMany` relations to have more control over how related records are retrieved. This can be useful when you want to customize the behavior of the relation. Here's how you can specify a field or fields for `toMany` relations: #### Specifying a Single Field To specify a single field for a `toMany` relation, you can use the `field` option. This option allows you to define the field in your entity that establishes the relation. For example: In this case, the `field` option is set to `"customer"`, indicating that the `customer` field in the `Order` entity establishes the relation between customers and their orders. #### Specifying Multiple Fields In some cases, you may need to specify multiple fields to establish a `toMany` relation. To do this, you can use the `fields` option, which allows you to define a mapping of fields between entities. Here's an example: In this example, the `fields` option is used to specify that the `branchId` field in the `Order` entity corresponds to the `branchId` field in the `Customer` entity, and the `customerId` field in the `Order` entity corresponds to the `id` field in the `Customer` entity. By specifying fields in this manner, you have fine-grained control over how the relation is established and how related records are retrieved. This allows you to tailor the behavior of `toMany` relations to your specific use case and data model. ### Customizing a `toMany` Relation In Remult, you can exercise precise control over a `toMany` relation by utilizing the `findOptions` option. This option allows you to define specific criteria and behaviors for retrieving related records. Here's how you can use `findOptions` to fine-tune a `toMany` relation: In this example, we've specified the following `findOptions`: - `limit: 5`: Limits the number of related records to 5. Only the first 5 related records will be included. - `orderBy: { amount: "desc" }`: Orders the related records by the `amount` field in descending order. This means that records with higher `amount` values will appear first in the result. - `where: { amount: { $gt: 10 } }`: Applies a filter to include only related records where the `amount` is greater than 10. This filters out records with an `amount` of 10 or lower. By using `findOptions` in this manner, you gain precise control over how related records are retrieved and included in your query results. This flexibility allows you to tailor the behavior of the `toMany` relation to suit your specific application requirements and use cases. #### Fine-Tuning a `toMany` Relation with `include` In Remult, you can exercise even more control over a `toMany` relation by using the `include` option within your queries. This option allows you to further customize the behavior of the relation for a specific query. Here's how you can use `include` to fine-tune a `toMany` relation: In this code snippet: - We use the `include` option within our query to specify that we want to include the related `orders` for each customer. - Inside the `include` block, we can provide additional options to control the behavior of this specific inclusion. For example: - `limit: 10` limits the number of related orders to 10 per customer. This will override the `limit` set in the original relation. - `where: { completed: true }` filters the included orders to only include those that have been marked as completed. The `where` option specified within `include` will be combined with the `where` conditions defined in the `findOptions` of the relation using an "and" relationship. This means that both sets of conditions must be satisfied for related records to be included. Using `include` in this way allows you to fine-tune the behavior of your `toMany` relation to meet the specific requirements of each query, making Remult a powerful tool for building flexible and customized data retrieval logic in your application. ## Repository `relations` In Remult, managing relationships between entities is a crucial aspect of working with your data. When dealing with a `toMany` relationship, Remult provides you with powerful tools through the repository's `relations` property to handle related rows efficiently, whether you want to retrieve them or insert new related records. ### Inserting Related Records Consider a scenario where you have a `Customer` entity with a `toMany` relationship to `Order` entities. You can create a new customer and insert related orders in a straightforward manner: In this example, you first create a new `Customer` entity with the name "Abshire Inc." Then, using the `relations` method, you access the related `orders`. By calling the `insert` method on the `orders` relation, you can add new order records. Remult automatically sets the `customer` field for these orders based on the specific customer associated with the `relations` call. ### Loading Unfetched Relations Another powerful use of the `repository` methods is to load related records that were not initially retrieved. Let's say you have found a specific customer and want to access their related orders: Here, you first search for a customer with the name "Abshire Inc." After locating the customer, you can use the `relations` method again to access their related orders. By calling the `find` method on the `orders` relation, you retrieve all related order records associated with the customer. #### Contextual Repository: Tailored Operations for Related Data The `relations` method serves as a specialized repository, tightly associated with the particular customer you supply to it. This dedicated repository offers a tailored context for performing operations related to the specific customer's connection to orders. It enables you to seamlessly find related records, insert new ones, calculate counts, and perform other relevant actions within the precise scope of that customer's relationship with orders. This versatile capability streamlines the management of intricate relationships in your application, ensuring your data interactions remain organized and efficient. Remult's repository methods empower you to seamlessly manage and interact with related data, making it easier to work with complex data structures and relationships in your applications. Whether you need to insert related records or load unfetched relations, these tools provide the flexibility and control you need to handle your data efficiently. --- ### Fetching Unloaded `toOne` Relations with `findOne` In addition to loading unfetched `toMany` relations, Remult offers a convenient way to retrieve `toOne` relations that were not initially loaded. This capability is especially useful when dealing with many-to-one relationships. Consider the following example, where we have a many-to-one relation between orders and customers. We want to fetch the customer related to a specific order, even if we didn't load it initially: In this code snippet: 1. We first obtain the order using the `findFirst` function, providing the order's unique identifier. 2. Next, we use the `relations` method to access the repository's relations and then chain the `customer` relation using dot notation. 3. Finally, we call `findOne` on the `customer` relation to efficiently retrieve the related customer information. This approach allows you to access and load related data on-demand, providing flexibility and control over your data retrieval process. Whether you're working with loaded or unloaded relations, Remult's intuitive functions give you the power to seamlessly access the data you need. --- You can seamlessly incorporate this extension into the "Loading Unfetched Relations" section of your documentation to provide a comprehensive overview of working with both `toMany` and `toOne` relations. --- ### Accessing Relations with `activeRecord` If you're following the `activeRecord` pattern and your entity inherits from `EntityBase` or `IdEntity`, you can access relations directly from the entity instance. This approach offers a convenient and straightforward way to work with relations. #### Inserting Related Records You can insert related records directly from the entity instance. For example, consider a scenario where you have a `Customer` entity and a `toMany` relation with `Order` entities. Here's how you can insert related orders for a specific customer: In this code: - We create a new `Customer` instance using `customerRepo.insert` and set its properties. - Using `customer._.relations.orders`, we access the `orders` relation of the customer. - We insert two orders related to the customer by calling `.insert` on the `orders` relation. #### Retrieving Related Records Fetching related records is just as straightforward. Let's say you want to find a customer by name and then retrieve their related orders: In this code: - We search for a customer with the specified name using `customerRepo.findFirst`. - Once we have the customer instance, we access their `orders` relation with `customer._.relations.orders`. - We use `.find` to retrieve all related orders associated with the customer. Using the `activeRecord` pattern and direct access to relations simplifies the management of related data, making it more intuitive and efficient. ## Many-to-Many In Remult, you can effectively handle many-to-many relationships between entities by using an intermediate table. This approach is especially useful when you need to associate multiple instances of one entity with multiple instances of another entity. In this section, we'll walk through the process of defining and working with many-to-many relationships using this intermediate table concept. #### Entity Definitions: To illustrate this concept, let's consider two entities: `Customer` and `Tag`. In this scenario, multiple customers can be associated with multiple tags. ### Intermediate Table To establish this relationship, we'll create an intermediate table called `tagsToCustomers`. In this table, both `customerId` and `tagId` fields are combined as the primary key. - To uniquely identify associations between customers and tags in a many-to-many relationship, we use the combined `customerId` and `tagId` fields as the primary key, specified using the 'id' option in the `@Entity` decorator. - In this scenario, we've defined a `toOne` relation to the `Tag` entity within the `TagsToCustomers` entity to efficiently retrieve tags associated with a specific customer. This approach simplifies the management of many-to-many relationships while ensuring unique identification of each association. Now, let's enhance our customer entity with a toMany relationship, enabling us to fetch all of its associated tags effortlessly. ### Working with Many-to-Many Relationships Let's explore how to interact with many-to-many relationships using an intermediate table in Remult. #### 1. Adding Tags to a Customer: To associate a tag with a customer, consider the follow code: Here's an explanation of what's happening in this code: 1. We first insert some tags into the "tags" entity. 2. We then create a repository instance for the "customer" entity using `repo`. 3. We retrieve a specific customer by searching for one with the name "Abshire Inc" using `customerRepo.findFirst`. The `customer` variable now holds the customer entity. 4. To associate tags with the customer, we use the `relations` method provided by the repository. This method allows us to work with the customer's related entities, in this case, the "tags" relation to the TagsToCustomers entity. 5. Finally, we call the `insert` method on the "tags" relationship and provide an array of tag objects to insert. In this example, we associate the customer with the "vip" tag and the "influencer" tag by specifying the tags' indices in the `tags` array. **2. Retrieving Tags for a Customer:** To fetch the tags associated with a specific customer: In this code, we're querying the "customer" entity to find a customer named "Abshire Inc." We're also including the related "tags" for that customer, along with the details of each tag. This allows us to fetch both customer and tag data in a single query, making it more efficient when working with related entities. ### Resulting Data Structure Here's an example result of running `JSON.stringify` on the `customer` object: Utilizing an intermediate table for managing many-to-many relationships in Remult allows for a flexible and efficient approach to handle complex data associations. Whether you are connecting customers with tags or other entities, this method provides a powerful way to maintain data integrity and perform queries effectively within your application. --- In this guide, we've explored the essential concepts of managing entity relations within the Remult library. From one-to-one to many-to-many relationships, we've covered the declaration, customization, and querying of these relations. By understanding the nuances of entity relations, users can harness the full potential of Remult to build robust TypeScript applications with ease. --- tags: - Where - Filter - Entity Where - Entity Filter llm: "EntityFilter operators - $in, $ne, $gt/$gte/$lt/$lte, contains, $and/$or, $not - for where clauses." --- # EntityFilter Used to filter the desired result set ### Basic example ( this will include only items where the status is equal to 1. ### In Statement ### Not Equal ### Not in ### Comparison operators ### Contains ### Not Contains ### Starts With ### Ends With ### Id Equal ### Multiple conditions has an `and` relationship ### $and ### $or ### $not --- llm: "Gallery of example apps - TodoMVC, CRM, shadcn/TanStack tables, ready-to-play sandboxes." --- # Example Apps We have already a _ton_ of examples! Pick and choose the one that fits your needs 😊 ## Todo MVC ## CRM Demo A fully featured CRM! Make sure to check out the link: Dev / Admin on top right! ## Shadcn React Table Using remult with server side sorting, filtering, paging & CRUD ## TanStack React Table Example of using remult with react table - most basic design, with server side sorting, paging & filtering ## πŸš€ Ready to play An environment to reproduce issues using stackblitz, with optional sqlite database ## Group by Example And example of the usage of groupBy ## Todo for most frameworks - - - - - - - - - - ## Other example - - - - - --- llm: "Built-in @Fields decorators - string, number, integer, boolean, date, dateOnly, json, object, literal." --- # Field Types ## Common field types There are also several built in Field decorators for common use case: ### @Fields.string A field of type string ### @Fields.number Just like TypeScript, by default any number is a decimal . ### @Fields.integer For cases where you don't want to have decimal values, you can use the `@Fields.integer` decorator ### @Fields.boolean ### @Fields.date ### @Fields.dateOnly Just like TypeScript, by default any `Date` field includes the time as well. For cases where you only want a date, and don't want to meddle with time and time zone issues, use the `@Fields.dateOnly` ### @Fields.createdAt Automatically set on the backend on insert, and can't be set through the API ### @Fields.updatedAt Automatically set on the backend on update, and can't be set through the API ## JSON Field You can store JSON data and arrays in fields. ## Auto Generated Id Field Types ### @Fields.id This id value is determined on the backend on insert, and can't be updated through the API. By default it uses `crypto.randomUUID` to generate the id. You can change the algorithm used to generate the id by setting the `Fields.defaultIdFactory` to a different function like: You can also pass an id factory as an option to the `@Fields.id` to have a different value locally. So, you can select the algorithm you prefer: - `cuid`: `import { createId } from '@paralleldrive/cuid2'` - `uuid`: `import { v4 as uuid } from 'uuid'` - `nanoid`: `import { nanoid } from 'nanoid'` - `ulid`: `import { ulid } from 'ulid'` - and any other function that returns a string! Let us know what's your favorite! ### @Fields.autoIncrement This id value is determined by the underlying database on insert, and can't be updated through the API. ### MongoDB ObjectId Field To indicate that a field is of type object id, change it's `fieldTypeInDb` to `dbid`. ## Enum Field Enum fields allow you to define a field that can only hold values from a specific enumeration. The `@Fields.enum` decorator is used to specify that a field is an enum type. When using the `@Fields.enum` decorator, an automatic validation is added that checks if the value is valid in the specified enum. In this example, the `priority` field is defined as an enum type using the `@Fields.enum` decorator. The `Priority` enum is passed as an argument to the decorator, ensuring that only valid `Priority` enum values can be assigned to the `priority` field. The `Validators.enum` validation is used and ensures that any value assigned to this field must be a member of the `Priority` enum, providing type safety and preventing invalid values. ::: tip Need extra properties on each value ? Use instead - it lets each value carry its own data and behavior. ::: ## Literal Fields Literal fields let you restrict a field to a specific set of string values using the `@Fields.literal` decorator. This is useful for fields with a finite set of possible values. In this example, we use the `as const` assertion to ensure that the array `` is treated as a readonly array, which allows TypeScript to infer the literal types 'open', 'closed', 'frozen', and 'in progress' for the elements of the array. This is important for the type safety of the `status` field. The `status` field is typed as `'open' | 'closed' | 'frozen' | 'in progress'`, which means it can only hold one of these string literals. The `@Fields.literal` decorator is used to specify that the `status` field can hold values from this set of strings, and it uses the `Validators.in` validator to ensure that the value of `status` matches one of the allowed values. For better reusability and maintainability, and to follow the DRY principle, it is recommended to refactor the literal type and the array of allowed values into separate declarations: In this refactored example, `statuses` is a readonly array of the allowed values, and `StatusType` is a type derived from the elements of `statuses`. The `@Fields.literal` decorator is then used with the `statuses` array, and the `status` field is typed as `StatusType`. This approach makes it easier to manage and update the allowed values for the `status` field, reducing duplication and making the code more robust and easier to maintain. ::: tip Need extra properties on each value ? Use instead - it lets each value carry its own data and behavior. ::: ## ValueListFieldType ### Overview The `ValueListFieldType` is useful in cases where simple enums and unions are not enough, such as when you want to have more properties for each value. For example, consider representing countries where you want to have a country code, description, currency, and international phone prefix. ### Defining a ValueListFieldType Using enums or union types for this purpose can be challenging. Instead, you can use the `ValueListFieldType`: ### Using in an Entity In your entity, you can define the field as follows: ### Accessing Properties The property called `id` will be stored in the database and used through the API, while in the code itself, you can use each property: Note: Only the `id` property is saved in the database and used in the API. Other properties, such as `label`, `currency`, and `phonePrefix`, are only accessible in the code and are not persisted in the database. ### Getting Optional Values To get the optional values for `Country`, you can use the `getValueList` function, which is useful for populating combo boxes: ### Special Properties: id and label The `id` and `label` properties are special in that the `id` will be used to save and load from the database, and the `label` will be used as the display value. ### Binding to a `` values and URL parameters are strings, but a ValueList field is a class instance. `ValueListInfo.get` bridges the two: - `toInput` -> the `id` as a string - `fromInput` -> the instance Given a `Status` with an extra `color`: Populate a `