A function is a module that calls Deno.serve with a handler from Request to Response.
#The handler
Your entrypoint (index.ts by default) calls Deno.serve once. The handler receives a standard Request and returns a Response (or a promise of one).
Deno.serve(async (req) => {
const { name = 'world' } = await req.json().catch(() => ({}))
return Response.json({ message: `Hello ${name}!` })
})The request URL path starts with the function name: a call to /functions/v1/my-api/items/42?x=1 arrives as /my-api/items/42?x=1. Use it to serve several routes from one function:
import { Hono } from 'jsr:@hono/hono@4'
// The request path starts with the function name: /my-api/...
const app = new Hono().basePath('/my-api')
app.get('/items/:id', (c) => c.json({ id: c.req.param('id') }))
app.post('/items', async (c) => c.json(await c.req.json(), 201))
Deno.serve(app.fetch)#Imports
// npm and JSR packages, and modules from URLs
import Stripe from 'npm:stripe@17'
import { Hono } from 'jsr:@hono/hono@4'
import { createClient } from 'https://stitchdb.com/sdk/stitchdb-js.mjs'
// Your own files, deployed with the function
import { slugify } from './lib/text.ts'Packages are downloaded when the function first starts and cached on the server. Pin versions (npm:stripe@17) so a new release can't change your function unexpectedly.
#Several files and an import map
A function can have up to 200 files. Add files in the Code tab (+ File) or upload them with the API. A deno.json (or import_map.json) next to the entrypoint is used as the import map:
{
"imports": {
"zod": "npm:zod@3",
"@/": "./lib/"
}
}#Streaming and background work
Responses stream to the caller as you produce them, so server-sent events work:
Deno.serve(() => {
const body = new ReadableStream({
async start(controller) {
for (let i = 1; i <= 5; i++) {
controller.enqueue(new TextEncoder().encode(`data: tick ${i}\n\n`))
await new Promise((r) => setTimeout(r, 1000))
}
controller.close()
},
})
return new Response(body, { headers: { 'Content-Type': 'text/event-stream' } })
})To answer first and keep working, hand a promise to EdgeRuntime.waitUntil(). It runs until it settles or the wall-clock limit is reached:
Deno.serve(async (req) => {
const payload = await req.json()
// Answer right away; the work continues after the response (within the wall-clock limit).
EdgeRuntime.waitUntil(fetch('https://hooks.example.com/ingest', { method: 'POST', body: JSON.stringify(payload) }))
return new Response('accepted', { status: 202 })
})#What is not available
- Starting processes (
Deno.Command) and native libraries (FFI). - The server's file system. Each request may write temporary files under
/tmp; they are not kept. - Private and internal network addresses, UDP and SMTP on port 25. Send email through an HTTPS API (see Examples).
#Errors
| Status | Code | Meaning |
|---|---|---|
| 401 | UNAUTHORIZED_NO_AUTH_HEADER / UNAUTHORIZED_INVALID_JWT | JWT verification is on and the request has no valid token. |
| 404 | NOT_FOUND | No function with that name. |
| 429 | QUOTA_EXCEEDED | The monthly invocations of your plan are used up (or the API rate limit was hit). |
| 500 | WORKER_ERROR | The function threw before answering. See its logs. |
| 503 | BOOT_ERROR | The function failed to start: a syntax or import error. See its logs. |
| 504 | IDLE_TIMEOUT | The function did not start answering in time. |
| 546 | WORKER_LIMIT | The function was stopped because it used more CPU time, memory or wall-clock time than your plan allows. |