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
adapterActivityAdapterRequired. 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.