Express.jsNode.jsTypeScript

The customer database for Express.js backends

Express.js powers a significant share of Node.js APIs and backends. Whether you're running a full monolith or a microservice handling auth, TinyCRM slots in as a single function call in your route handlers — no new dependencies, no middleware to configure, no behavioral changes to your existing code.

Installation

npm install tinycrm-sdk

Set your API key in .env:

TINYCRM_API_KEY=tcrm_proj_xxxxxxxxxxxx

Express architecture and integration points

In an Express.js app, TinyCRM belongs in your route handlers — specifically wherever you create users, confirm sign-ups, or process payment webhooks.

src/
  lib/
    tinycrm.ts       ← Shared singleton
  routes/
    auth.ts          ← POST /auth/signup — call identify() here
    webhooks.ts      ← Payment webhooks — update status on paid
  middleware/
    activity.ts      ← Optional: ping on authenticated requests

Getting started: Express.js integration

Step 1 — Create a shared SDK instance

// src/lib/tinycrm.ts
import { TinyCRM } from "tinycrm-sdk";

export const tinycrm = new TinyCRM({
  apiKey: process.env.TINYCRM_API_KEY!,
});

Step 2 — Identify in your sign-up route

// src/routes/auth.ts
import { Router, Request, Response } from "express";
import { tinycrm } from "../lib/tinycrm";
import { db } from "../lib/db";

const router = Router();

router.post("/signup", async (req: Request, res: Response) => {
  const { email, name, password } = req.body;

  const user = await db.users.create({ email, name, password });

  // Fire-and-forget — no await, response is not blocked
  tinycrm.identify({
    email: user.email,
    name:  user.name,
    status: "free",
    params: {
      source:     req.query.utm_source as string ?? "direct",
      ip_country: req.headers["cf-ipcountry"] as string ?? "",
    },
  });

  res.json({ ok: true, userId: user.id });
});

export default router;

Step 3 — Integrate with Passport.js local strategy

// src/auth/passport.ts
import passport from "passport";
import { Strategy as LocalStrategy } from "passport-local";
import { tinycrm } from "../lib/tinycrm";
import { db } from "../lib/db";

passport.use(new LocalStrategy(
  { usernameField: "email" },
  async (email, password, done) => {
    let user = await db.users.findByEmail(email);

    if (!user) {
      // New user — create and identify
      user = await db.users.create({ email, password });
      tinycrm.identify({ email, status: "free" });
    }

    if (!user.verifyPassword(password)) {
      return done(null, false, { message: "Invalid credentials" });
    }

    return done(null, user);
  }
));

Step 4 — Stripe webhook for paid conversions

// src/routes/webhooks.ts
import { Router } from "express";
import Stripe from "stripe";
import { tinycrm } from "../lib/tinycrm";

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
const router = Router();

router.post(
  "/stripe",
  express.raw({ type: "application/json" }),
  (req, res) => {
    const event = stripe.webhooks.constructEvent(
      req.body,
      req.headers["stripe-signature"]!,
      process.env.STRIPE_WEBHOOK_SECRET!
    );

    if (event.type === "checkout.session.completed") {
      const session = event.data.object;
      tinycrm.identify({
        email:  session.customer_email!,
        status: "paid",
        params: { plan: session.metadata?.plan ?? "pro" },
      });
    }

    res.json({ received: true });
  }
);

export default router;

Step 5 — Optional: ping middleware for activity tracking

// src/middleware/activity.ts
import { Request, Response, NextFunction } from "express";
import { tinycrm } from "../lib/tinycrm";

export function trackActivity(req: Request, res: Response, next: NextFunction) {
  if (req.user?.email) {
    // Fire-and-forget ping to update last_activity
    tinycrm.ping({ email: req.user.email });
  }
  next();
}

// Apply to authenticated routes:
app.use("/api/dashboard", authenticate, trackActivity);

Node.js ecosystem compatibility

Passport.js

Integrate in strategy verify callbacks or route handlers.

Prisma

Call identify() after prisma.user.create().

TypeORM

Works after userRepo.save(user).

Mongoose

Use post('save') middleware on your User schema.

Fastify

SDK is framework-agnostic — works in any route handler.

AWS Lambda

Works in Lambda Node.js 18+ runtimes.

Express.js FAQ

Does tinycrm-sdk work with Express.js middleware?

Yes. You can create a custom Express middleware that calls ping() on every authenticated request, or call identify() directly in your route handler functions. Both patterns are valid.

Can I use TinyCRM with Passport.js authentication?

Yes. Call identify() inside the Passport strategy's verify callback after the user is created or first logged in. For OAuth strategies, fire it in the done() callback after user upsert.

Does it work with Fastify or Koa instead of Express?

Yes. The SDK is framework-agnostic — it uses native fetch and has no Express dependency. Import it in any Node.js 18+ server framework including Fastify, Koa, Hapi, or raw HTTP handlers.

How do I avoid the identify() call blocking my Express route response?

Don't await the identify() promise. Call it without await (fire-and-forget) and the Express route returns immediately. The SDK runs the HTTP request in the background. Errors are swallowed silently by default.

Your Node.js customers deserve a real home

14-day free trial. No credit card. Works with any Express or Node.js backend.

Start free trial
npm install tinycrm-sdk