Skip to Content
✨ v1.1.0 Released - See the release notes
DocsNextmin Nodenextmin-node

nextmin-node

Node.js toolkit for building schema‑driven CRUD APIs, auth, and file uploads.

Features

  • Express router: createNextMinRouter(options) mounts a full API
  • Auth: sign in, sign up, refresh token, forgot password
  • CRUD: create, read (filter/sort/paginate), update, delete per schema
  • Extended schemas: child models can extends a base model (e.g., Doctors → Users)
    • Create/Update automatically split base vs child fields
    • Read automatically merges base + child fields; baseId stays hidden
    • Delete removes child and linked base
  • Policies: role/owner checks, read masks, write deny, field restrictions
  • Files: upload/download with pluggable storage (local/S3) and MIME → ext helper
  • Database: MongoDB adapter with index sync; in‑memory adapter for tests
  • Schemas: hot‑reload via SchemaService and automatic model wiring
  • Utilities: generateApiKey, seed default data, schema loaders

This guide covers installation, quick start, and common usage patterns.

Installation

Use your preferred package manager:

bash
# npm
npm install nextmin-node
 
# yarn
yarn add nextmin-node
 
# pnpm
pnpm add nextmin-node

Quick start

ts
import { NextMinClient } from '@airoom/nextmin-node';
 
const client = new NextMinClient({
  baseURL: process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3000/api',
  // IMPORTANT: Use the API key from the DB → Settings table (see below)
  apiKey: process.env.NEXTMIN_API_KEY,
});
 
async function main() {
  // Example: health check
  const status = await client.health();
  console.log('Service status:', status);
}
 
main().catch(console.error);

Usage

  • Auth: client.auth.signIn({ email, password })
  • Users: client.users.getById(id)
  • Custom endpoints: client.request('GET', '/your-route')

Configuration

Create a .env.local in your Next.js project:

env
NEXT_PUBLIC_API_URL=https://your-host.tld/api
# Use the API key stored in your database → Settings table (see below)
NEXTMIN_API_KEY=your_api_key_here

Where to find your API key (NEXTMIN_API_KEY)

On first run, nextmin-node initializes default data and ensures there is a system Settings document that contains an apiKey. You should use this value in your clients.

Steps:

  • Start your server once so initialization runs.
  • Open your database and locate the Settings collection/table.
  • Read the first Settings document and copy the apiKey field.
  • Use that apiKey as:
    • NEXTMIN_API_KEY for server-side or Node clients, and
    • NEXT_PUBLIC_NEXTMIN_API_KEY for nextmin-react apps.

Notes:

  • You can preseed the initial key by setting the env var NEXTMIN_API_KEY before the first boot; if absent, the server will generate one and store it in Settings.
  • If you rotate the key in the DB, restart clients with the new value.

TypeScript

Types are bundled with the package. Import what you need:

ts
import type { SignInResponse } from '@airoom/nextmin-node/types';

Examples

Full Express server example (from the examples/node/server.ts):

ts
import dotenv from 'dotenv';
import express from 'express';
import {
  createNextMinRouter,
  MongoAdapter,
  S3FileStorageAdapter,
} from '@airoom/nextmin-node';
import cors from 'cors';
import http from 'http';
 
dotenv.config();
 
async function start() {
  const app = express();
  const server = http.createServer(app); // <— the ONE server
 
  app.use(cors());
 
  const dbAdapter = new MongoAdapter('mongodb://localhost:27017', 'doctors24');
  await dbAdapter.connect();
 
  const fileStorageAdapter = new S3FileStorageAdapter({
    bucket: process.env.S3_BUCKET!,
    region: process.env.S3_REGION!,
    credentials:
      process.env.S3_ACCESS_KEY_ID && process.env.S3_SECRET_ACCESS_KEY
        ? {
            accessKeyId: process.env.S3_ACCESS_KEY_ID!,
            secretAccessKey: process.env.S3_SECRET_ACCESS_KEY!,
          }
        : undefined,
    endpoint: process.env.S3_ENDPOINT || undefined,
    forcePathStyle: /^(1|true|yes)$/i.test(
      process.env.S3_FORCE_PATH_STYLE || '',
    ),
    defaultACL: (process.env.S3_DEFAULT_ACL as any) || 'public-read',
    publicBaseUrl: process.env.S3_PUBLIC_BASE_URL || undefined,
  });
 
  // Pass THIS 'server' so the socket can attach to the listening server
  const nextminRouter = createNextMinRouter({
    dbAdapter,
    server,
    fileStorageAdapter,
  });
 
  app.use('/rest', nextminRouter);
 
  // IMPORTANT: listen with 'server', not app.listen
  server.listen(8081, () => {
    console.log('Server running on http://localhost:8081');
  });
}
 
start().catch(console.error);

Environment variables used by the example (as in examples/node/.env):

env
# Application
APP_MODE=development               # runtime mode (development|production)
JWT_SECRET=your_jwt_secret_here    # secret for signing JWT tokens

# S3 / Object Storage for file uploads
S3_BUCKET=your-bucket-name         # required: target bucket name
S3_REGION=us-east-1                # required: bucket region
S3_ACCESS_KEY_ID=your-access-key   # required if your storage is private
S3_SECRET_ACCESS_KEY=your-secret   # required if your storage is private
S3_ENDPOINT=http://127.0.0.1:9000  # optional: custom endpoint (e.g., MinIO)
S3_FORCE_PATH_STYLE=true           # optional: true for MinIO/path-style URLs
S3_DEFAULT_ACL=public-read         # optional: default object ACL
S3_PUBLIC_BASE_URL=https://cdn.example.com  # optional: public CDN/base URL

# Frontend (used by maps autocomplete in admin UI)
NEXT_PUBLIC_GOOGLE_MAPS_KEY=your_google_maps_api_key

Extended schemas (inheritance)

You can make a model extend another by adding "extends": "BaseModelName" to the child schema. Example: Doctors extends Users.

How it works at runtime:

  • Hidden foreign key: The child stores a private baseId that links to its base record. This field is never shown in schema output or forms and is stripped from API responses.
  • Create: When posting to the child (e.g., POST /rest/doctors), the server splits the payload into base vs child fields, creates the base first (e.g., Users), then creates the child with baseId set automatically. The request does not need to include baseId.
  • Read (list/by-id): The server merges base + child documents by baseId and returns a single object per row. If both define the same field, the child wins.
  • Update: You may send both base and child fields to the child route; the server partitions and updates both sides, then returns the merged doc. Changing baseId is not allowed.
  • Delete: Deleting a child record also deletes the linked base record.
  • Validation: Required-field checks apply to the currently targeted model only (child on create/update of child) and skip private fields. Uniqueness is enforced per collection.
  • Privacy/masks: Private fields (including baseId) are hidden unless your policy exposes them. Sensitive masks apply if exposePrivate is enabled by policy.
  • Find routes: The generic /find and /find/reverse routes hydrate extended targets the same way (merge by baseId before masking).

Related UI behavior (nextmin-react):

  • The form renderer never shows baseId, but preserves its value when editing existing records so updates behave like a foreign key.

See also: Schema examples → · API examples →

Troubleshooting

  • Ensure the baseURL is correct and reachable.
  • If you see 401 errors, verify your client key matches the value in DB → Settings.apiKey.
  • For CORS issues, configure your backend to allow your docs/app origin in development.

See also

  • nextmin-react package
  • Project repository on GitHub
Last updated on