Functions
A function is one file, or a folder with an entry file, that answers HTTPS requests. No repository, framework or port. Deploy it from the CLI and it gets its own URL, reading the variables you give your project's functions.
Functions suit the small pieces of backend code a frontend needs: a Paystack webhook, a call to an AI API with a secret key, sending an email. They are like Supabase Edge Functions, and most Supabase functions copy over as they are.
Quick start
-
Install the CLI and log in. Once per machine.
Terminalbashnpm i -g @naijacloud/cli naijacloud login -
Save this as hello.ts.
hello.tstsexport default async function handler(req: Request) { const name = process.env.GREETING ?? "world"; return Response.json({ message: `Hello ${name}` }); } -
Deploy it. Run this in the folder with hello.ts, with your project's name.
Terminalbashnaijacloud functions deploy hello --project <your-project>
The CLI prints the function's URL when it is live. It looks like
https://<your-project>-fn.naijacloud.app/functions/v1/hello; always copy the
one the CLI or the dashboard shows. The function's page in the dashboard shows
its calls, errors and logs.
The CLI is naijacloud, or njc for short.
Two ways to write a function
Both are TypeScript or JavaScript, and both are saved as hello.ts (or .js).
TypeScript, Node style. Export a handler that takes a Request and returns
a Response. You can use process.env, Buffer and node: modules.
export default async function handler(req: Request) {
const name = process.env.GREETING ?? "world";
return Response.json({ message: `Hello ${name}` });
}Supabase / Deno style. Pass your handler to Deno.serve. This is the shape
Supabase Edge Functions use.
Deno.serve(async (req: Request) => {
const name = Deno.env.get("GREETING") ?? "world";
return Response.json({ message: `Hello ${name}` });
});export default { fetch } works too.
npm packages
Put a package.json next to your entry file and import packages by name, for
example import { z } from "zod". The CLI uploads the folder and the
dependencies are installed when the deploy builds. An import that cannot be
found fails the deploy, and the version before it keeps serving, so a bad import
never reaches your first call.
{
"dependencies": {
"zod": "^3.23.8"
}
}Paths
Your function sees the path with /functions/v1 taken off, so a call to
/functions/v1/hello/receipts arrives as /hello/receipts. A router that
expects that base path, such as Hono's basePath('/hello'), works unchanged.
Names
Function names use lower-case letters, numbers and dashes, up to 40
characters, and cannot start or end with a dash. send-receipt is a good name.
Each name is unique in a project.
Read a secret
Every function in a project reads the same variables and secret files. They
belong to the project's functions runtime, a service named functions-runtime.
Set one from the CLI:
naijacloud env set GREETING --service functions-runtime --project <your-project>If the project has functions in more than one environment, add --env <name>.
After the first deploy you can also set them in the dashboard: open any function and choose Edit project variables on its Overview, which opens the runtime's Variables tab.
Then read it with process.env.GREETING, or Deno.env.get("GREETING") in
the Deno style. The function's
Overview lists the names it can read, never the values, with a link to edit
them. A changed variable applies on the next deploy.
Deleting a project's last function removes its runtime, and the runtime's variables and secret files go with it, so an empty project isn't billed. Set them again after you deploy a function there next.
Call it from your frontend
A function is public: anyone with the URL can call it. Check what you need in your code, such as a webhook signature or a token in a header.
A browser on another domain needs CORS headers. Answer the preflight and add the header to every response:
const cors = {
"Access-Control-Allow-Origin": "https://your-site.example",
"Access-Control-Allow-Headers": "content-type, authorization",
};
export default async function handler(req: Request) {
if (req.method === "OPTIONS") return new Response(null, { headers: cors });
return Response.json({ ok: true }, { headers: cors });
}const res = await fetch("https://<your-project>-fn.naijacloud.app/functions/v1/hello");
const data = await res.json();View logs
Open the function in the dashboard and choose Logs. Each call shows its
request line (method, path, status and how long it took) above the lines your
code printed with console.log, console.warn or console.error. With
Argus on, the same lines are in Argus too.
Moving from Supabase Edge Functions
The command is almost the same:
| Supabase | Naijacloud |
|---|---|
supabase functions deploy hello | naijacloud functions deploy hello --project <your-project> |
supabase secrets set NAME=value | naijacloud env set NAME --service functions-runtime --project <your-project> |
Deno.env.get("NAME") | Deno.env.get("NAME") or process.env.NAME |
What doesn't carry over yet:
verify_jwt. Functions do not check a Supabase JWT for you. Every function is public, so check the token in your code.- Supabase's own variables. Supabase sets
SUPABASE_URLand its keys for you. Here, set the ones your code reads withnaijacloud env set.
Limits and pricing
| Limit | Value |
|---|---|
| Memory | 256 MB per function runtime |
| Longest run per call | 150 s on the Free tier, 400 s on paid tiers. A longer call returns 504. |
| Code size | 20 MB per deploy |
Functions are included in your project's compute and billed in naira, with no charge per call. All of a project's functions share one runtime, which costs the same as a web service of the same size. On the Free tier it sleeps when idle and wakes on the next call, so that first call takes longer. On a paid tier it stays awake.
Related: Environment variables and the CLI.