npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@axiumine/koa-utils

v3.8.0

Published

Koa Utils

Downloads

464

Readme

@axiumine/koa-utils

TypeScript utility library for Koa + GraphQL backends. Ships authentication middleware, GraphQL error helpers, data-source connectors (MongoDB, MariaDB, PostgreSQL, Redis), file upload pipeline (ClamAV + sharp re-encode), and transactional email via SocketLabs.

  • Module format: ESM only (.mjs + .d.mts)
  • Node: ^24.14.0
  • TypeScript: 5.9
  • License: GPL-3.0-or-later
  • Maintainer: Giovanni Manzoni

Install

yarn add @axiumine/koa-utils
# or
npm install @axiumine/koa-utils

All runtime dependencies are declared as peer dependencies. Install the ones you use:

yarn add @node-rs/bcrypt @sentry/node @socketlabs/email clamscan dotenv \
  file-type fs-extra graphql keygrip koa koa-logger mongoose pg redis \
  reflect-metadata sequelize sequelize-typescript sharp uuid

Usage

No barrel — import each helper from its explicit subpath.

Data sources

import { MongoDBConnect, MongoDBDisconnect } from '@axiumine/koa-utils/dataSources/MongoDB'
import { redisClient, RedisConnect } from '@axiumine/koa-utils/dataSources/Redis'
import { pgPool, PostgreSQLClientConnect } from '@axiumine/koa-utils/dataSources/PostgreSQL'
import { sequelize, MariaDBConnect } from '@axiumine/koa-utils/dataSources/MariaDB'

await MongoDBConnect()
await RedisConnect()

Koa middleware

import Koa from 'koa'
import Keygrip from 'keygrip'
import { tdwKoaErrorHandler } from '@axiumine/koa-utils/koa/tdwKoaErrorHandler'
import { authenticatedResourceHandler } from '@axiumine/koa-utils/koa/middleware/authenticatedResourceHandler'
import { authenticatedAuthorizationHandler } from '@axiumine/koa-utils/koa/middleware/authenticatedAuthorizationHandler'

const app = new Koa()
const keys = new Keygrip([process.env.COOKIE_KEY!])

app.use(tdwKoaErrorHandler)
app.use(authenticatedResourceHandler())                // resource endpoints
app.use(authenticatedAuthorizationHandler(keys))       // refresh endpoint

GraphQL mutations

Plain mutation definitions ready to drop into a GraphQLObjectType:

import { GraphQLObjectType } from 'graphql'
import { signUp } from '@axiumine/koa-utils/graphQL/schema/mutations/signUp'
import { loginRememberme } from '@axiumine/koa-utils/graphQL/schema/mutations/loginRememberme'
import { logout } from '@axiumine/koa-utils/graphQL/schema/mutations/logout'
import { refresh } from '@axiumine/koa-utils/graphQL/schema/mutations/refresh'
import { resetPwd } from '@axiumine/koa-utils/graphQL/schema/mutations/resetPwd'
import { updatePassword } from '@axiumine/koa-utils/graphQL/schema/mutations/updatePassword'

const Mutation = new GraphQLObjectType({
	name: 'Mutation',
	fields: { signUp, loginRememberme, logout, refresh, resetPwd, updatePassword }
})

GraphQL error helpers

import { throwForbiddenError } from '@axiumine/koa-utils/graphQL/throw/throwForbiddenError'
import { throwNotFoundError } from '@axiumine/koa-utils/graphQL/throw/throwNotFoundError'
import { throwTooManyRequestsError } from '@axiumine/koa-utils/graphQL/throw/throwTooManyRequestsError'

if (!user) throw throwNotFoundError()

All throw* helpers wrap throwGraphQLError(status, title, description) and yield a GraphQLError carrying extensions.http.status so tdwKoaErrorHandler maps it to the right HTTP code.

File uploads

import { initClamScan } from '@axiumine/koa-utils/files/scanVirus'
import { uploadTemp } from '@axiumine/koa-utils/files/uploadTempImage'
import { uploadTempPdf } from '@axiumine/koa-utils/files/uploadTempPdf'

await initClamScan()  // once at boot
const { tempFile, ext } = await uploadTemp(filePromise)  // jpg/png → webp

