Extensibility
Module Augmentation 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:
- Add more fields to the User object: Extend the
UserInfointerface to include additional fields likeemailandphone. - Add custom options/metadata to fields and entities: Extend the
FieldOptionsorEntityOptionsinterfaces to include custom properties such asplaceholderTextorhelpText. - Add fields/methods to the
remult.contextobject: Extend theRemultContextinterface 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:
Create a TypeScript Declaration File: Add a file named
types.d.tsin thesrcfolder of your project. This file will be used to declare custom type extensions, such as additional user info fields.ts// src/types.d.ts export {} declare module 'remult' { interface UserInfo { phone: string email: string } }The
export {}is required to indicate that this file is a module, as per the Vue.js documentation on augmenting global properties.Include the Declaration File in tsconfig: Make sure that the
types.d.tsfile is included in theincludesection of yourtsconfig.jsonfile. If you have a separatetsconfigfor the server, ensure that it's also added there.json// tsconfig.server.json { "compilerOptions": { //... }, "include": ["src/server/**/*", "src/shared/**/*", "src/types.d.ts"] }Utilize the Custom Fields in Your Code: Once you've defined custom fields in the
types.d.tsfile and ensured they're included in yourtsconfig.json, you can start using them throughout your application. For instance, if you've addedphoneandemailto theUserInfointerface, you can access these properties in your code as follows:ts// Accessing custom user info fields console.log(remult.user.phone) console.log(remult.user.email)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:
Extend FieldOptions: In your
types.d.tsfile, extend theFieldOptionsinterface to include your custom options. For example:tsdeclare module 'remult' { interface FieldOptions<entityType, valueType> { placeholderText?: string } } export {}Set Custom Option: Specify the
placeholderTextin your entity field options:tsimport { Entity, Fields } from 'remult' @Entity('tasks', { allowApiCrud: true }) export class Task { @Fields.id() id!: string @Fields.string({ placeholderText: 'Please enter a task title', }) title = '' @Fields.boolean() completed = false }Use in UI: Access the custom option in your UI components:
html<input placeholder="{taskRepo.fields.title.options.placeholderText}" />
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:
declare module 'remult' {
export interface RemultContext {
origin?: string
}
}Then, set the origin property in the initRequest option in the api.ts file:
export const api = remultApi({
initRequest: async (_, req) => {
remult.context.origin = req.headers.origin
},
entities: [Task],
//...
})You can now use the origin property anywhere in your code, for example:
@BackendMethod({ allowed: Roles.admin })
static async doSomethingImportant() {
console.log(remult.context.origin);
}or in an entity's saving event:
@Entity<Task>("tasks", {
saving: task => {
task.lastUpdateDate = new Date();
task.lastUpdateUser = remult.user?.name;
task.lastUpdateOrigin = remult.context.origin;
},
//...
});By leveraging module augmentation, you can tailor Remult to your specific needs, adding custom options and extending interfaces to suit your application's requirements.