Integration guides
Nuxt and Next
Moderate content in a Nuxt server route or a Next.js route handler, so the key never leaves the server.
Both frameworks run server code next to your pages. Put the call to openshield there, and let your pages call your own route.
Nuxt
Keep the key in a private runtime config value. In nuxt.config.ts, an empty default under runtimeConfig (outside public) is filled from the environment variable NUXT_OPENMOD_API_KEY:
nuxt.config.ts
export default defineNuxtConfig({
runtimeConfig: {
openmodApiKey: "", // set NUXT_OPENMOD_API_KEY in the environment
},
})
server/api/comments.post.ts
interface Answer {
summary: { action: "accept" | "review" | "reject"; reject_reason: Array<{ id: string }> }
request_id: string
}
export default defineEventHandler(async (event) => {
const { text, id } = await readBody<{ text: string; id: string }>(event)
const { openmodApiKey } = useRuntimeConfig(event)
const result = await $fetch<Answer>("https://api.openshield.dev/v1/text", {
method: "POST",
headers: { Authorization: `Bearer ${openmodApiKey}` },
body: { text, ref: id },
timeout: 10_000,
}).catch((error: { statusCode?: number }) => error.statusCode ?? 0)
if (typeof result === "number") {
// 400, 413 and 415: the content cannot be accepted as it is.
if ([400, 413, 415].includes(result)) throw createError({ statusCode: 422 })
// Anything else, a timeout included: the check could not be made. Fail closed, keep the comment hidden.
return { published: false }
}
const { action, reject_reason } = result.summary
if (action === "reject") throw createError({ statusCode: 422, data: { reason: reject_reason[0]?.id } })
// Store the comment, hidden when action is "review", published when it is "accept".
return { published: action === "accept", requestId: result.request_id }
})
Next.js
Keep the key in .env.local (not committed) and read it in a route handler:
.env.local
OPENMOD_API_KEY=om_live_...
app/api/comments/route.ts
import { NextResponse } from "next/server"
export async function POST(request: Request) {
const { text, id } = (await request.json()) as { text: string; id: string }
let answer: Response
try {
answer = await fetch("https://api.openshield.dev/v1/text", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.OPENMOD_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ text, ref: id }),
signal: AbortSignal.timeout(10_000),
})
} catch {
// The check could not be made: fail closed, keep the comment hidden.
return NextResponse.json({ published: false })
}
const body = await answer.json()
if (!answer.ok) {
const refused = [400, 413, 415].includes(answer.status)
return NextResponse.json({ published: false, refused }, { status: refused ? 422 : 200 })
}
const { action, reject_reason } = body.summary
if (action === "reject") return NextResponse.json({ published: false, reason: reject_reason[0]?.id }, { status: 422 })
// Store the comment, hidden when action is "review", published when it is "accept".
return NextResponse.json({ published: action === "accept", requestId: body.request_id })
}
Do not prefix the variable with
NEXT_PUBLIC_: that ships it to every visitor's browser. In Nuxt, never put the key under runtimeConfig.public.