Run JavaScript remotely with any npm package and no deploy step
A run JavaScript remotely API is useful when the code is small and the setup around it is not. Parsing HTML with cheerio, normalizing data with lodash or computing a value next to the target site all need a runtime, dependencies and somewhere to host them. With Browser Functions you send the function, require what you need, and get the return value back.
A ten-line scraper should not need a deployment pipeline
The logic is short: fetch a page, load it into cheerio, pick a few values, return them. Around it you need a function host, a package.json, a bundle, a deploy, logs and a way to update the dependency when it ships a fix.
Serverless platforms solve hosting but not the ceremony. Every new script is a new function to configure and deploy, packages have to be bundled ahead of time, and if the code sometimes needs a browser you are back to shipping Chromium alongside it.
The function runtime installs dependencies for you. Any require() in your code is detected, installed in a sandbox and cached, and require('[email protected]') pins a version. When the function never references page, no browser starts at all, so plain JavaScript runs faster. The writing functions guide covers each step.
How to run serverless JavaScript scraping with npm packages
Start without a browser. Add page only when the content you need is rendered by JavaScript on the target.
import createClient from 'microlink.io'
const microlink = createClient({
apiKey: process.env.MICROLINK_API_KEY
})
const headings = async ({ url }) => {
const cheerio = require('[email protected]')
const response = await fetch(url)
if (!response.ok) throw new Error(`HTTP ${response.status}`)
const $ = cheerio.load(await response.text())
return $('h2').map((_, el) => $(el).text().trim()).get()
}
const { isFulfilled, value } = await microlink.function(
'https://example.com/docs',
headings
)The function never touches page, so no browser starts. It fetches the target itself, which counts as a same-origin request and is allowed on every plan, and returns the headings as an array.
import createClient from 'microlink.io'
const microlink = createClient({
apiKey: process.env.MICROLINK_API_KEY
})
const rendered = async ({ page }) => {
const cheerio = require('[email protected]')
const $ = cheerio.load(await page.content())
return $('[data-price]').map((_, el) => $(el).attr('data-price')).get()
}
const { value } = await microlink.function('https://app.example.com', rendered, {
waitForSelector: '[data-price]'
})Referencing page starts a browser and navigates first, so page.content() is the HTML after JavaScript ran. waitForSelector holds the call until the elements exist.
import createClient from 'microlink.io'
const microlink = createClient({
apiKey: process.env.MICROLINK_API_KEY
})
const pick = ({ url, fields }) => {
const { pick: select } = require('lodash')
return fetch(url)
.then(response => response.json())
.then(items => items.map(item => select(item, fields.split(','))))
}
const { value } = await microlink.function(
'https://api.example.com/products.json',
pick,
{ fields: 'id,name,price' }
)fields is not an API parameter, so it reaches the function as a named argument. One function serves many requests without changing its code.
- function The code to run. require() calls are detected and installed automatically.
- waitForSelector Waits for an element before a function that uses page is called.
- meta Skipped by default from the SDK, so the request only pays for the function.
- ttl Caches the function result like any other response, from 1 minute to 31 days. Pro plans.
The first run of a new dependency set shows a high install time in profiling.phases; later runs with the same dependencies skip installation and it drops to zero. The function reference documents the install, build, spawn and run phases.
Why a remote function beats deploying a script per job
The unit you write is a function. The runtime, the dependencies and the browser are resolved per request, so there is nothing else to maintain.
The Browser Functions feature summarizes the runtime, and the sitemap tool is built on this same function method.
For click loops, waits and interaction, see run Puppeteer without hosting Chrome.
When not to: packages that spawn child processes or write to the filesystem outside the sandbox fail with ERR_ACCESS_DENIED, and heavy CPU work can hit CpuTimeError. If a declarative rule can read the value, scrape it to JSON instead.
FAQ
Can I use cheerio in the cloud without deploying a server?
Which npm packages can I require in a remote function?
Why is the first run of my remote JavaScript function slower?
Can serverless JavaScript scraping call other domains?
How do I pin an npm package version in a remote function?
Solve the next problem with the same API
Puppeteer without hosting Chrome
Every link and email on a page
Cached JSON endpoints
Any website to JSON
Pagination and Load more buttons
LLM context from any URL
Ready to run JavaScript remotely?
Any npm package, no deploy step, a browser only when you ask for one. Start on the free tier and send your first function today.