Skip to content

API Reference ​

activityLog(options) ​

Express middleware factory. Logs each request automatically.

ts
import { activityLog } from '@actinode/express-activitylog'

app.use(activityLog(options))

Options ​

OptionTypeDefaultDescription
adapterActivityAdapter—Required. The database adapter to write logs to.
getUserId(req: Request) => string | undefinedundefinedExtract the authenticated user's ID from the request. Used as the causer.
getDescription(req: Request) => string`${req.method} ${req.path}`Build the log description from the request.
skip(req: Request) => booleanundefinedReturn true to skip logging for a given request.
maskMaskConfigundefinedMasking configuration applied to req.body. Default sensitive fields are always masked even without this option.

Adapter errors are swallowed silently — a logging failure never breaks the request.


activity(adapter) ​

Returns an ActivityLogger for manual, fluent log creation.

ts
import { activity } from '@actinode/express-activitylog'

await activity(adapter).log('something happened')

ActivityLogger methods ​

All methods (except log) return this for chaining.

.by(causer) ​

Set the causer — the entity that performed the action.

ts
.by(req.user)

causer must have id: string | number and constructor.name (any class instance satisfies this). The causer's class name is stored as causerType.

.on(subject) ​

Set the subject — the entity the action was performed on.

ts
.on(post)

Same shape requirement as .by(). The subject's class name is stored as subjectType.

.withProperties(properties) ​

Attach arbitrary metadata to the log entry.

ts
.withProperties({ ip: req.ip, plan: 'pro' })
ParamType
propertiesRecord<string, unknown>

.mask(fields) ​

Mask specific fields on this log entry. Applies a field-level denyList to the properties set via .withProperties(). Call it after .withProperties() and before .log().

ts
await activity(adapter)
  .by(user)
  .on(post)
  .withProperties({ title: 'Hello', secret: 'draft-key' })
  .mask(['secret'])
  .log('created post')
ParamTypeDescription
fieldsstring[]Fields to replace with the default replacement string (***masked***).

TIP

.mask() supports field-level denyList only. For allowList, per-model config, or a custom replacement, use the mask option on activityLog().

.log(description) ​

Persist the log entry. Must be called last. Returns Promise<void>.

ts
await activity(adapter)
  .by(user)
  .on(post)
  .withProperties({ reason: 'spam' })
  .log('deleted post')

prismaAdapter(prisma) ​

Creates an adapter backed by Prisma.

ts
import { prismaAdapter } from '@actinode/express-activitylog'

const adapter = prismaAdapter(prisma)
ParamTypeDescription
prismaPrismaClientLikeAny Prisma client instance with an activityLog model.

mongooseAdapter(mongoose) ​

Creates an adapter backed by Mongoose.

ts
import { mongooseAdapter } from '@actinode/express-activitylog'

const adapter = mongooseAdapter(mongoose)
ParamTypeDescription
mongooseMongooseLikeThe mongoose module (or a compatible object).

The adapter registers the ActivityLog model on first call. If the model is already registered it is reused.


Masking types ​

ts
interface MaskConfig {
  denyList?:    string[]                        // mask these fields
  allowList?:   string[]                        // keep only these, mask everything else
  replacement?: string                          // default: '***masked***'
  deep?:        boolean                         // recurse into nested objects/arrays, default: true
  models?:      Record<string, ModelMaskConfig> // per-model overrides keyed by class name
}

interface ModelMaskConfig {
  denyList?:  string[] // overrides global denyList for this model
  allowList?: string[] // overrides global allowList for this model
}
FieldTypeDefaultDescription
denyListstring[]undefinedFields to mask. Merged with the 11 default sensitive fields.
allowListstring[]undefinedFields to keep. Everything else is masked. Applied after denyList.
replacementstring'***masked***'The string used in place of a masked value.
deepbooleantrueWhen true, recursively masks nested objects and arrays.
modelsRecord<string, ModelMaskConfig>undefinedPer-model overrides. Keys match the class constructor name of the causer or subject.

Types ​

ts
interface ActivityPayload {
  description:  string
  causerId?:    string
  causerType?:  string
  subjectId?:   string
  subjectType?: string
  properties?:  Record<string, unknown>
  createdAt:    Date
}

interface ActivityAdapter {
  save(payload: ActivityPayload): Promise<void>
}

interface HasId {
  id: string | number
}

interface ActivityLogOptions {
  adapter:          ActivityAdapter
  getUserId?:       (req: Request) => string | undefined
  getDescription?:  (req: Request) => string
  skip?:            (req: Request) => boolean
  mask?:            MaskConfig
}

interface PrismaClientLike {
  activityLog: {
    create(args: { data: Record<string, unknown> }): Promise<unknown>
  }
}

interface MongooseLike {
  model(name: string, schema?: unknown): MongooseModelLike
  Schema: new (definition: Record<string, unknown>, options?: Record<string, unknown>) => unknown
}

interface MongooseModelLike {
  create(doc: Record<string, unknown>): Promise<unknown>
}

Need an admin panel?

Get a beautiful admin UI for all actinode packages. Contact us to learn more about our premium admin package.

No spam, ever. Unsubscribe at any time.

Released under the MIT License.