Introducing FunctionScript - Turn JavaScript Functions into Typed HTTP APIs

Introducing FunctionScript - Turn JavaScript Functions into Typed HTTP APIs

FunctionScript is a language and specification for turning JavaScript functions into typed HTTP APIs.

It allows **JavaScript **(Node.js) functions to be seamlessly exported as HTTP APIs by defining what the HTTP interface will look like and how it behaves in the preceding comment block - including type-safety mechanisms.

**FunctionScript **arose out of a need to introduce developers with little programming experience, but familiarity with JavaScript, to full-stack API development and best practices around defining and connecting HTTP application interfaces. For this reason, the goals of the language are significantly different than TypeScript. FunctionScript is intended to provide an easy introduction to API development for those of any skill level, while maintaining professional power and flexibility.

**FunctionScript **is the primary specification underpinning the Standard Library **API development **and integration platform. You can start building with FunctionScript immediately using Code on Standard Library, right in your web browser. An animated example has been provided below.

Note: In order to use Code on Standard Library you must have a registered account on, available for free.

Quick Example of a FunctionScript API

The following is a real-world excerpt of an **API **that can be used to query a Spreadsheet like a Database. The underlying implementation has been hidden, but the parameters for the API can be seen.

* Select Rows from a Spreadsheet by querying it like a Database
* @param {string} spreadsheetId The id of the Spreadsheet.
* @param {string} range The A1 notation of the values to use as a table.
* @param {enum} bounds Specify the ending bounds of the table.
* @param {object} where A list of column values to filter by.
* @param {object} limit A limit representing the number of results to return
* @ {number} offset The offset of records to begin at
* @ {number} count The number of records to return, 0 will return all
* @returns {object} selectQueryResult
* @ {string} spreadsheetId
* @ {string} range
* @ {array} rows An array of objects corresponding to row values
module.exports = async (
  spreadsheetId = null,
  bounds = 'FIRST_EMPTY_ROW',
  where = {},
  limit = {offset: 0, count: 0},
) => {

  /* implementation-specific JavaScript */

  return {/* some data */};


It generates an API which accepts (and type checks against, following schemas):

  • <strong>spreadsheetId</strong> A string
  • <strong>range</strong> A string
  • <strong>bounds</strong> An enum, can be either "FIRST_EMPTY_ROW" or "FULL_RANGE"
  • <strong>where</strong> An object
  • <strong>limit</strong> An object that must contain:
    limit.offset, a number``````limit.count, a number
    It will return an object:
    <strong>selectQueryResult</strong>selectQueryResult.spreadsheetId must be a string``````selectQueryResult.range must be a string``````selectQueryResult.rows must be an array##

The impetus for creating **FunctionScript **is simple: it stems from the initial vision of Standard Library. We believe the modern web is missing a base primitive - the API. Daily, computer systems and developers around the planet make trillions of requests to perform specific tasks: process credit card payments with Stripe, send team messages via Slack, create SMS messages with Twilio. These requests are made primarily over HTTP: Hypertext Transfer Protocol. However, little to no "hypertext" is actually sent or received, these use cases have emerged in an ad hoc fashion as a testament to the power of the world wide web. Oftentimes, API standardization attempts have been presented as band-aids instead of solutions: requiring developers to jury rig a language, framework, markup language and hosting provider together just to get a simple "hello world" out the door.

By *creating API development *standards as part of a language specification instead of a framework, **FunctionScript **truly treats the web API as a base primitive of software development instead of an afterthought. This allows teams to be able to deliver high-quality APIs with the same fidelity as organizations like Stripe in a fraction of the time without requiring any additional tooling.

**FunctionScript **has been developed by the team at Polybit Inc., responsible for Standard Library. Ongoing development is, in part, funded by both Stripe and Slack as venture investments in the parent organization.

Table of Contents
  1. Introduction
  2. Why FunctionScript?
    FunctionScript ExamplesAll Available TypesSpecificationFunctionScript Resource DefinitionContext DefinitionParametersConstraintsTypesType ConversionNullabilityFunctionScript Resource RequestsContextErrorsClientErrorParameterErrorDetails: RequiredDetails: InvalidFatalErrorRuntimeErrorValueError1. FunctionScript Server and Gateway: Implementation
  3. Acknowledgements

To put it simply, **FunctionScript **defines semantics and rules for turning exported JavaScript (Node.js) functions into strongly-typed, HTTP-accessible web APIs. In order to use FunctionScript, you'd set up your own FunctionScript Gateway or you would use an existing FunctionScript-compliant service like Standard Library.

FunctionScript allows you to turn something like this...

// hello_world.js

* My hello world function!
module.exports = (name = 'world') => {

  return `hello ${name}`;


Into a web API that can be called over HTTP like this (GET):

https://$[email protected]/hello_world?name=joe

Or like this (POST):

  "name": "joe"

And gives a result like this:

"hello joe"

Or, when a type mismatch occurs (like {"name":10}):

  "error": {

Why FunctionScript?

FunctionScript is intended primarily to provide a scaffold to build and deliver **APIs **easily. It works best in conjunction with the Standard Library platform which consumes the **FunctionScript API **definitions, hosts the code, generates documentation from the definitions, and automatically handles versioning and environment management. The reason we've open sourced the language specification is so that developers have an easier time developing against the highly modular API ecosystem we've created and can contribute their thoughts and requests.

You can break down the reason for the development of **FunctionScript **into a few key points:

  • Modern developers and people being introduced to software development for the first time are often trying to build web-native scripts. It is exceedingly difficult to go from "zero to API" in less than a few hours, writing code is just the first step of many. We'd like it to be the first and only step.
  • No true standards around **APIs **have ever been built or enforced in a rigorous manner across the industry. Primarily, opinions around SOAP, REST and GraphQL requests have been built into frameworks and tools instead of a language specification, which has artificially inflated the cognitive overhead required to ship functional web-based software.
  • Companies like Stripe and Twilio which have built and enforced their own API development paradigms internally have unlocked massive developer audiences in short timeframes, indicating the power of treating web APIs as a first-class citizen of development.
  • Serverless computing, specifically the Function-as-a-Service model of web-based computation, has made API development significantly more accessible but has not brought us over the "last-mile" hump.
  • JavaScript, specifically Node.js, is an ideal target for API development standardization due to its accessibility (front-end and back-end), growth trajectory, and flexibility. Most new developers are introduced to **JavaScript **out of necessity.
  • As opposed to something like TypeScript, FunctionScript helps newer entrants to software development by extending **JavaScript **with very little overhead. It adds types around only the HTTP interface, leaving the majority of the language footprint untouched but strengthening the "weakest" and least predictable link in the development chain: user input.

With FunctionScript, it's our goal to develop a language specification for building APIs that automatically provides a number of necessary features without additional tooling:

  • Standardized API Calling Conventions (HTTP)
  • Type-Safety Mechanisms at the HTTP -> Code Interface
  • Automatically Generated API Documentation
FunctionScript Examples

We'll be updating this section with examples for you to play with and modify on your own.

All Available Types

Here's an example of a hypothetical createUser.js function that can be used to create a user resource. It includes all available type definitions.

* @param {integer} id ID of the User
* @param {string} username Name of the user
* @param {number} age Age of the user
* @param {float} communityScore Community score (between 0.00 and 100.00)
* @param {object} metadata Key-value pairs corresponding to additional user data
* @ {string} createdAt Created at ISO-8601 String. Required as part of metadata.
* @ {?string} notes Additional notes. Nullable (not required) as part of object
* @param {array} friendIds List of friend ids
* @ {integer} friendId ID of a user (forces array to have all integer entries)
* @param {buffer} profilePhoto Base64-encoded filedata, read into Node as a Buffer
* @param {enum} userGroup The user group. Can be "USER" (read as 0) or "ADMIN" (read as 9)
*   ["USER", 0]
*   ["ADMIN", 9]
* @param {boolean} overwrite Overwrite current user data, if username matching
* @returns {object.http} successPage API Returns an HTTP object (webpage)
module.exports = async (id = null, username, age, communityScore, metadata, friendsIds = [], profilePhoto, userGroup, overwrite = false) => {

  // NOTE: id, friendIds and overwrite will be OPTIONAL as they have each been
  //       provided a defaultValue

  // Implementation-specific code here

  // API Output
  // NOTE: Note that because "object.http" was specified, this MUST follow the
  //       object.http schema: headers, statusCode, body

  return {
    headers: {'Content-Type': 'text/html'},
    statusCode: 200,
    body: Buffer.from('Here is a success message!')


Specification FunctionScript Resource Definition

A **FunctionScript **definition is a JSON output, traditionally saved as a definition.json file, generated from a **JavaScript **file, that respects the following format.

Given a function like this (filename my_function.js):

// my_function.js

* This is my function, it likes the greek alphabet
* @param {String} alpha Some letters, I guess
* @param {Number} beta And a number
* @param {Boolean} gamma True or false?
* @returns {Object} some value
module.exports = async (alpha, beta = 2, gamma, context) => {
  /* your code */

The FunctionScript parser will generate a definition.json file that looks like the following:

  "name": "my_function",
  "format": {
    "language": "nodejs",
    "async": true
  "description": "This is my function, it likes the greek alphabet",
  "bg": {
    "mode": "info",
    "value": ""
  "context": null,
  "params": [
      "name": "alpha",
      "type": "string",
      "description": "Some letters, I guess"
      "name": "beta",
      "type": "number",
      "defaultValue": 2,
      "description": "And a number"
      "name": "gamma",
      "type": "boolean",
      "description": "True or false?"
  "returns": {
    "type": "object",
    "description": "some value"

A definition must implement the following fields;

Context Definition

If the function does not access execution context details, this should always be null. If it is an object, it indicates that the function does access context details (i.e. remoteAddress, http headers, etc. - see Context).

This object does not have to be empty, it can contain vendor-specific details; for example "context": {"user": ["id", "email"]} may indicate that the execution context specifically accesses authenticated user id and email addresses.


Parameters have the following format;


As FunctionScript interfaces with "userland" (user input), a strongly typed signature is enforced for all inbound parameters. The following is a list of supported FunctionScript types.

Type Conversion

The buffer type will automatically be converted from any object with a single key-value pair matching the footprints {"_bytes": []} or {"_base64": ""}.

Otherwise, parameters provided to a function are expected to match their defined types. Requests made over HTTP via query parameters or POST data with type application/x-www-form-urlencoded will be automatically converted from strings to their respective expected types, when possible (see FunctionScript Resource Requests below):


All types are potentially nullable, an nullability can be defined in two ways:

(1) by setting "defaultValue": null in the NamedParameter definition.

/** * @param {string} nullableString */ module.exports = (nullableString = null) => { return `Test ${nullableString}`; } 

(2) By prepending a ? before the type name in the comment definition, i.e.:

/** * @param {?string} nullableString */ module.exports = (nullableString) => { return `Test ${nullableString}`; } 

NOTE: That the difference between this two behaviors is that the latter will mean nullableString is both required AND nullable, whereas the former means nullableString has a defaultValue (is optional).

Setting HTTP headers

The object.http type should be used to generate HTTP responses that are intended to return more complex data than simple *JSON *responses.

You can provide headers, statusCode and body in an object.http response.

For example, to return an image that's of type image/png...

* Retrieves an image
* @param {string} imageName The name of the image
* @returns {object.http} image The result
module.exports = (imageName) => {

  // fetch image, returns a buffer
  let png = imageName === 'cat' ?
    fs.readFileSync(`/images/kitty.png`) :

  // Forces image/png over HTTP requests, default
  //  for buffer would otherwise be application/octet-stream
  return {
    headers: {'Content-Type': 'image/png'},
    statusCode: 200,
    body: png


FunctionScript Resource Requests

FunctionScript requests must complete the following steps;

  1. Ensure the Resource Definition is valid and compliant, either on storage or accession.
  2. Performs a handshake (i.e. HTTP) with initial request details
  3. Accept an Array, Object or a string of URLencoded variables
  4. If over HTTP and query parameters present, query parameters used as URL encoded variables
  5. If over HTTP POST and query parameters present, reject requests that try to specify a POST body as well with a ClientError
  6. If over HTTP POST, requests must include a Content-Type header or a ClientError is immediately returned
  7. If over HTTP POST, Content-Type must be application/json for Array or Object data, or application/x-www-form-urlencoded for string data or a ClientError is immediately returned
  8. If application/x-www-form-urlencoded values are provided (either via POST body or query parameters), convert types based on Type Conversion and knowledge of the function definition and create an Object
  9. If Array: Parameters will be checked for type consistency in the order of the definition params
  10. If Object: Parameters will be checked for type consistency based on names of the definition params
  11. If any inconsistencies are found, cease execution and immediately return a ParameterError
  12. If a parameter has no defaultValue specified and is not provided, immediately return a ParameterError
  13. Try to execute the function, if the function fails to parse or is not valid, immediately return a FatalError
  14. If a function hits a specified timeout (execution time limit), immediately return a FatalError
  15. If a function returns an error (via callback) or one is thrown and not caught, immediately return a RuntimeError
  16. If function returns inconsistent response (does not match returns type), immediately return a ValueError
  17. If no errors are encountered, return the value to the client
  18. If over HTTP and content-type is not being overloaded (i.e. developer specified through a vendor-specific mechanism), return buffer type data as application/octet-stream and any other values as application/json.


Every function intended to be consumed via FunctionScript has the option to specify an optional magic context parameter that receives vendor-specific information about the function execution context - for example, if consumed over HTTP, header details. FunctionScript definitions must specify whether or not they consume a context object. Context objects are extensible but MUST contain the following fields;


Errors returned by FunctionScript-compliant services must follow the following JSON format:

  "error": {
    "type": "ClientError",
    "message": "You know nothing, Jon Snow",
    "details": {}

details is an optional object that can provide additional Parameter details. Valid Error types are:

  • ClientError
  • ParameterError
  • FatalError
  • RuntimeError
  • ValueError


ClientErrors are returned as a result of bad or malformed client data, including lack of authorization or a missing function (not found). If over HTTP, they must returns status codes in the range of 4xx.


ParameterErrors are a result of Parameters not passing type-safety checks, and must return status code 400 if over HTTP.

Parameter Errors must have the following format;

  "error": {
    "type": "ParameterError",
    "message": "ParameterError",
    "details": {...}

"details" should be an object mapping parameter names to their respective validation (type-checking) errors. Currently, this specification defines two classifications of a ParameterError for a parameter; required and invalid. The format of "details": {} should follow this format;

Details: Required
  "param_name": {
    "message": "((descriptive message stating parameter is required))",
    "required": true

Details: Invalid
  "param_name": {
    "message": "((descriptive message stating parameter is invalid))",
    "invalid": true,
    "expected": {
      "type": "number"
    "actual": {
      "type": "string",
      "value": "hello world"


FatalErrors are a result of function mismanagement - either your function could not be loaded, executed, or it timed out. These must return status code 500 if over HTTP.


RuntimeErrors are a result of uncaught exceptions in your code as it runs, including errors you explicitly choose to throw (or send to clients via a callback, for example). These must return status code 403 if over HTTP.


ValueErrors are a result of your function returning an unexpected value based on FunctionScript type-safety mechanisms. These must return status code 502 if over HTTP.

ValueError looks like an invalid ParameterError, where the details Object only ever contains a single key called "returns". These are encountered due to implementation issues on the part of the function developer.

  "error": {
    "type": "ValueError",
    "message": "ValueError",
    "details": {
      "returns": {
        "message": "((descriptive message stating return value is invalid))",
        "invalid": true,
        "expected": {
          "type": "boolean"
        "actual": {
          "type": "number",
          "value": 2017

FunctionScript Server and Gateway: Implementation

A fully-compliant **FunctionScript **gateway (that just uses local function resources) is available with this package, simply clone it and run npm test or look at the /tests folder for more information.

The **FunctionScript **specification is used as the platform specification for Standard Library, and is available for local use with the Standard Library CLI Package which relies on this repository as a dependency.


FunctionScript is the result of years of concerted effort working to make API development easier. It would not be possible without the personal and financial commitments of some very amazing people and companies.

Hire Dedicated eCommerce Web Developers | Top eCommerce Web Designers

Hire Dedicated eCommerce Web Developers | Top eCommerce Web Designers

Build your eCommerce project by hiring our expert eCommerce Website developers. Our Dedicated Web Designers develop powerful & robust website in a short span of time.

Build your eCommerce project by hiring our expert eCommerce Website developers. Our Dedicated Web Designers develop powerful & robust website in a short span of time.

Hire Now:

Mobile App Development Company India | Ecommerce Web Development Company India

Mobile App Development Company India | Ecommerce Web Development Company India

Best Mobile App Development Company India, WebClues Global is one of the leading web and mobile app development company. Our team offers complete IT solutions including Cross-Platform App Development, CMS & E-Commerce, and UI/UX Design.

We are custom eCommerce Development Company working with all types of industry verticals and providing them end-to-end solutions for their eCommerce store development.

Know more about Top E-Commerce Web Development Company

JavaScript developers should you be using Web Workers?

JavaScript developers should you be using Web Workers?

Do you think JavaScript developers should be making more use of Web Workers to shift execution off of the main thread?

Originally published by David Gilbertson at

So, Web Workers. Those wonderful little critters that allow us to execute JavaScript off the main thread.

Also known as “no, you’re thinking of Service Workers”.

Photo by Caleb Jones on Unsplash

Before I get into the meat of the article, please sit for a lesson in how computers work:

Understood? Good.

For the red/green colourblind, let me explain. While a CPU is doing one thing, it can’t be doing another thing, which means you can’t sort a big array while a user scrolls the screen.

This is bad, if you have a big array and users with fingers.

Enter, Web Workers. These split open the atomic concept of a ‘CPU’ and allow us to think in terms of threads. We can use one thread to handle user-facing work like touch events and rendering the UI, and different threads to carry out all other work.

Check that out, the main thread is green the whole way through, ready to receive and respond to the gentle caress of a user.

You’re excited (I can tell), if we only have UI code on the main thread and all other code can go in a worker, things are going to be amazing (said the way Oprah would say it).

But cool your jets for just a moment, because websites are mostly about the UI — it’s why we have screens. And a lot of a user’s interactions with your site will be tapping on the screen, waiting for a response, reading, tapping, looking, reading, and so on.

So we can’t just say “here’s some JS that takes 20ms to run, chuck it on a thread”, we must think about where that execution time exists in the user’s world of tap, read, look, read, tap…

I like to boil this down to one specific question:

Is the user waiting anyway?

Imagine we have created some sort of git-repository-hosting website that shows all sorts of things about a repository. We have a cool feature called ‘issues’. A user can even click an ‘issues’ tab in our website to see a list of all issues relating to the repository. Groundbreaking!

When our users click this issues tab, the site is going to fetch the issue data, process it in some way — perhaps sort, or format dates, or work out which icon to show — then render the UI.

Inside the user’s computer, that’ll look exactly like this.

Look at that processing stage, locking up the main thread even though it has nothing to do with the UI! That’s terrible, in theory.

But think about what the human is actually doing at this point. They’re waiting for the common trio of network/process/render; just sittin’ around with less to do than the Bolivian Navy.

Because we care about our users, we show a loading indicator to let them know we’ve received their request and are working on it — putting the human in a ‘waiting’ state. Let’s add that to the diagram.

Now that we have a human in the picture, we can mix in a Web Worker and think about the impact it will have on their life:


First thing to note is that we’re not doing anything in parallel. We need the data from the network before we process it, and we need to process the data before we can render the UI. The elapsed time doesn’t change.

(BTW, the time involved in moving data to a Web Worker and back is negligible: 1ms per 100 KB is a decent rule of thumb.)

So we can move work off the main thread and have a page that is responsive during that time, but to what end? If our user is sitting there looking at a spinner for 600ms, have we enriched their experience by having a responsive screen for the middle third?


I’ve fudged these diagrams a little bit to make them the gorgeous specimens of graphic design that they are, but they’re not really to scale.

When responding to a user request, you’ll find that the network and DOM-manipulating part of any given task take much, much longer than the pure-JS data processing part.

I saw an article recently making the case that updating a Redux store was a good candidate for Web Workers because it’s not UI work (and non-UI work doesn’t belong on the main thread).

Chucking the data processing over to a worker thread sounds sensible, but the idea struck me as a little, umm, academic.

First, let’s split instances of ‘updating a store’ into two categories:

  1. Updating a store in response to a user interaction, then updating the UI in response to the data change
  2. Not that first one

If the first scenario, a user taps a button on the screen — perhaps to change the sort order of a list. The store updates, and this results in a re-rendering of the DOM (since that’s the point of a store).

Let me just delete one thing from the previous diagram:

In my experience, it is rare that the store-updating step goes beyond a few dozen milliseconds, and is generally followed by ten times that in DOM updating, layout, and paint. If I’ve got a site that’s taking longer than this, I’d be asking questions about why I have so much data in the browser and so much DOM, rather than on which thread I should do my processing.

So the question we’re faced with is the same one from above: the user tapped something on the screen, we’re going to work on that request for hopefully less than a second, why would we want to make the screen responsive during that time?

OK what about the second scenario, where a store update isn’t in response to a user interaction? Performing an auto-save, for example — there’s nothing more annoying than an app becoming unresponsive doing something you didn’t ask it to do.

Actually there’s heaps of things more annoying than that. Teens, for example.

Anyhoo, if you’re doing an auto-save and taking 100ms to process data client-side before sending it off to a server, then you should absolutely use a Web Worker.

In fact, any ‘background’ task that the user hasn’t asked for, or isn’t waiting for, is a good candidate for moving to a Web Worker.

The matter of value

Complexity is expensive, and implementing Web Workers ain’t cheap.

If you’re using a bundler — and you are — you’ll have a lot of reading to do, and probably npm packages to install. If you’ve got a create-react-app app, prepare to eject (and put aside two days twice a year to update 30 different packages when the next version of Babel/Redux/React/ESLint comes out).

Also, if you want to share anything fancier than plain data between a worker and the main thread you’ve got some more reading to do (comlink is your friend).

What I’m getting at is this: if the benefit is real, but minimal, then you’ve gotta ask if there’s something else you could spend a day or two on with a greater benefit to your users.

This thinking is true of everything, of course, but I’ve found that Web Workers have a particularly poor benefit-to-effort ratio.

Hey David, why you hate Web Workers so bad?

Good question.

This is a doweling jig:

I own a doweling jig. I love my doweling jig. If I need to drill a hole into the end of a piece of wood and ensure that it’s perfectly perpendicular to the surface, I use my doweling jig.

But I don’t use it to eat breakfast. For that I use a spoon.

Four years ago I was working on some fancy animations. They looked slick on a fast device, but janky on a slow one. So I wrote fireball-js, which executes a rudimentary performance benchmark on the user’s device and returns a score, allowing me to run my animations only on devices that would render them smoothly.

Where’s the best spot to run some CPU intensive code that the user didn’t request? On a different thread, of course. A Web Worker was the correct tool for the job.

Fast forward to 2019 and you’ll find me writing a routing algorithm for a mapping application. This requires parsing a big fat GeoJSON map into a collection of nodes and edges, to be used when a user asks for directions. The processing isn’t in response to a user request and the user isn’t waiting on it. And so, a Web Worker is the correct tool for the job.

It was only when doing this that it dawned on me: in the intervening quartet of years, I have seen exactly zero other instances where Web Workers would have improved the user experience.

Contrast this with a recent resurgence in Web Worker wonderment, and combine that contrast with the fact that I couldn’t think of anything else to write about, then concatenate that combined contrast with my contrarian character and you’ve got yourself a blog post telling you that maybe Web Workers are a teeny-tiny bit overhyped.

Thanks for reading

If you liked this post, share it with all of your programming buddies!

Follow us on Facebook | Twitter

Further reading

An Introduction to Web Workers

JavaScript Web Workers: A Beginner’s Guide

Using Web Workers to Real-time Processing

How to use Web Workers in Angular app

Using Web Workers with Angular CLI