2025-01-18 13:46:25 +00:00
# @cap.js/server
2025-04-12 13:39:56 +01:00
`@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:
2025-01-18 13:46:25 +00:00
2025-04-12 13:39:56 +01:00
::: code-group
```bash [bun]
bun add @cap.js/server
2025-01-18 13:46:25 +00:00
` ``
2025-04-12 13:39:56 +01:00
` ``bash [npm]
2025-01-18 13:46:25 +00:00
npm i @cap.js/server
` ``
2025-04-12 13:39:56 +01:00
` ``bash [pnpm]
pnpm i @cap.js/server
` ``
:::
2025-01-18 13:46:25 +00:00
## Example code
2025-04-12 13:39:56 +01:00
::: code-group
2025-04-29 19:01:22 +01:00
2025-04-12 13:39:56 +01:00
` ``js [Elysia]
2025-04-29 19:01:22 +01:00
import { Elysia } from "elysia";
2025-04-12 13:39:56 +01:00
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);
2025-04-29 19:01:22 +01:00
2025-04-12 13:39:56 +01:00
console.log(` 🦊 Elysia is running at http://localhost:3000`);
` ``
` ``js [Fastify]
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) => {
2025-04-29 19:01:22 +01:00
res.send(cap.createChallenge());
2025-04-12 13:39:56 +01:00
});
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 [Bun.serve]
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`);
` ``
2025-05-30 19:57:40 +01:00
` ``js [hono]
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,
};
` ``
2025-04-12 13:39:56 +01:00
` ``js [Express]
import express from "express";
import Cap from "@cap.js/server";
2025-01-18 13:46:25 +00:00
const app = express();
app.use(express.json());
const cap = new Cap({
2025-04-29 19:01:22 +01:00
tokens_store_path: ".data/tokensList.json",
2025-01-18 13:46:25 +00:00
});
2025-04-29 19:01:22 +01:00
app.post("/api/challenge", (req, res) => {
2025-01-18 13:46:25 +00:00
res.json(cap.createChallenge());
});
2025-04-29 19:01:22 +01:00
app.post("/api/redeem", async (req, res) => {
2025-01-18 13:46:25 +00:00
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, () => {
2025-04-29 19:01:22 +01:00
console.log("Listening on port 3000");
});
2025-01-18 13:46:25 +00:00
` ``
2025-04-29 19:01:22 +01:00
2025-04-12 13:39:56 +01:00
:::
2025-06-11 17:20:14 +01:00
::: warning
These example codes don't have ratelimiting for simplicity. Make sure to add proper ratelimiting to your endpoints to prevent abuse.
:::
2025-04-12 13:39:56 +01:00
Then, you can verify the CAPTCHA tokens on your server by calling the ` await cap.validateToken("<token>")` method. Example:
2025-04-29 19:01:22 +01:00
2025-04-12 13:39:56 +01:00
` ``js
const { success } = await cap.validateToken("9363220f..."); // [!code highlight]
if (success) {
console.log("Valid token");
} else {
console.log("Invalid token");
}
` ``
2025-01-18 13:46:25 +00:00
## Supported methods and arguments
The following methods are supported:
#### ` new Cap({ ... })`
2025-04-29 19:01:22 +01:00
2025-01-18 13:46:25 +00:00
Creates a new Cap instance.
**Arguments**
2025-04-29 19:01:22 +01:00
2025-01-18 13:46:25 +00:00
` ``json
{
2025-04-29 19:01:22 +01:00
"tokens_store_path": ".data/tokensList.json",
"noFSState": false,
"state": {
"challengesList": {},
"tokensList": {}
}
2025-01-18 13:46:25 +00:00
}
` ``
2025-05-30 19:57:40 +01:00
You can always access or set the options of the ` Cap` class by accessing or modifying the ` cap.config` object.
2025-01-18 13:46:25 +00:00
#### ` cap.createChallenge({ ... })`
2025-04-29 19:01:22 +01:00
2025-01-18 13:46:25 +00:00
**Arguments**
2025-04-29 19:01:22 +01:00
2025-01-18 13:46:25 +00:00
` ``json
{
2025-06-11 17:13:22 +01:00
"challengeCount": 50,
2025-04-29 19:01:22 +01:00
"challengeSize": 32,
"challengeDifficulty": 4,
"expiresMs": 600000
2025-01-18 13:46:25 +00:00
}
` ``
2025-04-29 19:01:22 +01:00
2025-04-11 19:36:05 +01:00
**Response:** ` { challenge, expires }`
2025-01-18 13:46:25 +00:00
2025-04-29 19:01:22 +01:00
> 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](https://github.com/tiagorangel1/cap/pull/16)
2025-01-18 13:46:25 +00:00
#### ` cap.redeemChallenge({ ... })`
2025-04-29 19:01:22 +01:00
2025-01-18 13:46:25 +00:00
` ``json
{
token,
solutions
}
` ``
2025-04-11 19:36:05 +01:00
**Response:** ` { success, token }`
2025-01-18 13:46:25 +00:00
#### ` await cap.validateToken("...", { ... })`
2025-04-29 19:01:22 +01:00
2025-01-18 13:46:25 +00:00
**Arguments:**
2025-04-29 19:01:22 +01:00
2025-01-18 13:46:25 +00:00
` ``json
{
2025-04-29 19:01:22 +01:00
"keepToken": false
2025-01-18 13:46:25 +00:00
}
` ``
2025-04-29 19:01:22 +01:00
**Response:** ` { success }`