@microlink/function lets you write normal JavaScript functions and run them remotely with the Microlink function parameter — full Puppeteer access, npm packages, and zero infrastructure.
- 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.
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.
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>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.
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.
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!'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('cheerio@1.0.0')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.
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."
}
}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.
| 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. |
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.
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 stringEvery 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: 8,
// 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 |
Peak memory usage in MB. |
size |
Bundled code size in bytes. |
| Free | Pro | |
|---|---|---|
| Timeout | 5 seconds | Up to 28 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.
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.
@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).
- 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.
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.
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. |
- EINVALFUNCTION — invalid JavaScript syntax in the function string.
- EINVALEVAL — the function executed but threw at runtime.
- 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.
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. |
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.
See examples.
Returns an async function (url, [mqlOpts], [gotOpts]) => Promise<FunctionResponse>.
Required
Type: function
The function to execute remotely.
Type: object
Default options forwarded to @microlink/mql. Per-call options merge on top.
Type: object
HTTP client options forwarded to got inside MQL — use this for authentication headers and other request settings.
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>
}microlink.compress(code)— compress a function body for manual MQL usage.microlink.mql— the underlying @microlink/mql client.microlink.version— package version string.
@microlink/function © Microlink, released under the MIT License.
Authored and maintained by Kiko Beats with help from contributors.
microlink.io · GitHub @MicrolinkHQ · X @microlinkhq