
Security News
/Research
Fake Corepack Site Distributes Infostealer and Proxyware to Developers
A fake corepack.org site is impersonating the Node.js tool and delivers an infostealer and proxyware to developers who download it.
nextjs-adapter-firebase-functions
Advanced tools
Firebase Cloud Functions adapter for Next.js 16.2 adapter API
Firebase Cloud Functions adapter for Next.js 16.2 custom adapter API.
Deploys your Next.js app as a single Firebase Cloud Function (v2) using onRequest. All routes — App Router, Pages Router, API routes, middleware, image optimization — are handled by one function via next().getRequestHandler().
Built with Claude Code
>=16.2.022npm install -g firebase-tools)npm install nextjs-adapter-firebase-functions
Point adapterPath at the adapter. The zero-config default covers most apps:
// next.config.ts
import { createRequire } from 'node:module'
const require = createRequire(import.meta.url)
export default {
adapterPath: require.resolve('nextjs-adapter-firebase-functions'),
}
To configure secrets, function settings, or env vars, use a dedicated adapter config file (adapter.config.ts, .mjs, or .js — any extension works):
// adapter.config.ts
import { createFirebaseAdapter } from 'nextjs-adapter-firebase-functions'
export default createFirebaseAdapter({
functionName: 'nextjs',
secrets: ['DATABASE_URL', 'STRIPE_SECRET_KEY'],
functionConfig: {
region: 'europe-west1',
memory: '2GiB',
timeoutSeconds: 120,
minInstances: 1,
},
env: {
NEXT_PUBLIC_APP_URL: 'https://myapp.com',
},
})
// next.config.ts
import { createRequire } from 'node:module'
const require = createRequire(import.meta.url)
export default {
adapterPath: require.resolve('./adapter.config'),
}
next.config.ts wrappers)adapterPath works even when your config is wrapped by withPayload() or similar:
// next.config.ts
import { withPayload } from '@payloadcms/next/withPayload'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
const dirname = path.dirname(fileURLToPath(import.meta.url))
export default withPayload({
adapterPath: path.resolve(dirname, './adapter.config.mjs'),
})
If you cannot modify next.config.ts (e.g. in CI), set NEXT_ADAPTER_PATH as an environment variable instead:
NEXT_ADAPTER_PATH="$(node -e "console.log(require.resolve('nextjs-adapter-firebase-functions'))")" \
next build
Turbopack: Next.js 16+ uses Turbopack by default. Turbopack creates
.next/node_modules/with machine-specific symlinks that break on Cloud Run. Always build with--webpackfor Firebase deploys.
Monorepo: Only the project's own
package.jsondependenciesare included in the generatedfunctions/package.json. Workspace root deps are intentionally excluded to avoid pulling in unrelated sibling packages. The workspace rootnode_modulesis still scanned for native binary sub-packages (e.g.sharp), so hoisted native modules are handled correctly.
next build --webpack
The adapter runs after the Next.js build and writes its output to firebase-dist/ (configurable via outDir):
firebase-dist/
functions/
.next/ # copied from .next/ (cache/, dev/, trace excluded)
public/ # copied from public/ (if present)
index.js # Firebase Function entry (generated, CJS)
firebase-params.js # Secret/param definitions (generated, CJS)
runtime-next-config.json # Runtime Next.js config
deployment-manifest.json # Adapter metadata
package.json # Functions package (generated)
.gitignore
firebase.json # Written to project root if absent
firebase deploy --only functions --project my-project-id
During firebase deploy, you will be prompted to set any secret values not yet stored in Cloud Secret Manager.
predeploy in firebase.jsonAdd a predeploy hook so firebase deploy triggers the build automatically:
{
"functions": {
"source": "firebase-dist/functions",
"predeploy": ["next build --webpack"]
}
}
Then deploy with:
firebase deploy --only functions --project my-project-id
Import from the environment subpath to read the active Firebase project. Useful for conditional config based on the active environment (e.g. production vs staging).
Requires firebase-tools as a peer dependency (npm install --save-dev firebase-tools).
import { getFirebaseEnvironment } from 'nextjs-adapter-firebase-functions/environment'
const env = await getFirebaseEnvironment()
// env?.projectId → "my-firebase-project"
// env?.alias → "default"
// env?.root → "/path/to/project"
Example — conditional minInstances based on alias:
// adapter.config.ts
import { getFirebaseEnvironment } from 'nextjs-adapter-firebase-functions/environment'
import { createFirebaseAdapter } from 'nextjs-adapter-firebase-functions'
const env = await getFirebaseEnvironment()
export default createFirebaseAdapter({
secrets: ['DATABASE_URL', 'PAYLOAD_SECRET'],
functionConfig: {
region: 'europe-west1',
minInstances: env?.alias === 'production' ? 1 : 0,
},
})
Requires a
.firebasercin the project or workspace root. Runfirebase use <project>once to create it.
FirebaseAdapterOptionsinterface FirebaseAdapterOptions {
outDir?: string
functionName?: string
secrets?: (string | SecretParam)[]
params?: Record<string, FirebaseParamDefinition>
env?: Record<string, string>
envFilePath?: string
functionConfig?: FirebaseFunctionsHttpsOptions
}
outDirType: string Default: 'firebase-dist'
Directory (relative to project root) where the adapter writes its output.
functionNameType: string Default: 'nextjs'
The exported Firebase Function name. Appears in the Firebase console and emulator URL.
createFirebaseAdapter({ functionName: 'server' })
// URL: https://{region}-{project}.cloudfunctions.net/server
functionConfigType: FirebaseFunctionsHttpsOptions
Options spread directly into onRequest(). All Firebase Functions v2 HttpsOptions fields are accepted.
createFirebaseAdapter({
functionConfig: {
region: 'europe-west1',
timeoutSeconds: 120, // default: 60
memory: '2GiB', // default: '1GiB'
minInstances: 1, // default: 0 (cold start on first request)
maxInstances: 100,
concurrency: 80, // default: 80
invoker: 'public', // default: 'public'
labels: { team: 'frontend' },
},
})
memory values: '128MiB' | '256MiB' | '512MiB' | '1GiB' | '2GiB' | '4GiB' | '8GiB' | '16GiB' | '32GiB'
invoker: 'public' (anyone can call) | 'private' (requires auth) | string[] (service accounts)
secretsType: (string | SecretParam)[]
Cloud Secret Manager secrets to bind to the function. Accepts plain name strings or SecretParam objects from defineSecret(). At runtime, bound secrets are available as process.env.SECRET_NAME.
// Plain strings
createFirebaseAdapter({
secrets: ['DATABASE_URL', 'STRIPE_SECRET_KEY'],
})
// SecretParam objects (e.g. shared across multiple functions)
import { defineSecret } from 'firebase-functions/params'
const DATABASE_URL = defineSecret('DATABASE_URL')
const STRIPE_SECRET_KEY = defineSecret('STRIPE_SECRET_KEY')
export default createFirebaseAdapter({
secrets: [DATABASE_URL, STRIPE_SECRET_KEY],
})
Access in your Next.js code without any imports:
// app/api/charge/route.ts
export async function POST(req: Request) {
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!)
}
Setting secrets:
firebase functions:secrets:set DATABASE_URL
# prompts for value, stores in Cloud Secret Manager
paramsType: Record<string, FirebaseParamDefinition>
Non-sensitive configuration values (feature flags, API endpoints, limits). Available as process.env.KEY at runtime.
createFirebaseAdapter({
params: {
NEXT_PUBLIC_API_URL: { type: 'string', default: 'https://api.example.com' },
ITEMS_PER_PAGE: { type: 'int', default: 20 },
ENABLE_ANALYTICS: { type: 'boolean', default: true },
},
})
interface FirebaseParamDefinition {
type: 'string' | 'int' | 'boolean'
default?: string | number | boolean
description?: string
}
envType: Record<string, string>
Inline environment variables written to functions/.env. Available as process.env.KEY at runtime and in the emulator.
envFilePathType: string
Path to a .env file (relative to project root) merged into functions/.env. Inline env values take precedence on conflict.
createFirebaseAdapter({ envFilePath: '.env.production' })
functions/index.jsGenerated Firebase Function entry — re-generated on every build, do not edit manually.
functionNamereq.rawBody — the generated entry restores the stream so Next.js App Router can read it correctlyfunctions/firebase-params.jsGenerated secret/param definitions. Can be required from other functions in the same codebase to share secrets:
const { DATABASE_URL } = require('./firebase-params')
exports.myOtherFunction = onRequest({ secrets: [DATABASE_URL] }, handler)
firebase.jsonWritten to the project root if it doesn't already exist. If you already have a firebase.json, it is never overwritten — merge the generated settings manually.
node --import tsx/esm --test test/unit/*.test.ts
Scaffolds create-next-app, builds with the adapter, deploys to the Firebase emulator:
ADAPTER_DIR="$(pwd)" bash scripts/e2e-deploy-starter.sh
Scaffolds a Payload CMS blank app, builds with the adapter, deploys to the Firebase emulator. Uses mongodb-memory-server — no external MongoDB required:
ADAPTER_DIR="$(pwd)" bash scripts/e2e-deploy-payload.sh
Both scripts accept:
| Variable | Default | Description |
|---|---|---|
ADAPTER_DIR | (required) | Path to this repo root |
NEXT_TEST_DIR | temp dir | Directory to scaffold into |
FIREBASE_PROJECT_ID | (required) | Firebase project ID for emulator |
MIT
FAQs
Firebase Cloud Functions adapter for Next.js 16.2 adapter API
We found that nextjs-adapter-firebase-functions demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
/Research
A fake corepack.org site is impersonating the Node.js tool and delivers an infostealer and proxyware to developers who download it.

Research
/Security News
A large-scale campaign abused GitHub Actions in compromised repositories to exploit CVE-2026-41940 in cPanel and WHM and steal server credentials.

Security News
Five frontier LLMs generated the same nonexistent package names, leaving 53 available for potential slopsquatting across PyPI and npm.