Skip to content

@cap.js/server

@cap.js/server is Cap's server-side library. It helps you create and validate challenges for your users. Start by installing it using bun (recommended), npm, or pnpm:

bash
bun add @cap.js/server
bash
npm i @cap.js/server
bash
pnpm i @cap.js/server

Example code

js
import { Elysia } from "elysia";
import Cap from "@cap.js/server";

const cap = new Cap({
  tokens_store_path: ".data/tokensList.json",
});

new Elysia()
  .post("/api/challenge", () => {
    return cap.createChallenge();
  })

  .post("/api/redeem", async ({ body, set }) => {
    const { token, solutions } = body;

    if (!token || !solutions) {
      set.status = 400;
      return { success: false };
    }

    return await cap.redeemChallenge({ token, solutions });
  })
  .listen(3000);

console.log(`🦊 Elysia is running at http://localhost:3000`);
js
import Fastify from "fastify";
import Cap from "@cap.js/server";

const fastify = Fastify();
const cap = new Cap({
  tokens_store_path: ".data/tokensList.json",
});

fastify.post("/api/challenge", (req, res) => {
  res.send(cap.createChallenge());
});

fastify.post("/api/redeem", async (req, res) => {
  const { token, solutions } = req.body;
  if (!token || !solutions) {
    return res.code(400).send({ success: false });
  }

  res.send(await cap.redeemChallenge({ token, solutions }));
});

fastify.listen({ port: 3000, host: "0.0.0.0" }).then(() => {
  console.log("Server is running on http://localhost:3000");
});
js
import Cap from "@cap.js/server";

const cap = new Cap({
  tokens_store_path: ".data/tokensList.json",
});

Bun.serve({
  port: 3000,
  routes: {
    "/api/challenge": {
      POST: () => {
        return Response.json(cap.createChallenge());
      },
    },
    "/api/redeem": {
      POST: async (req) => {
        const body = await req.json();
        const { token, solutions } = body;

        if (!token || !solutions) {
          return Response.json({ success: false }, { status: 400 });
        }

        return Response.json(await cap.redeemChallenge({ token, solutions }));
      },
    },
  },
});

console.log(`Server running at http://localhost:3000`);
js
import { Hono } from "hono";
import Cap from "@cap.js/server";

const app = new Hono();
const cap = new Cap({
  tokens_store_path: ".data/tokensList.json",
});

app.post("/api/challenge", (c) => {
  return c.json(cap.createChallenge());
});

app.post("/api/redeem", async (c) => {
  const { token, solutions } = await c.req.json();
  if (!token || !solutions) {
    return c.json({ success: false }, 400);
  }

  return c.json(await cap.redeemChallenge({ token, solutions }));
});

export default {
  port: 3000,
  fetch: app.fetch,
};
js
import express from "express";
import Cap from "@cap.js/server";

const app = express();
app.use(express.json());

const cap = new Cap({
  tokens_store_path: ".data/tokensList.json",
});

app.post("/api/challenge", (req, res) => {
  res.json(cap.createChallenge());
});

app.post("/api/redeem", async (req, res) => {
  const { token, solutions } = req.body;
  if (!token || !solutions) {
    return res.status(400).json({ success: false });
  }
  res.json(await cap.redeemChallenge({ token, solutions }));
});

app.listen(3000, () => {
  console.log("Listening on port 3000");
});

Then, you can verify the CAPTCHA tokens on your server by calling the await cap.validateToken("<token>") method. Example:

js
const { success } = await cap.validateToken("9363220f..."); 

if (success) {
  console.log("Valid token");
} else {
  console.log("Invalid token");
}

Supported methods and arguments

The following methods are supported:

new Cap({ ... })

Creates a new Cap instance.

Arguments

json
{
  "tokens_store_path": ".data/tokensList.json",
  "noFSState": false,
  "state": {
    "challengesList": {},
    "tokensList": {}
  }
}

You can always access or set the options of the Cap class by accessing or modifying the cap.config object.

cap.createChallenge({ ... })

Arguments

json
{
  "challengeCount": 18,
  "challengeSize": 32,
  "challengeDifficulty": 4,
  "expiresMs": 600000
}

Response: { challenge, expires }

if noFSState is set to true, the state will be stored in memory only. You can use this together with setting config.state to use a custom db such as redis for storing tokens. Added by #16

cap.redeemChallenge({ ... })

json
{
  token,
  solutions
}

Response: { success, token }

await cap.validateToken("...", { ... })

Arguments:

json
{
  "keepToken": false
}

Response: { success }

Built in Europe 🇪🇺
Released under the Apache 2.0 License.