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
UserInfo
interface to include additional fields likeemail
andphone
. - Add custom options/metadata to fields and entities: Extend the
FieldOptions
orEntityOptions
interfaces to include custom properties such asplaceholderText
orhelpText
. - Add fields/methods to the
remult.context
object: Extend theRemultContext
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:
Create a TypeScript Declaration File: Add a file named
types.d.ts
in thesrc
folder 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.ts
file is included in theinclude
section of yourtsconfig.json
file. If you have a separatetsconfig
for 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.ts
file and ensured they're included in yourtsconfig.json
, you can start using them throughout your application. For instance, if you've addedphone
andemail
to theUserInfo
interface, 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.ts
file, extend theFieldOptions
interface to include your custom options. For example:tsdeclare module 'remult' { interface FieldOptions<entityType, valueType> { placeholderText?: string } } export {}
Set Custom Option: Specify the
placeholderText
in your entity field options:tsimport { Entity, Fields } from 'remult' @Entity('tasks', { allowApiCrud: true }) export class Task { @Fields.uuid() 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 = remultExpress({
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.