Pipeline: stream to /tmp (5 MB cap) → extension + magic-number MIME check → ClamAV → sharp re-encode (strips EXIF). PDF path scans only (no re-encode).

Email

import { SocketLabsLib } from '@axiumine/koa-utils/email/SocketlabsLib'

const mailer = new SocketLabsLib()
await mailer.sendEmailVerify('[email protected]', hash)

Note: copy is Italian. Subclass / replace template methods for other locales.

Helpers

import { encryptPassword } from '@axiumine/koa-utils/lib/encryptPassword'
import { compareHashAsync } from '@axiumine/koa-utils/lib/hash'
import { checkEmailLen } from '@axiumine/koa-utils/lib/checkEmailLen'
import { checkPwdLen } from '@axiumine/koa-utils/lib/checkPwdLen'
import { generateAccessToken, generateRefreshToken, accessTokenExpiry, REFRESH_TOKEN_EXPIRY } from '@axiumine/koa-utils/lib/tokens'
import { setLoginCookies } from '@axiumine/koa-utils/lib/setLoginCookies'
import { DateLib } from '@axiumine/koa-utils/lib/DateLib'
import { StringLib } from '@axiumine/koa-utils/lib/StringLib'

Auth flow

  1. signUp — creates a user with account.email.valid=false, sends verify email.
  2. routerVerifyEmail — Koa router validating the email + hash, enabling the account.
  3. loginRememberme / login4Ever / loginAdmin — bcrypt compare, generate uuid access:<uuid> + refresh:<uuid> in Redis, set signed refresh_token cookie (90 d), return access token in body.
  4. authenticatedResourceHandler — middleware reading Authorization: Bearer access:<uuid>.
  5. authenticatedAuthorizationHandler(keys) — middleware verifying signed refresh cookie via Keygrip; mount before the refresh endpoint.
  6. refresh — rotates both tokens, random access TTL in [30, 90] minutes, fixed 90-day refresh TTL.
  7. logout + authenticatedLogoutHandler — clears Redis keys and cookie.

Environment variables

| Var | Purpose | | --- | --- | | MONGODB_URI | Mongo connection string | | MARIADB_DBNAME, _USER, _PWD, _IP, _PORT, _RETRY, _TIMEOUT, _LOGGING | MariaDB | | POSTGRESQL_USER, _PWD, _HOST, _PORT, _DBNAME, _POOL_MAX, _IDLE_TIMEOUT | PostgreSQL | | REDIS_URL or REDIS_IS_CLUSTER + REDIS_DB{1,2,3}_HOST/PORT, REDIS_USERNAME, REDIS_PASSWORD | Redis | | REDIS_KEY | Prefix for all keys (e.g. myapp:) | | INTROSPECTION_CODE | Bypass header x-introspectioncode for schema introspection | | SOCKETLABS_SERVER_ID, SOCKETLABS_SERVER_APIKEY | SocketLabs auth | | PLATFORM_NAME, APP_DOMAIN, EMAIL_FROM, DEV_TEAM_EMAIL | Email templating | | STATIC_FOLDER | Destination for moveFileStaticDomain | | NODE_ENV | development enables extra Sentry capture in the error handler |

Each module calls dotenv.config() at import time.

TLS / cookies

tokenOptions sets secure: false by design. Flip it at the reverse proxy (Nginx / Caddy / CloudFront), not in source.

Build (contributors only)

yarn install
yarn build        # ESM only — production output
yarn build:all    # ESM + CJS dual output
yarn lint         # eslint --fix + prettier --write
yarn clean

Source files are .mts. Imports use .mjs extension explicitly (NodeNext). Path aliases (@lib/*, @throw/*, @models/*, @context/*, @stypes/*, @private/*, @email/*, @dataSources/*) are rewritten to relative paths in emitted code via typescript-transform-paths (run through tspc, the ts-patch wrapper).

Adding a new public symbol requires both:

  1. Creating src/<area>/<Name>.mts
  2. Adding the matching entry under exports in package.json

There is no barrel and no main / module field.

Repository

https://github.com/Axiumine/koa-utils

License

GPL-3.0-or-later © Giovanni Manzoni