Skip to content

express-activitylog ​

@actinode/express-activitylog logs every request (or any custom event) to your database with a fluent, readable API.

Installation ​

sh
pnpm add @actinode/express-activitylog
sh
npm install @actinode/express-activitylog
sh
yarn add @actinode/express-activitylog

Then install an adapter. See Adapters for full setup guides.

sh
pnpm add @prisma/client
sh
pnpm add mongoose

Quick start ​

1. Set up your adapter ​

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

const prisma = new PrismaClient()
const adapter = prismaAdapter(prisma)
ts
import mongoose from 'mongoose'
import { mongooseAdapter } from '@actinode/express-activitylog'

await mongoose.connect(process.env.MONGODB_URI!)
const adapter = mongooseAdapter(mongoose)

2. Register the middleware ​

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

const app = express()

app.use(
  activityLog({
    adapter,
    getUserId: (req) => req.user?.id,
    skip: (req) => req.method === 'GET',
  }),
)

Every non-GET request is now logged automatically.

3. Log manually with the fluent builder ​

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

await activity(adapter)
  .by(req.user)       // causer
  .on(post)           // subject
  .withProperties({ ip: req.ip })
  .log('published post')

Masking sensitive data ​

Sensitive fields are masked before they reach your database — both in the middleware (applied to req.body) and in manual log entries (applied to .withProperties()).

TIP

Even without any mask config, common sensitive fields like password, token, and apiKey are masked automatically. See the full list.

denyList — mask specific fields, keep everything else:

ts
activityLog({
  adapter,
  mask: { denyList: ['password', 'token'] },
})

allowList — declare which fields are safe to log, mask everything else:

ts
activityLog({
  adapter,
  mask: { allowList: ['name', 'email', 'role'] },
})

For the full masking reference — deep masking, per-model config, custom replacement strings, and security best practices — see the Masking page.

Minimal working example ​

ts
import express from 'express'
import { PrismaClient } from '@prisma/client'
import { prismaAdapter, activityLog, activity } from '@actinode/express-activitylog'

const prisma = new PrismaClient()
const adapter = prismaAdapter(prisma)
const app = express()

app.use(express.json())

// Auto-log every mutating request
app.use(
  activityLog({
    adapter,
    getUserId: (req) => (req as any).user?.id,
    getDescription: (req) => `${req.method} ${req.path}`,
    skip: (req) => req.method === 'GET',
  }),
)

app.post('/posts/:id/publish', async (req, res) => {
  const post = await prisma.post.update({
    where: { id: req.params.id },
    data: { published: true },
  })

  // Manual log with full context
  await activity(adapter)
    .on(post)
    .log('published post')

  res.json(post)
})

app.listen(3000)

Community & Support ​

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.