[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/FunctionScript/functionscript-server/master/README.md [Back]  [Original]

# functionscript-server

A simple Heroku-based example of [FunctionScript](https://github.com/FunctionScript/FunctionScript) API Development

[![FunctionScript](./static/images/fs-wordmark.png)](https://github.com/FunctionScript/FunctionScript)

## Introduction

You can use this repository to build a brand new [FunctionScript](https://github.com/FunctionScript/FunctionScript)
API in a flash. It's the simplest way to build an API, routing is handled
automatically by the file structure. HTTP GET and POST requests are handled
identically, treating each endpoint with a more **imperative**, or command-based
approach. Instead of `HTTP POST /users/` to create a user resource, for example,
we'd recommend a `/createUser/` endpoint.

A simple summary is that all JavaScript files in the `./functions` folder will
be automatically turned into API endpoints with:

1. Automatic type coercion
2. Automatic error handling
3. Differentiation between required / optional parameters
4. Embedded support for API Schemas

The example contained here is intended for deployment on Heroku, and used the
[FunctionScript](https://github.com/FunctionScript/FunctionScript) Daemon and Gateway to handle requests.
Though [FunctionScript](https://github.com/FunctionScript/FunctionScript)
is primarily intended to be used alongside serverless compute models, including
being the primary development model for [Standard Library](https://stdlib.com) APIs,
the Gateway is easily manipulable to be able to handle requests on a standalone
server -- and [Heroku](https://heroku.com) is the best place to get started.

## Installation

You can deploy to Heroku *instantly* by clicking the button below.

[![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy)

If you're feeling adventurous, you can set up the server locally by following
these instructions:

```shell
$ git clone git@github.com:FunctionScript/functionscript-server.git
$ cd functionscript-server
$ npm install
$ npm start
```

That's it! You're done!


## API Endpoints

There are four API endpoints that serve different purposes to introduce
you to the basics of [FunctionScript](https://github.com/FunctionScript/FunctionScript)

### `./function/__main__.js`

Reads `./pages/index.html` and uses it as a simple HTML template. In this example,
you're introduced to the `{object.http}` type for returning HTTP responses. The
default response type is JSON.

```javascript
const fs = require('fs');
const PAGE = fs.readFileSync('./pages/index.html').toString();

/**
* This API endpoint simply serves a static HTML page.
*   It replaces all instances of `{%varname}` with corresponding GET / POST
*   parameters. The default is to respond to the `name` parameter
* @param {string} name A name to enter.
* @returns {object.http} response The HTTP response
*/
module.exports = async (name = 'world', context) => {
  let render = PAGE.replace(/\{\%([\w]+)\}/gi, ($0, $1) => context.params[$1]);
  return {
    headers: {
      'Content-Type': 'text/html'
    },
    body: render
  };
};
```

### `./functions/add.js`

Adds two numbers together with a hello message, returns JSON string. This example
shows you the built-in FuncitonScript error-handling using required parameters.

```javascript
/**
* A simple API that adds two numbers together. `a` and `b` are required parameters
*   and the API will automatically throw an error if they are not provided
* @param {string} name A name to enter.
* @param {number} a The first of two numbers to add.
* @param {number} b The second of two numbers to add.
* @returns {string} message A simple hello mesage
*/
module.exports = async (name = 'world', a, b) => {
  return `Hello ${name}, ${a} + ${b} = ${a + b}!`;
};
```

### `./functions/sms.js`

Uses [utils/sms](https://stdlib.com/@utils/lib/sms) to send an SMS message.
This example shows off a little of the power of [Standard Library](https://stdlib.com),
a central registry of [FunctionScript](https://github.com/FunctionScript/FunctionScript)-powered
APIs. You can obtain your own `STDLIB_SECRET_TOKEN` by registering on
Standard Library.

```javascript
const lib = require('lib')({token: process.env.STDLIB_SECRET_TOKEN});

/**
* Sends an SMS using https://stdlib.com/@utils/lib/sms
* @param {string} tel A telephone number to send SMS to
* @param {string} body A message body to send
* @returns {object} result The result of the SMS
*/
module.exports = async (tel, body) => {
  let result = await lib.utils.sms['@1.0.11']({
    to: tel,
    body: `Testing from FunctionScript Server:\n${body}`
  });
  return result;
};
```

### `./functions/static/__notfound__.js`

Routes static resources from `./static/` directory based on pathname. This
is a comprehensive example that reads from multiple different folders
and serves different HTTP headers (`"Content-Type"`) based on the filenames
it's serving. It is also an introduction to the `__notfound__.js` handler.

```javascript
const mime = require('mime');

const fileio = require('../../helpers/fileio.js');

let filepath = './static';
let staticFiles = fileio.readFiles(filepath);

/**
 * This endpoint handles all routes to `/static` over HTTP, and maps them to the
 *  `./static` directory (part of the root dir)
 * It is a special example of a "NOT FOUND" handler, which any request not
 *   matching an existing functions/ API endpoint will be routed to
 * `context.path` can be used to retrieve the path name
 * @returns {object.http}
 */
module.exports = async (context) => {

  // Hot reload for local development
  if (process.env.NODE_ENV !== 'release') {
    staticFiles = fileio.readFiles(filepath);
  }

  let pathEnd = context.path.slice().pop();
  let staticFilepath = context.path.slice(1).join('/');
  let file = pathEnd.indexOf('.') !== -1
    ? staticFiles[staticFilepath]
    : (
      staticFiles[[staticFilepath, 'index.htm'].filter(v => !!v).join('/')] ||
      staticFiles[[staticFilepath, 'index.html'].filter(v => !!v).join('/')]
    );

  if (!file) {
    return {
      statusCode: 404,
      body: '404 - Not Found',
      headers: {
        'Content-Type': 'text/plain'
      }
    };
  }

  let cacheControl = process.env.NODE_ENV === 'release'
    ? 'max-age=31536000'
    : 'max-age=0';

  return {
    statusCode: 200,
    body: Buffer.from(file),
    headers: {
      'Content-Type': mime.getType(staticFilepath),
      'Cache-Control': cacheControl
    }
  };
};
```

## Acknowledgements

Thanks to those who helped with the example!

### Contributors

- [Steve Meyer](https://twitter.com/notoriaga)
- [Keith Horwood](https://twitter.com/keithwhor)

FunctionScript is (c) 2019 Polybit Inc.

Web Proxy Viewer  |  New URL  |  Original Page