@furystack/repository
Version:
Repository implementation for FuryStack
65 lines (46 loc) • 3.23 kB
Markdown
# repository
Repository implementation for FuryStack.
With a repository, you can implement entity-level business logic in an easy and structured way.
You can authorize, manipulate, and observe CRUD operations.
## Setting Up a Repository
You can set up a repository as follows:
```ts
import { Injector } from '@furystack/inject'
import { InMemoryStore, addStore } from '@furystack/core'
import { getRepository, getDataSetFor } from '@furystack/repository'
class MyModel {
declare id: number
declare value: string
}
const myInjector = new Injector()
addStore(myInjector, new InMemoryStore({ model: MyModel, primaryKey: 'id' }))
getRepository(myInjector).createDataSet(MyModel, 'id', {
onEntityAdded: ({ injector, entity }) => {
injector.logger.verbose({ message: `An entity was added with value '${entity.value}'` })
},
authorizeUpdate: async () => ({
isAllowed: false,
message: 'This is a read-only dataset. No update is allowed. :(',
}),
/** custom repository options */
})
```
In the example above, we've created a physical InMemory store for the model `MyModel`, and we've configured a repository with a DataSet.
It will log to a logger when an entity has been added, and it won't allow updates to entities.
### Working with the DataSet
A DataSet is similar to a physical store, but it can have custom event callbacks and authorization logic.
You can retrieve the dataset as follows:
```ts
const dataSet = getDataSetFor(myInjector, MyModel, 'id')
dataSet.add(myInjector, { id: 1, value: 'foo' }) // <-- this will log to a logger
dataSet.update(myInjector, 1, { id: 1, value: 'bar' }) // <--- this one will be rejected
```
### Events
Events are great for logging or monitoring DataSet changes or distributing changes to clients. They are simple optional callbacks – if defined, they will be called on a specific event. These events are `onEntityAdded`, `onEntityUpdated`, and `onEntityRemoved`
### Authorizing operations
**Authorizers** are similar callbacks but they have to return a promise with an `AuthorizationResult` object - you can allow or deny CRUD operations or add additional filters to collections with these Authorize callbacks. These are `authorizeAdd`, `authorizeUpdate`, `authorizeUpdateEntity` (this needs an additional reload of entity but can compare with the original one), `authorizeRemove`, `authorizeRemoveEntity` (also needs reloading), `authorizeGet`, `authorizeGetEntity` (also needs reloading),
### Modifiers and additional filters
There are some callbacks that modifies an entity before persisting (like `modifyOnAdd` or `modifyOnUpdate`). For example, you can fill createdByUser or lastModifiedByUser fields with these.
There is an additional property called `addFilter`, you can use that to add a pre-filter condition **before** a filter expression will be evaluated in the data store - ensuring e.g. that the user can _retrieve_ only suff from the physical store that she has enough permission.
### Getting the Context
All methods above has an _injector instance_ on the call parameter - you can use that injector to get service instances from the right caller context. It means that you can use e.g.: HttpUserContext to getting the current user.