@microlink/function
v0.3.4
Published
Write JavaScript functions and run them remotely with Microlink — Puppeteer access, npm packages, and zero infrastructure.
Maintainers
Readme
Highlights
- Starts from $0/mo.
- Run JavaScript remotely with no servers, bundles, or browser fleet to manage.
- Full Puppeteer access when your function references
page. require()any npm package — dependencies are detected and installed on-the-fly.- Automatic serialization and compression of function bodies.
- Execution metrics at
result.profiling.
Documentation
The full guides live on microlink.io:
- Function — overview and when to use it.
- Writing functions — return values, custom parameters, and npm packages.
- Browser interaction — Puppeteer helpers and page automation.
- Profiling and performance — execution phases, plan limits, and optimization.
- Troubleshooting — error handling and common failure modes.
Installation
npm install @microlink/functionLoad directly in the browser from a CDN:
<script src="https://cdn.jsdelivr.net/npm/@microlink/function/dist/microlink-function.min.js"></script>Your first function
Pass a JavaScript function and a target URL. The library handles serialization, compression, and the API call:
const microlink = require('@microlink/function')
const fn = microlink(() => 40 + 2)
const result = await fn('https://example.com')
console.log(result.isFulfilled) // true
console.log(result.value) // 42
console.log(result.profiling) // { phases: { ... }, cpu, memory, size }When your function references page, Microlink starts a headless browser and gives you full Puppeteer access:
const microlink = require('@microlink/function')
const getTitle = ({ page }) => page.title()
const fn = microlink(getTitle)
const result = await fn('https://example.com')
console.log(result.value) // 'Example Domain'When your function does not reference page, no browser is started — execution is faster and cheaper.
Writing functions
Return any value
Functions can return strings, numbers, booleans, arrays, or plain objects:
const microlink = require('@microlink/function')
const fn = microlink(() => ({
greeting: 'Hello',
items: [1, 2, 3],
nested: { works: true }
}))
const result = await fn('https://example.com')
console.log(result.value)
// { greeting: 'Hello', items: [1, 2, 3], nested: { works: true } }The return value is always at result.value. If the function throws, result.isFulfilled is false and result.value contains the error details.
Custom parameters
Any extra option you pass to the returned function is forwarded to your code:
const microlink = require('@microlink/function')
const greet = ({ name, greeting }) => `${greeting}, ${name}!`
const fn = microlink(greet)
const result = await fn('https://example.com', {
name: 'Kiko',
greeting: 'Hello'
})
console.log(result.value) // 'Hello, Kiko!'npm packages
You can require() any npm package inside your function. Dependencies are detected automatically and installed on-the-fly:
const microlink = require('@microlink/function')
const fn = microlink(() => {
const { kebabCase } = require('lodash')
return kebabCase('Hello World')
})
const result = await fn('https://example.com')
console.log(result.value) // 'hello-world'Pin a version by appending it to the package name:
const cheerio = require('[email protected]')When no version is specified, the latest version is installed. Check result.profiling.phases — a high install value on the first run is normal and drops to zero once cached.
Security restrictions
The runtime restricts certain system capabilities. Operations such as spawning child processes or writing to the filesystem outside the sandbox are not permitted:
{
"isFulfilled": false,
"value": {
"name": "Error",
"code": "ERR_ACCESS_DENIED",
"permission": "ChildProcess",
"message": "Access to this API has been restricted."
}
}Browser interaction
When your function references page, you get the full Puppeteer Page object:
const microlink = require('@microlink/function')
const scrape = ({ page }) =>
page.$eval('h1', el => el.textContent.trim())
const fn = microlink(scrape)
const result = await fn('https://example.com')
console.log(result.value) // 'Example Domain'Start with the high-level helpers before reaching for lower-level APIs:
page.title()— document title.page.$eval()— run a function on the first matching element.page.$$eval()— run a function on all matching elements.page.url()— current URL.page.content()— full page HTML.
Any Puppeteer Page method is available.
What your function receives
| Property | Description |
| --- | --- |
| page | Full Puppeteer access. Microlink navigates to the URL before calling your function. |
| response | The HTTP response from the implicit navigation. Only available when the function uses page. |
| headers | Request headers used to fetch the target URL. |
| any extra parameter | Custom inputs forwarded from the request options. |
Click, wait, and navigate
const microlink = require('@microlink/function')
const scrapeAfterClick = ({ page }) =>
page.click('button.load-more')
.then(() => page.waitForSelector('.results'))
.then(() => page.$$eval('.results li', items =>
items.map(el => el.textContent.trim())
))
const fn = microlink(scrapeAfterClick)
const result = await fn('https://example.com')Replace fixed waits like page.waitForTimeout(3000) with page.waitForSelector() or page.waitForNavigation() whenever possible.
Combine with other parameters
Because function is just another Microlink parameter, you can prepare the page before your function runs using scripts, modules, click, or waitForSelector:
const microlink = require('@microlink/function')
const fn = microlink(
({ page }) => page.evaluate('jQuery.fn.jquery'),
{ meta: false }
)
const result = await fn('https://microlink.io', {
scripts: ['https://code.jquery.com/jquery-3.5.0.min.js']
})
console.log(result.value) // jQuery version stringProfiling and performance
Every function response includes profiling data:
const result = await fn('https://example.com')
console.log(result.profiling)
// {
// phases: { install: 0, build: 120, spawn: 45, run: 890, total: 1055 },
// cpu: 234,
// memory: { total: 69996544, used: 2359296, heap: 4410880, external: 1742574 },
// size: 156
// }| Field | Description |
| --- | --- |
| phases.install | Time spent installing npm dependencies (0 when none are used). |
| phases.build | Time spent bundling the function code. |
| phases.spawn | Time spent starting the isolated process. |
| phases.run | Time spent executing the function. |
| phases.total | Wall-clock time from start to finish. |
| cpu | Peak CPU time in milliseconds. |
| memory.total | Resident memory of the sandbox, Node.js baseline included, in bytes. |
| memory.used | Resident memory attributable to your function, in bytes. |
| memory.heap | V8 heap in use, in bytes. The only field the memory limit bounds. |
| memory.external | Off-heap Buffer/ArrayBuffer memory, in bytes. |
| size | Bundled code size in bytes. |
Plan limits
| | Free | Pro | | --- | --- | --- | | Timeout | 5 seconds | Up to 60 seconds | | Memory | 16 MB | 32 MB | | Code size | 1024 bytes | Unlimited | | Concurrency | 1 per IP | Unlimited |
The free plan is enough to prototype workflows. For production workloads that need more time, memory, or parameters such as headers, proxy, ttl, or staleTtl, use a pro plan.
Skip metadata
Most function-only workflows do not need normalized metadata. Set meta: false to skip it — this is usually the biggest speedup:
const fn = microlink(({ page }) => page.title(), { meta: false })If you still need the rendered markup, call page.content() inside the function.
Compression
@microlink/function compresses function bodies automatically before sending them to the API. You can also compress manually via microlink.compress() — useful when calling MQL directly:
const compressed = await microlink.compress(({ page }) => page.title())
// 'br#...' on Node.js (brotli), 'lz#...' as fallbackSupported prefixes: lz# (lz-string), br# (brotli), gz# (gzip).
Optimization checklist
- Set
meta: falseunless you need normalized metadata. - Use
page.title()andpage.$eval()instead ofpage.evaluate()when possible. - Replace fixed waits with
page.waitForSelector(). - Check
result.profiling.phasesto find the bottleneck. - Minimize dependencies — each
require()adds install and build time.
Troubleshooting
Error handling
When a function throws, the result comes back with isFulfilled: false and the error details at result.value:
const failing = ({ name }) => name()
const fn = microlink(failing)
const result = await fn('https://example.com', { name: 'Kiko' })
console.log(result.isFulfilled) // false
console.log(result.value.name) // 'TypeError'
console.log(result.value.message) // 'name is not a function'Non-Error throws (like throw 'oh no') are normalized into a NonError with the thrown value as the message.
Resource errors
When a function exceeds its plan limits, the API returns a descriptive error:
| Error | Trigger |
| --- | --- |
| TimeoutError | Wall-clock time exceeded the plan limit. |
| CpuTimeError | CPU time exceeded the plan limit. |
| MemoryError | Memory usage exceeded the plan limit. |
| CodeSizeError | Code exceeds the 1024 bytes free plan limit. |
| ConcurrencyError | Too many concurrent executions for the free plan (1 per IP). |
| OutgoingRequestError | Cross-origin network request on the free plan. |
Function-specific errors
- EINVALFUNCTION — invalid JavaScript syntax in the function string.
- EINVALEVAL — the function executed but threw at runtime.
Debugging tips
- Start simple — reduce the function to
({ page }) => page.title()to isolate the problem. - Set
meta: falseunless metadata is required. - Inspect
result.profilingto see where time is spent. - Keep orchestration in the outer function and DOM-only code inside
page.evaluate(). - Watch for null — DOM queries return null when the element does not exist.
See the troubleshooting guide for detailed remediation steps.
Choose the lightest tool
Use function when the built-in parameters stop being expressive enough, not as the default for every workflow.
| If you need | Best option | Why |
| --- | --- | --- |
| Simple field extraction from the DOM | data | Declarative rules are shorter and easier to maintain. |
| Inject CSS or JavaScript before another workflow | styles, modules, or scripts | Lighter than full browser automation. |
| Click, wait, compute, reshape, or orchestrate custom logic | function | Puppeteer access plus any npm package. |
Authentication
Pass your API key via the third argument (gotOpts):
const microlink = require('@microlink/function')
const fn = microlink(
({ page }) => page.title(),
{},
{ headers: { 'x-api-key': process.env.MICROLINK_API_KEY } }
)
const result = await fn('https://example.com')See authentication for endpoint and quota details.
Examples
See examples.
API
microlink(fn, [mqlOpts], [gotOpts])
Returns an async function (url, [mqlOpts], [gotOpts]) => Promise<FunctionResponse>.
fn
Required
Type: function
The function to execute remotely.
mqlOpts
Type: object
Default options forwarded to @microlink/mql. Per-call options merge on top.
gotOpts
Type: object
HTTP client options forwarded to got inside MQL — use this for authentication headers and other request settings.
Response shape
type FunctionResponse =
| {
isFulfilled: true
value: any
profiling: FunctionProfiling
logging: Record<string, unknown>
}
| {
isFulfilled: false
value: { name: string; message: string; [key: string]: unknown }
profiling: FunctionProfiling
logging: Record<string, unknown>
}Static properties
microlink.compress(code)— compress a function body for manual MQL usage.microlink.mql— the underlying @microlink/mql client.microlink.version— package version string.
License
@microlink/function © Microlink, released under the MIT License. Authored and maintained by Kiko Beats with help from contributors.
microlink.io · GitHub @MicrolinkHQ · X @microlinkhq
