{"id":19410083,"url":"https://github.com/nicolasdao/webfunc","last_synced_at":"2025-04-06T12:07:31.042Z","repository":{"id":81202315,"uuid":"97034598","full_name":"nicolasdao/webfunc","owner":"nicolasdao","description":"Universal Serverless Web Framework. Write Express apps ready to be deployed to Zeit-Now, Google Cloud Functions (incl. functions reacting to Pub/Sub topics or Storage changes), and AWS Lambdas.","archived":false,"fork":false,"pushed_at":"2024-10-24T11:52:44.000Z","size":562,"stargazers_count":73,"open_issues_count":0,"forks_count":4,"subscribers_count":6,"default_branch":"master","last_synced_at":"2024-10-25T06:13:47.645Z","etag":null,"topics":["aws-lambda","functions","google-functions","neap","pubsub","serverless","topic","zeit"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nicolasdao.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-07-12T17:24:04.000Z","updated_at":"2024-10-24T11:52:48.000Z","dependencies_parsed_at":null,"dependency_job_id":"3a2be2d5-b10e-4639-9200-d6be46d553de","html_url":"https://github.com/nicolasdao/webfunc","commit_stats":{"total_commits":287,"total_committers":2,"mean_commits":143.5,"dds":0.04181184668989546,"last_synced_commit":"5ed6edc95810ad379f6243a49c435f5f8fe761ad"},"previous_names":[],"tags_count":114,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nicolasdao%2Fwebfunc","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nicolasdao%2Fwebfunc/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nicolasdao%2Fwebfunc/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nicolasdao%2Fwebfunc/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nicolasdao","download_url":"https://codeload.github.com/nicolasdao/webfunc/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247478318,"owners_count":20945266,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aws-lambda","functions","google-functions","neap","pubsub","serverless","topic","zeit"],"created_at":"2024-11-10T12:14:45.687Z","updated_at":"2025-04-06T12:07:31.021Z","avatar_url":"https://github.com/nicolasdao.png","language":"JavaScript","readme":"__WARNING__:\n```\nThis project is no longer supported. If you still plan on using this project, be aware that it does not support Zeit Now \u003e= 2.0. We are currently working on a replacement project that we hope to release very soon. Thank you for your support. \n\nThe Neap Team\n```\n\n# WebFunc \u0026middot;  [![NPM](https://img.shields.io/npm/v/webfunc.svg?style=flat)](https://www.npmjs.com/package/webfunc) [![Tests](https://travis-ci.org/nicolasdao/webfunc.svg?branch=master)](https://travis-ci.org/nicolasdao/webfunc) [![License](https://img.shields.io/badge/License-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause)\n__*Universal Serverless Web Framework*__. Write code for serverless similar to [Express](https://expressjs.com/) once, deploy everywhere (thanks to the awesome [Zeit Now-CLI](https://zeit.co/now)). This also include creating functions that can get triggered by [Google Cloud PubSub topics](https://cloud.google.com/pubsub/docs/overview). Targeted platforms:\n- [__*Zeit Now*__](https://zeit.co/now) (using express under the hood)\n- [__*Google Cloud Functions*__](https://cloud.google.com/functions/) (incl. reacting to [__*Pub/Sub events*__](reacting-to-google-pubsub-topics) or __*Storage changes*__)\n- [__*AWS Lambdas*__](https://aws.amazon.com/lambda)\n- [__*Azure Functions*__](https://azure.microsoft.com/en-us/services/functions/) (COMING SOON...)\n\n```js\nconst { app } = require('webfunc')\n\napp.get('/users/:username', (req, res) =\u003e res.status(200).send(`Hello ${req.params.username}`))\n\neval(app.listen('app', 4000))\n```  \n\n__*[Webfunc](https://github.com/nicolasdao/webfunc) supports [Express middleware](#compatible-with-all-express-middleware)*__. \n\nForget any external dependencies to run your serverless app locally. Run `node index.js` and that's it. \n\nOut-of-the-box features include:\n- [_Routing_](#basic)\n- [_CORS_](#cors)\n- [_Body \u0026 Route Variables Parsing_](#multiple-endpoints)\n- [_Environment Variables Per Deployment_](#managing-environment-variables-per-deployment) \n- [_Middleware (incl. Express)_](#compatible-with-all-express-middleware)\n\n# Table of Contents\n\n\u003e * [Install](#install) \n\u003e * [How To Use It](#how-to-use-it) \n\u003e   - [Basic - Build Once Deploy Everywhere](#basic---build-once-deploy-everywhere)\n\u003e   - [Passing Parameters To Your HTTP Endpoint](#passing-parameters-to-your-http-endpoint)\n\u003e   - [Webfunc Properties On The Request Object](#webfunc-properties-on-the-request-object)\n\u003e   - [Deploying To Google Functions or AWS](#deploying-to-google-functions-or-aws)\n\u003e * [Examples](#examples) \n\u003e   - [Creating A REST API](#creating-a-rest-api)\n\u003e   - [Compatible With All Express Middleware](#compatible-with-all-express-middleware)\n\u003e   - [Managing Environment Variables Per Deployment](#managing-environment-variables-per-deployment)\n\u003e   - [Intercepting The res.send() Method](#intercepting-the-res.send-method)\n\u003e   - [Reacting To Google PubSub Topics](#reacting-to-google-pubsub-topics)\n\u003e   - [Authentication](#authentication) \n\u003e   - [Uploading Files \u0026 Images](#uploading-files--images)\n\u003e   - [GraphQL](#graphql)\n\u003e * [Configuration](#configuration)\n\u003e   - [CORS](#cors) \n\u003e   - [Disabling Body Or Route Parsing](#disabling-body-or-route-parsing)\n\u003e   - [Customizing The req.params Property](#customizing-the-reqparams-property)\n\u003e * [Tips \u0026 Tricks](#tips--tricks)\n\u003e   - [Dev - Easy Hot Reloading](#dev---easy-hot-reloading)\n\u003e   - [Dev - Better Deployments With now-flow](#dev---better-deployments-with-now-flow)\n\u003e * [FAQ](#faq)\n\u003e * [Annexes](#annexes)\n\u003e   - [A.1. CORS Refresher](#a1-cors-refresher)\n\u003e   - [A.2. CORS Basic Errors](#a2-cors-basic-errors)\n\u003e * [Contributing](#contributing)\n\u003e * [About Neap](#this-is-what-we-re-up-to)\n\u003e * [License](#license)\n\n\n# Install\n## Prerequisites\n\u003e To run your function outside of your local machine, you will need to install Zeit Now.\n\u003e The lastest stable version of now that works is @9.0.0. We're currently working on fixing the issues with more recent versions of now.\n\n```\nnpm install now@9.0.0 -g\n```\n## Install Webfunc\n\n```\nnpm install webfunc --save\n```\n\n# How To Use It\n## Basic - Build Once Deploy Everywhere\n\u003e To deploy your app to any serverless solution, first make sure you have installed [_Zeit Now_](https://zeit.co/now) globally:\n```\nnpm install now -g\n```\n\n__*1. Create an index.js:*__\n\n```js\nconst { app } = require('webfunc')\n\napp.get('/users/:username', (req, res) =\u003e res.status(200).send(`Hello ${req.params.username}`))\n\neval(app.listen('app', 4000))\n```  \n\u003eMore details on why `eval` is used under [What does webfunc use eval() to start the server?](#what-does-webfunc-use-eval-to-start-the-server) in the [FAQ](#faq).\n\n__*2. Add a \"start\" script in your package.json*__\n```js\n  \"scripts\": {\n    \"start\": \"node index.js\"\n  }\n```\n\n__*3.A. Deploy locally without any other dependencies*__\n```\nnpm start\n```\n\n__*3.B. Deploy to Zeit Now*__\n```\nnow\n```\n\n__*3.C. Deploy to Google Cloud Function*__\n\nAdd a __*now.json*__ file similar to the following:\n```js\n{\n  \"gcp\": {\n    \"memory\": 128,\n    \"functionName\": \"webfunctest\",\n    \"timeout\": \"100s\"\n  },\n  \"environment\":{\n    \"active\": \"staging\",\n    \"default\":{\n      \"hostingType\": \"localhost\"\n    },\n    \"staging\":{\n      \"hostingType\": \"gcp\"\n    }\n  }\n}\n```\nRun this command:\n```\nnow gcp\n```\n\nThe `environment.active = \"staging\"` indicates that the configuration for your app is inside the `environment.staging` property. There, you can see `\"hostingType\": \"gcp\"`. Webfunc uses the `hostingType` property to define how to serve your app (this is indeed different from platform to platform. Trying to deploy a `\"hostingType\": \"gcp\"` to Zeit Now will fail). \n\n\u003eNOTE: Weird timeout convention\n\u003e Notice that the timeout used in for gcp is in seconds not millisends. Also, you have to specify the unit at the end: `\"timeout\": \"100s\"`.\n\n__*3.D. Deploy to Google Cloud Function For Pub/Sub or Storage Based Triggers or Deploy to AWS Lambda*__\n\nYou will need to enhance the _now-CLI_ capabilities by adding a dev dependency called [__*now-flow.js*__](#dev---better-deployments-with-now-flow). An example if available in section [Google Pub/Sub Topic \u0026 Storage Trigger Based Functions](#google-pubsub-topic--storage-trigger-based-functions).\n\n\u003eHIGHLY RECOMMENDED - USE __*now-flow.js*__ TO MANAGE YOUR DEPLOYMENTS  \n\u003eWithout [__*now-flow.js*__](#dev---better-deployments-with-now-flow), you won't be able to deploy to AWS or to GCP using a Pub/Sub topic trigger. _now-flow.js_ is not just about adding other deployment options to _webfunc_. It also tremendously helps to [managing environment variables per deployment](#managing-environment-variables-per-deployment)).\n\n## Passing Parameters To Your HTTP Endpoint\nWhether your passing parameters in the URL route, the querystring, or the HTTP body, webfunc will parse those arguments and store them into as a JSON in the `req.params` property. Here is an example of a GET to the following URL: _https://yourapp.com/users/frank?lastname=fitzerald_\n\n```js\napp.get('/users/:username', (req, res) =\u003e res.status(200).send(`Hello ${req.params.username} ${req.params.lastname}`))\n```\n\n## Webfunc Properties On The request Object\nThe first operation made by webfunc when it receives a request is to add 3 properties on the __*request*__ object:\n* `__receivedTime`: Number that milliseconds since epoc when the request reaches the server.\n* `__transactionId`: String representing a unique identifier (e.g. useful for tracing purposes).\n* `__ellapsedMillis`: Function with no arguments returning the number of milliseconds ellapsed since `__receivedTime`.\n\n## Deploying To Google Functions or AWS\n\u003e IMPORTANT: Before deploying to Google Functions (GCP), YOU'LL HAVE TO ENABLE BILLING under the specific project hosting your function. Simply browse to your account ([https://console.cloud.google.com/](https://console.cloud.google.com/)), click on the _Cloud Functions_, and then click on _Enable Billing_.\n\nYou will need to enhance the _now-CLI_ capabilities by adding a dev dependency called [__*now-flow.js*__](#dev---better-deployments-with-now-flow). An example if available in section [Google Pub/Sub Topic \u0026 Storage Trigger Based Functions](#google-pubsub-topic--storage-trigger-based-functions).\n\nBefore deploying to GCP or AWS, you'll have to login first using:\n\n```\nnow gcp login\n```\n\nor\n\n```\nnow aws login\n```\n\nAfter using one of the above command, you'll be prompt to select a project within your GCP or AWS account. Once selected, your function will be deployed withing that project. \n\nTo change to another project, re-rerun the commands above.\n\n\u003eHIGHLY RECOMMENDED - USE __*now-flow.js*__ TO MANAGE YOUR DEPLOYMENTS  \n\u003eWithout [__*now-flow.js*__](#dev---better-deployments-with-now-flow), you won't be able to deploy to AWS or to GCP using a Pub/Sub topic trigger. _now-flow.js_ is not just about adding other deployment options to _webfunc_. It also tremendously helps to [managing environment variables per deployment](#managing-environment-variables-per-deployment)).\n\n\n# Examples\n## Creating A REST API\n\u003eA REST api is cool but GraphQL is even cooler. Check out how you can create a [GraphQL api](#graphql) in less than 2 minutes [__*here*__](#graphql).\n### Single Endpoint\n_index.js:_\n\n```js\nconst { app } = require('webfunc')\n\napp.get('/users/:username', (req, res) =\u003e res.status(200).send(`Hello ${req.params.username}`))\n\neval(app.listen('app', 4000))\n```  \n\nTo run this code locally, simply run in your terminal:\n```\nnode index.js\n```\n\n\u003e To speed up your development, use [_hot reloading_](#dev---easy-hot-reloading) as explained in the [Tips \u0026 Tricks](#tips--tricks) section below.\n\n### Multiple Endpoints\n```js\nconst { app } = require('webfunc')\n\n// 1. Simple GET method. \napp.get('/users/:username', (req, res) =\u003e \n  res.status(200).send(`Hello ${req.params.username}`))\n\n// 2. GET method with more variables in the route. The conventions are the same as\n//    the 'path-to-regex' module (https://github.com/pillarjs/path-to-regexp).\napp.get('/users/:username/account/:accountId', (req, res) =\u003e \n  res.status(200).send(`Hello ${req.params.username} (account: ${req.params.accountId})`))\n\n// 3. Support for multiple routes pointing to the same action.\napp.get(['/companies/:companyName', '/organizations/:orgName'], (req, res) =\u003e \n  res.status(200).send(req.params.companyName ? `Hello company ${req.params.companyName}` : `Hello organization ${req.params.orgName}`))\n\n// 4. Supports all http verbs: GET, POST, PUT, DELETE, HEAD, OPTIONS.\napp.post('/login', (req, res, params={}) =\u003e {\n  if (req.params.username == 'nic' \u0026\u0026 req.params.password == '123')\n    res.status(200).send(`Welcome ${req.params.username}.`)\n  else\n    res.status(401).send('Invalid username or password.')\n})\n\n// 5. Supports route for any http verb.\napp.all('/', (req, res) =\u003e res.status(200).send('Welcome to this awesome API!'))\n\neval(app.listen('app', 4000))\n``` \n\nNotice that in all the cases above, the `req.params` argument contains any parameters that are either passed in the route or in the payload. This scenario is so common that webfunc automatically supports that feature. No need to install any middleware like [body-parser](https://github.com/expressjs/body-parser). Webfunc can even automatically parse __*multipart/form-data*__ content type usually used to upload files (e.g. images, documents, ...). More details under [Uploading Images](#uploading-files--images) in the [Use Cases](#use-cases) section.\n\nBased on certain requirements, it might be necessary to disable this behavior. To do so, please refer to [Disabling Body Or Route Parsing](#disabling-body-or-route-parsing) under the [Configuration](#configuration) section.\n\n## Compatible With All Express Middleware\nThat's probably one of the biggest advantage of using webfunc. [Express](https://expressjs.com/) offers countless of open-sourced [middleware](https://expressjs.com/en/resources/middleware.html) that would not be as easily usable in a FaaS environment without webfunc. You can for example use the code you're to write in Express to write functions that react to a Google Cloud Pub/Sub topic.\n\nNext, we'll demonstrate 4 different basic scenarios:\n1. [Using An Express Middleware Globally](#using-an-express-middleware-globally)\n2. [Using An Express Middleware On a Specific Endpoint Only](#using-an-express-middleware-on-a-specific-endpoint-only)\n3. [Creating Your Own Middleware](#creating-your-own-middleware)\n4. [Chaining Multiple Middleware On a Specific Endpoint](#chaining-multiple-middleware-on-a-specific-endpoint)\n\n### Using An Express Middleware Globally\n```js\nconst { app } = require('webfunc')\nconst responseTime = require('response-time')\n\napp.use(responseTime())\n\napp.get('/users/:username', (req, res) =\u003e \n  res.status(200).send(`Hello ${req.params.username}`))\n\napp.get('/users/:username/account/:accountId', (req, res) =\u003e \n  res.status(200).send(`Hello ${req.params.username} (account: ${req.params.accountId})`))\n\neval(app.listen('app', 4000))\n```\n\nThe snippet above demonstrate how to use the Express middleware [response-time](https://github.com/expressjs/response-time). This middleware measures the time it takes for your server to process a request. It will add a new response header called __X-Response-Time__. In this example, all APIs will be affected. \n\n### Using An Express Middleware On a Specific Endpoint Only\nSimilar to Express, webfunc allows to target APIs specifically:\n\n```js\nconst { app } = require('webfunc')\nconst responseTime = require('response-time')\n\napp.get('/users/:username', responseTime(), (req, res) =\u003e \n  res.status(200).send(`Hello ${req.params.username}`))\n\napp.get('/users/:username/account/:accountId', (req, res) =\u003e \n  res.status(200).send(`Hello ${req.params.username} (account: ${req.params.accountId})`))\n\neval(app.listen('app', 4000))\n```\n\nIn the snippet above, the _response-time_ will only affect the first API.\n\n### Creating Your Own Middleware\nObviously, you can also create your own middleware the exact same way you would have done it with Express, which means you'll also be able to use it with Express:\n\n```js\nconst { app } = require('webfunc')\n\nconst authenticate = (req, res, next) =\u003e {\n  if (!req.headers['Authorization'])\n    res.status(401).send(`Missing 'Authorization' header.`)\n  next()\n}\n\napp.use(authenticate)\n\napp.get('/users/:username', (req, res) =\u003e \n  res.status(200).send(`Hello ${req.params.username}`))\n\napp.get('/users/:username/account/:accountId', (req, res) =\u003e \n  res.status(200).send(`Hello ${req.params.username} (account: ${req.params.accountId})`))\n\neval(app.listen('app', 4000))\n```\n\n### Chaining Multiple Middleware On a Specific Endpoint\nFor more complex scenario, you may need to chain multiple middleware differently depending on the endpoint:\n\n```js\nconst { app } = require('webfunc')\n\nconst doSomething = (req, res, next) =\u003e {\n  // Notice that it might be wise to take some precaution and not override the 'req.params'\n  // property, which might have been used a previous middleware.\n  if (!req.params || typeof(req.params) != 'object')\n    req.params = {}\n  Object.assign(req.params, { part_1: 'nice' })\n  next()\n}\n\nconst doSomethingElse = (req, res, next) =\u003e {\n  if (!req.params || typeof(req.params) != 'object')\n    req.params = {}\n  Object.assign(req.params, { part_2: 'to see you' })\n  next()\n}\n\n// One way to chain:\napp.get('/users/:username', doSomething, doSomethingElse, (req, res) =\u003e \n  res.status(200).send(`Hello ${req.params.username}, ${req.params.part_1} ${req.params.part_2}`))\n\n// Another way to chain:\nconst middleware = [doSomething, doSomethingElse]\n\napp.get('/', ...middleware.concat((req, res) =\u003e \n  res.status(200).send(`Hello ${req.params.part_1} ${req.params.part_2}`)))\n\neval(app.listen('app', 4000))\n```\n\n## Managing Environment Variables Per Deployment\nThe following code allows to access the current active environment's variables:\n\n```js\nconst { appConfig } = require('webfunc')\nconsole.log(appConfig.myCustomVar) // \u003e \"Hello Default\"\n```\n_**appConfig**_ is the configuration inside the _**now.json**_ under the _**active environment**_ (the _active environment_ is the value of the `environment.active` property).\n\nHere is an example of a typical _now.json_ file:\n\n```js\n{\n  \"environment\": {\n    \"active\": \"default\",\n    \"default\": {\n      \"hostingType\": \"localhost\",\n      \"myCustomVar\": \"Hello Default\"\n    },\n    \"staging\": {\n      \"hostingType\": \"gcp\",\n      \"myCustomVar\": \"Hello Staging\"\n    },\n    \"production\": {\n      \"hostingType\": \"now\",\n      \"myCustomVar\": \"Hello Prod\"\n    }\n  }\n}\n```\n\nAs you can see, the example above demonstrates 3 different types of environment setups: `\"default\"`, `\"staging\"`, `\"prod\"`. You can obviouly define as many as you want, and add whatever you need under those environments. Since the value of the `environment.active` is `\"default\"` in this example, the value of the _**appConfig**_ object is:\n```js\n{\n  \"hostingType\": \"localhost\",\n  \"myCustomVar\": \"Hello Default\"\n}\n```\n## Intercepting The res.send() Method\nWebfunc adds support for listeners on the following 3 response events:\n- __*sending*__ a response.\n- __*setting the headers*__ of a response.\n- __*setting the status*__ of a response. \n\n```js\nconst { app } = require('webfunc')\n\napp.on('send', (req, res, val) =\u003e console.log(val))\napp.on('headers', (req, res, ...args) =\u003e console.log(args))\napp.on('status', (req, res, val) =\u003e console.log(val))\n\napp.get('/users/:username', (req, res) =\u003e {\n  res.set('x-special', 'magic header')\n  res.status(200).send(`Hello ${req.params.username}`)\n})\n\neval(app.listen('app', 4000))\n```\n\n## Reacting To Google PubSub Topics\n\nOne of the goals of Webfunc was to provide a uniform API for building serverless functions. Serverless functions can react to more than HTTP requests, and in this section, we will demonstrate how to build and deploy a Google Cloud Function that can react to messages published to a Google PubSub topic. This section is broken down in 3 parts:\n1. [Intro](#intro) - Here we will show how to test a function locally, deploy it to Google Cloud, and then manually test it by publishing a message on a topic.\n2. [Minimum Message Requirement](#minimum-message-requirement) - In this section, we will briefly explain the requirement for the messages that can be published so that the function can react to them.\n3. [Programmatically Publish To Google PubSub](#programmatically-publish-to-google-pubsub) - Though this is not really relevant to Webfunc, it is still something we expect any coder will eventually do, so we thought that a little help might be useful.\n\n#### Intro\n\n\u003ePREREQUISITES:\n\u003e\n\u003e__CREATING A GOOGLE CLOUD PUBSUB TOPIC__\n\u003e\n\u003eBefore you can start deploying a webfunc function to Google Cloud that can react to a Google PubSub topic, you will have to do the following:\n\u003e1. Create a Google Cloud Account or logging to your Google Cloud console ([https://console.cloud.google.com](https://console.cloud.google.com)). Don't worry, it is free!\n\u003e2. Create a new project, or select an existing one.\n\u003e3. Enable billing on both Google Cloud Function and Google Cloud PubSub (simply click on those items in the menu and if the billing has not been turned on, then a pop up will appear with a simple button \"enable billing\". Those services are free unless you exceed the free quota).\n\u003e4. Browse to Google PubSub and create your first topic called 'hello-webfunc'.\n\u003e\n\u003e__INSTALL NOW-FLOW FOR EASIER DEPLOYMENTS TO GOOGLE CLOUD__\n\u003e\n\u003e[__*now-flow*__](https://github.com/nicolasdao/now-flow) automates your Zeit Now deployments and will decrease the configuration setup. Simply run `npm install now-flow --save-dev`.\n\nBecause Express has become such a familiar tool, our team decided to embrace its API conventions, even for functions reacting to events other than HTTP requests. If you're interested in seeing an example of how you would build a normal function using the standard Google API, please refer to [this documentation](https://cloud.google.com/functions/docs/tutorials/pubsub). The other reason we found it was very useful to use an Express convention for building any function is testability. Indeed, because Webfunc treats any event as a web request, it is very easy to test your function locally using a standard HTTP POST containing the event payload. We will demonstrate this by creating a simple email notification function that reacts to messages dropped on the Google Cloud PubSub topic we created in the prerequiste steps:\n\n1. Configure [now](https://zeit.co/now) to use the Google project we defined in the prerequisite steps: `now gcp login` (once you're logged in, you'll be asked to choose a project in your CLI. Choose the project we created in the prerequisite steps)\n2. Create a new npm project: `npm init`\n3. Install _nodemailer_ to send a dummy email: `npm install nodemailer --save`\n4. Create a new `index.js` as follow:\n  ```js\n  const { app } = require('webfunc')\n  const nodemailer = require('nodemailer');\n\n  app.post('/sayhi', (req, res) =\u003e {\n    // STEP 1. Get payload\n    const { to, subject, message } = req.params\n\n    // STEP 2. Send email\n    // Generate test SMTP service account from ethereal.email\n    // Only needed if you don't have a real mail account for testing\n    nodemailer.createTestAccount((err, account) =\u003e {\n      // create reusable transporter object using the default SMTP transport\n      let transporter = nodemailer.createTransport({\n        host: 'smtp.ethereal.email',\n        port: 587,\n        secure: false, // true for 465, false for other ports\n        auth: {\n          user: account.user, // generated ethereal user\n          pass: account.pass // generated ethereal password\n        }\n      })\n\n      // setup email data with unicode symbols\n      let mailOptions = {\n        from: '\"Webfunc 👻\" \u003cwebfunc-example@googlepubsub.com\u003e', // sender address\n        to: to,\n        subject: subject,\n        html: message\n      }\n\n      // send mail with defined transport object\n      transporter.sendMail(mailOptions, (error, info) =\u003e {\n        if (error) {\n          console.error(error)\n          res.status(500).send(error)\n        }\n        else {\n          const msg = `Message sent: ${info.messageId}\\nPreview URL: ${nodemailer.getTestMessageUrl(info)}`\n          console.log(msg)\n          res.status(200).send(msg)\n        }\n      })\n    })\n  })\n\n  eval(app.listen('app', 4000))\n  ```\n\n\u003eIMPORTANT: When it comes to create a webfunc function reacting to events other than HTTP requests, only POST  methods are allowed.\n\n5. Start the server locally to start testing this code: `node index.js`\n6. Test this code locally: \n  ```\n  curl -X POST -H 'Content-Type: application/json' -d '{\"to\":\"hello@webfunc.co\",\"subject\":\"PubSub with Webfunc\", \"message\": \"\u003cb\u003ePubSub with Webfunc is easy mate!!!\u003c/b\u003e\"}' http://localhost:4000/sayhi\n  ```\n   \n  This will return a message similar to:\n  ```\n  Message sent: \u003c6846cab2-a1fc-6310-cbba-d85159acf1cd@googlepubsub.com\u003e\n  Preview URL: https://ethereal.email/message/WpuwmIPHUAoowF06WpufewfwpxLDDPAAAAAYyFpXXAobCBPcRN5IvxKV0\n  ```\n\n  Copy/paster the Preview URL in your browser to visualise your test email.\n\n\nWhat the above achieved is nothing different from what we've been demonstrating previously. Let's deploy this code to a Google Cloud Function that should be responding to messages published to the topic we created in the prerequisite steps above:\n\n1. Create a new `now.json` file as follow:\n  ```js\n  {\n    \"environment\": {\n      \"active\": \"default\",\n      \"default\": {\n        \"hostingType\": \"localhost\"\n      },\n      \"prod\": {\n        \"hostingType\": \"gcp\",\n        \"gcp\": {\n          \"functionName\": \"webfunc-pubsub\",\n          \"memory\": 128,\n          \"trigger\": {\n            \"type\": \"cloud.pubsub\",\n            \"topic\": \"hello-webfunc\"\n          }\n        }\n      }\n    }\n  }\n  ```\n\n  This config file defines a production environment `prod` that contains the details of the Google Cloud Function we want to deploy as well as the PubSub topic we want it to react to (i.e. `hello-webfunc` created in the prerequisite steps).\n\n2. Add a deployment script in the `package.json` as follow:\n  ```js\n  \"scripts\": {\n    \"deploy\": \"nowflow prod\"\n  }\n  ```\n\n3. Deploy your function: `npm run deploy`\n\n  \u003eWARNING: There seems to be a bug the first time you deploy. A `failed deployment` error might happen when in reality the deployment was successful. Double-check that your function has been created in your [Google Function console](https://console.cloud.google.com/functions).\n\n4. Publish a message to the `hello-webfunc` topic to see if this function reacts to it:\n  - Go to the [Google PubSub console](https://console.cloud.google.com/cloudpubsub).\n  - Select the `hello-webfunc` topic.\n  - Click on the __PUBLISH MESSAGE__ button.\n  - Add the following 4 key/value pairs and then press __Publish__:\n\n  | Key      | Value                                         | \n  | :------- |:--------------------------------------------- | \n  | to       | whatever@gmail.com                            |\n  | subject  | Testing Webfunc PubSub                        |\n  | message  | `\u003ch1\u003eHi there\u003c/h1\u003e\u003cp\u003eWebfunc is awesome!\u003c/p\u003e` |\n  | pathname | sayhi                                         |\n\n  \u003eIMPORTANT - Notice that we need to add a new field called `pathname`. This is what allows to target a specific endpoint (in this case `app.post('/sayhi', (req, res) =\u003e { ... })`).\n\n5. Verify that this worked by checking the Google Function log. There, you should see the `Preview URL` and then browse there to verify that everything is working fine.\n\n#### Minimum Message Requirement\n\nAs we demonstrated in the example above, the structure of the published message can be anything __BUT IT MUST AT LEAST CONTAIN A *pathname* property. That `pathname` property__ is required to allow webfunc to identify which endpoint it needs to route that message to. \n\n#### Programmatically Publish To Google PubSub\n\n1. Get a JSON file containing a private key that allows to publish to the Google PubSub topic:\n  - Log to your Google Cloud project.\n  - Browse to the Service Accounts section under _IAM \u0026 admin/Service accounts_.\n  - Click on the __CREATE SERVICE ACCOUNT__ button. There give it a name, select the role _PubSub/PubSub Publisher_ and then tick the _Furnish a new private key_ (select JSON format) checkbox before clicking the CREATE button. \n  - Save that json file under your project and let's rename it `secrets.json` for the sake of this example.\n\n2. Install _@google-cloud/pubsub_: `npm install @google-cloud/pubsub --save`\n3. Create a new `publish.js` as follow:\n  ```js\n  const path = require('path')\n\n  const GOOGLE_PROJECT = 'your-google-project-name'\n  const TOPIC_NAME = 'hello-webfunc'\n  const SECRETS_PATH = path.join(process.cwd(), './secrets.json')\n\n  const pubsub = require('@google-cloud/pubsub')({\n    projectId: GOOGLE_PROJECT,\n    keyFilename: SECRETS_PATH\n  })\n\n  const topic = pubsub.topic(TOPIC_NAME)\n  const publisher = topic.publisher()\n  const emptyBuffer = Buffer.from('')\n\n  const publish = (data={}) =\u003e new Promise((onSuccess, onFailure) =\u003e publisher.publish(emptyBuffer, data, (err, res) =\u003e err ? onFailure(err) : onSuccess(res)))\n\n  publish({\n    to: 'hello@webfunc.com',\n    subject: 'Webfunc ♥ Google PubSub',\n    message: '\u003ch1\u003eLove Story\u003c/h1\u003e\u003cb\u003eWebfunc and Google PubSub rock together.\u003c/b\u003e',\n    pathname: 'sayhi'\n  })\n  .then(() =\u003e console.log(`Great!!! Message sent!`))\n  .catch(err =\u003e console.error(`Dammit! Somthing went wrong:\\n${err.message}\\n${err.stack}`))\n  ```\n\n4. Excute this code: `node publish.js`\n\n## Authentication \nAuthentication using webfunc is left to you. That being said, here is a quick example on how that could work using the awesome [passport](http://passportjs.org/) package. The following piece of code for Google Cloud Functions exposes a _signin_ POST endpoint that expects an email and a password and that returns a JWT token. Passing that JWT token in the _Authorization_ header using the _bearer_ scheme will allow access to the _/_ endpoint.\n\n```js\nconst { app } = require('webfunc')\nconst jwt = require('jsonwebtoken')\nconst passport = require('passport')\nconst { ExtractJwt, Strategy } = require(\"passport-jwt\")\n\nconst SECRETKEY = 'your-super-secret-key'\nconst jwtOptions = {\n  jwtFromRequest: ExtractJwt.fromAuthHeaderWithScheme('bearer'),\n  secretOrKey: SECRETKEY\n}\n\npassport.use(new Strategy(jwtOptions, (decryptedToken, next) =\u003e {\n  // do more verification based on your requirements.\n  return next(null, decryptedToken)\n}))\n\nconst authenticate = () =\u003e (req, res, next) =\u003e passport.authenticate('jwt', (err, user) =\u003e {\n  if (user) {\n    // Notice that it might be wise to take some precaution and not override the 'req.params'\n    // property, which might have been used a previous middleware.\n    if (!req.params || typeof(req.params) != 'object')\n      req.params = {}\n    Object.assign(req.params, { user })\n    next()\n  }\n  else\n    res.status(401).send(`You must be logged in to access this endpoint!`)\n})(req, res)\n\n// This api does not require authentication. It is used to acquire the bearer token.\napp.post('/signin', (req, res) =\u003e {\n  if (req.params.email == 'hello@webfunc.co' \u0026\u0026 req.params.password == 'supersecuredpassword') {\n    const user = {\n      id: 1,\n      roles: [{\n        name: 'Admin',\n        company: 'neap pty ltd'\n      }],\n      username: 'neapnic',\n      email: 'hello@webfunc.co'\n    }\n    res.status(200).send({ message: 'Successfully logged in', token: jwt.sign(user, SECRETKEY) })\n  }\n  else\n    res.status(401).send(`Username or password invalid!`) \n})\n\n// This api requires authentication. You must pass a header names \"Authorization\"\napp.get('/', authenticate(), (req, res) =\u003e res.status(200).send(`Welcome ${req.params.user.username}!`))\n\neval(app.listen('app', 4000))\n```\n\nTo test that piece of code:\n\n__*1. Login:*__\n```\ncurl -X POST -H 'Content-Type: application/json' -d '{\"email\":\"hello@webfunc.co\",\"password\":\"supersecuredpassword\"}' http://localhost:4000/signin\n```\nExtract the token received from this POST request and use it in the following GET request's header:\n\n__*2. Access the secured _/_ endpoint:*__\n```\ncurl -v -H \"Authorization: Bearer your-jwt-token\" http://localhost:4000\n```\n\n## Uploading Files \u0026 Images\n\nAs mentioned before, webfunc's default behavior is to [automatically extract the payload as well as the route variables](#multiple-endpoints) into a json object. This includes the request with a __*multipart/form-data*__ content type. In that case, an object similar to the following will be stored under `req.params.yourVariableName`:\n```js\n{\n  filename: \"filename.ext\",\n  mimetype: \"image/png\",\n  value: \u003cBuffer\u003e\n}\n```\n\nwhere\n- `filename` is a string representing the name of the uploaded file.\n- `mimetype` is a string representing the mimetype of the uploaded file (e.g. 'image/png').\n- `value` is a Buffer representing the uploaded file itself.\n\nHere is a code snippet that shows how to store the uploaded file locally:\n\n```js\nconst { app } = require('webfunc')\nconst path = require('path')\nconst fs = require('fs')\n\nconst save = (filePath, data) =\u003e new Promise((onSuccess, onFailure) =\u003e fs.writeFile(filePath, data, err =\u003e {\n  if (err) {\n    console.error(err)\n    onFailure(err)\n  }\n  else\n    onSuccess()\n}))\n\napp.post('/upload', (req, res) =\u003e \n  save(path.join(process.cwd(), req.params.myimage.filename), req.params.myimage.value)\n  .then(() =\u003e res.status(200).send('Upload successfull'))\n  .catch(err =\u003e res.status(500).send(err.message))\n)\n\neval(app.listen('app', 4000))\n```\n\nYou can test this code locally by using [Postman](https://www.getpostman.com/) as follow:\n\n\u003cimg src=\"https://raw.githubusercontent.com/nicolasdao/webfunc/master/docs/mutltipart.png\" alt=\"Neap Pty Ltd logo\" title=\"Neap\" /\u003e\n\n\n## GraphQL\n\nDeploying a GraphQL api as well as a GraphiQL Web UI to document and test that api has never been easier. GraphQL is beyond the topic of this document. You can learn all about it on the official webpage. \n\nTo create your own GraphQL api in a few minutes using webfunc, simply run those commands:\n```\ngit clone https://github.com/nicolasdao/graphql-universal-server.git\ncd graphql-universal-server\nnpm install\nnpm start\n```\n\nThis will locally host the following:\n\n- [http://localhost:4000](http://localhost:4000): This is the GraphQL endpoint that your client can start querying.\n- [http://localhost:4000/graphiql](http://localhost:4000/graphiql): This is the GraphiQL Web UI that you can use to test and query your GraphQL server. \n\nMore details about modifying this project for your own project [here](https://github.com/nicolasdao/graphql-universal-server).\n\nThe index.js of that project looks like this:\n\n```js\nconst { app } = require('webfunc')\nconst { graphqlHandler } = require('graphql-serverless')\nconst { transpileSchema } = require('graphql-s2s').graphqls2s\nconst { makeExecutableSchema } = require('graphql-tools')\nconst { glue } = require('schemaglue')\n\nconst { schema, resolver } = glue('./src/graphql')\n\nconst executableSchema = makeExecutableSchema({\n  typeDefs: transpileSchema(schema),\n  resolvers: resolver\n})\n\nconst graphqlOptions = {\n  schema: executableSchema,\n  graphiql: true,\n  endpointURL: '/graphiql',\n  context: {} // add whatever global context is relevant to you app\n}\n\n// The GraphQL api\napp.all(['/', '/graphiql'], graphqlHandler(graphqlOptions))\n\neval(app.listen('app', 4000))\n```\n\n# Configuration\n## CORS\n\u003eMore details about those headers in the [Annexes](#annexes) section under [A.1. CORS Refresher](#a1-cors-refresher).\n\nSimilar to body parsing, CORS (i.e. [Cross-Origin Resource Sharing](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS)) is a feature that is so often required that webfunc also supports it out-of-the box. That means that in most cases, the [Express CORS](https://github.com/expressjs/cors) middleware will not be necessary. \n\n```js\nconst { app, cors } = require('webfunc')\n\n// AJAX requests allowed from any domain.\nconst globalAccess = cors()\n\n// No AJAX requests allowed except from domain than 'https://example.com'.\nconst restrictedAccess = cors({\n  methods: ['GET'],\n  allowedHeaders: ['Authorization', 'Origin', 'X-Requested-With', 'Content-Type', 'Accept'],\n  origins: ['https://example.com']\n})\n\napp.get('/products/:id', globalAccess, (req, res) =\u003e res.status(200).send(`This is product ${req.params.id}`))\n// The 'OPTIONS' verb is required to allow preflight requests. Indeed, request with headers like 'Authorization' are not considered\n// simple requests (ref. https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS). The browser will then do what's called a \n// preflight request using the 'OPTIONS' http method to acquire more data on the server. If the server fails to respond to \n// that 'OPTIONS' request, the browser will abort the request.\napp.options('/users/:username', restrictedAccess)\napp.get('/users/:username', restrictedAccess, (req, res) =\u003e res.status(200).send(`Hello ${req.params.username}`))\n\neval(app.listen('app', 4000))\n```\n\n\u003eCORS is a classic source of headache. Though webfunc allows to easily configure any project, it will not prevent you to badly configure a project, and therefore loose a huge amount of time. For that reason, a series of common mistakes have been documented in the [Annexes](#annexes) section under [A.2. CORS Basic Errors](#a2-cors-basic-errors).\n\n## Disabling Body Or Route Parsing\nWebfunc's default behavior is to parse both the payload and any variables found in the route into a JSON object (see [previous example](#multiple-endpoints)). Based on certain requirements, it might be necessary to disable that behavior (e.g. trying to read the payload again in your app might not work after webfunc has parsed it). \n\nTo disable completely or partially that behavior, add a `\"params\"` property in the __*now.json*__ configuration file in the root of your application as follow: \n\n_Example of a now.json config that disable the payload parsing only:_\n```js\n{\n  \"params\": { mode: \"route\" }\n}\n```\n\nThat _mode_ property accepts 4 modes:\n- __*all*__: (default) Both the payload and the route variables are extracted.\n- __*route*__: Only the variables from the route are extracted. The payload is completely ignored.\n- __*body*__: Only the payload is parsed. The route variables are completely ignored.\n- __*none*__: Neither the payload nor the route variables are extracted.\n\nIf the _params_ property or the _mode_ are not defined in the _now.json_, then the default mode is _all_.\n\n## Customizing The req.params Property\nIf the `params` property conflicts with some middleware or other 3rd party systems, you can change that property name. Just configure the __*now.json*__ as follow:\n```js\n{\n  \"params\": { propName: \"somethingElse\" }\n}\n```\nThe configuration above with replace `req.params` to `req.somethingElse`. \n\n# Tips \u0026 Tricks\n## Dev - Easy Hot Reloading\nWhile developing on your localhost, we recommend using hot re\nloading to help you automatically restart your node process after each change. [_node-dev_](https://github.com/fgnass/node-dev) is a lightweight development tools that watches the minimum amount of files in your project and automatically restart the node process each time a file has changed. \n```\nnpm install node-dev --save-dev\n```\nChange your __*start*__ script in your _package.json_ from `\"start\": \"node index.js\"` to:\n```js\n  \"scripts\": {\n    \"start\": \"node-dev index.js\"\n  }\n```\nThen simply start your server as follow:\n```\nnpm start\n```\n\n## Dev - Better Deployments With now-flow\n### Overview - Deploy To AWS, Add Google Pub/Sub Topic Trigger Based Functions \u0026 Automate Deployments\nAs we've see it above, a __*now.json*__ configuration becomes quickly necessary. Almost all projects will end up needing multiple environment configurations (i.e. prod, staging, ...) and  environment variables specific to them (more info in section [Managing Environment Variables Per Deployment](#managing-environment-variables-per-deployment)). Though it is really straightforward to configure the now.json file, it can be annoying as well as error-prone to modify it prior to each deployment (e.g. deploying to the `staging` environment requires to set up the `\"active\"` property to `\"staging\"`. Another classic example is to deploy to multiple _gcp_ environments. In that case, you will have to update the `\"gcp\"` property prior to each deployment). Beside, as of version 9.2.5., __*now-CLI*__ still experiences bugs while deploying to AWS and simply can't deploy Google Functions that can be triggered by Pub/Sub topics.\n\nFor all the reason above, we developed [__*now-flow*__](https://github.com/nicolasdao/now-flow). It is a simple dev dependency that you should add to your project. It controls the _now-CLI_ while enhancing its capabilities. Simply define all your configurations specific to each environment inside your usual __now.json__, and let NowFlow do the rest. \n\nNo more deployment then aliasing steps. No more worries that some environment variables have been properly deployed to the right environment. \n\n\u003e More details about __*now-flow*__ [here](https://github.com/nicolasdao/now-flow).\n\n### How To Use It\n#### HTTPS Endpoints\n\n__Install it first in your project:__\n```\nnpm install now-flow --save-dev\n```\n__Then configure your *now.json* for each of your environment:__\n```js\n{\n  \"environment\": {\n    \"active\": \"default\",\n    \"default\": {\n      \"hostingType\": \"localhost\",\n      \"db\": {\n        \"user\": \"postgres\",\n        \"password\": \"bla_staging_bla_staging\"\n      }\n    },\n    \"staging\": {\n      \"hostingType\": \"gcp\",\n      \"db\": {\n        \"user\": \"postgres\",\n        \"password\": \"bla_staging_bla_staging\"\n      },\n      \"gcp\": {\n        \"functionName\": \"yourapp-test\",\n        \"memory\": 128\n      }\n    },\n    \"uat\": {\n      \"hostingType\": \"aws\",\n      \"db\": {\n        \"user\": \"postgres\",\n        \"password\": \"bla_staging_bla_staging\"\n      },\n      \"aws\": {\n        \"memory\": 128,\n        \"region\": \"ap-southeast-2\"\n      }\n    },\n    \"prod\": {\n      \"hostingType\": \"now\",\n      \"db\": {\n        \"user\": \"postgres\",\n        \"password\": \"bla_prod_bla_prod\"\n      },\n      \"scripts\": {\n        \"start\": \"NODE_ENV=production node index.js\"\n      },\n      \"alias\": \"yourapp-prod\"\n    }\n  }\n}\n```\n__Add new deployment scripts in your *package.json*:__\n```js\n\"scripts\": {\n  \"deploy:staging\": \"nowflow staging\",\n  \"deploy:uat\": \"nowflow uat\",\n  \"deploy:prod\": \"nowflow prod\",\n}\n```\n\nNow you can deploy to `gcp`, `aws` or `now` using the exact same code:\n\n_Deploying to __gcp__ (make sure you have run `now gcp login` at least once before):_\n```\nnpm run deploy:staging\n```\n_Deploying to __aws__ (make sure you have run `now aws login` at least once before):_\n```\nnpm run deploy:uat\n```\n_Deploying to __now__ (make sure you have run `now login` at least once before):_\n```\nnpm run deploy:prod\n```\n\n#### Google Pub/Sub Topic \u0026 Storage Trigger Based Functions\nSimply add configurations similar to the following into the __*now.json*__:\n```js\n{\n  \"environment\": {\n    \"active\": \"default\",\n    \"default\": {\n      \"hostingType\": \"localhost\"\n    },\n    \"example1\": {\n      \"hostingType\": \"gcp\",\n      \"gcp\": {\n        \"functionName\": \"yourapp-test\",\n        \"memory\": 128,\n        \"trigger\": {\n          \"type\": \"cloud.pubsub\",\n          \"topic\": \"your-google-topic-name\"\n        }\n      }\n    },\n    \"example2\": {\n      \"hostingType\": \"gcp\",\n      \"gcp\": {\n        \"functionName\": \"yourapp-test\",\n        \"memory\": 128,\n        \"trigger\": {\n          \"type\": \"cloud.storage\",\n          \"bucket\": \"your-google-bucket-name\"\n        }\n      }\n    }\n  }\n}\n```\nSimilar to the previous example, update your _package.json_ as follow:\n```js\n\"scripts\": {\n  \"deploy:example1\": \"nowflow example1\",\n  \"deploy:example2\": \"nowflow example2\"\n}\n```\n\nThen deploy using the same commands as in the previous section (e.g. `npm run deploy:example1`).\n\nAs promised, the code you will write in your __*index.js*__ will be the same code you are used to writing for express apps:\n```js\nconst { app } = require('webfunc')\n\napp.post((req, res) =\u003e {\n  console.log(req)\n  console.log(`Hello ${req.params.firstName}`)\n})\n\neval(app.listen('app', 4000))\n``` \n\nAs for any webfunc app, the parameters passed the request will be in the `req.params` object. \n\n\u003eIMPORTANT - ONLY USE 'post' OR 'all' METHODS FOR PUB/SUB AND STORAGE BASED FUNCTIONS\n\n# FAQ\n## What does webfunc use eval() to start the server?\nYou should have noticed that all the snippets above end up with `eval(app.listen('app', 4000))`. The main issue webfunc tackles is to serve endpoints using a uniform API regardless of the serverless hosting platform. This is indeed a challenge as different platforms use different convention. [Zeit Now](https://zeit.co/now) uses a standard [Express](https://expressjs.com/) server, which means that the api to start the server is similar to `app.listen()`. However, with FaaS ([Google Cloud Functions](https://cloud.google.com/functions/), [AWS Lambdas](https://aws.amazon.com/lambda), ...), there is no server to be started. The server lifecycle is automatically managed by the 3rd party. The only piece of code you need to write is a handler function similar to `exports.handler = (req, res) =\u003e res.status(200).send('Hello world')`. In order to manage those 2 main scenarios, webfunc generate the code to be run as a string, and evaluate it using `eval()`. You can easily inspect the code as follow:\n\n```js\nconst { app } = require('webfunc')\n\napp.get('/users/:username', (req, res) =\u003e res.status(200).send(`Hello ${req.params.username}`))\n\nconst code = app.listen('app', 4000)\nconsole.log(eval(code))\neval(code)\n```  \n\nTo observe the difference between a hosting type `\"now\"` and `\"gcp\"`, update the __*now.json*__ file as follow:\n```js\n{\n  \"environment\": {\n    \"active\": \"default\",\n    \"default\": {\n      \"hostingType\": \"gcp\"\n    }\n  }\n}\n```\n\nThen run:\n```\nnode index.js\n```\n\n## Can I Use Webfunc In a Non-Serverless Environment?\nAbsolutely! If you don't specify a string as the first argument of the `listen` api, then it will work as an Express server:\n```js\nconst { app } = require('webfunc')\n\napp.get('/users/:username', (req, res) =\u003e res.status(200).send(`Hello ${req.params.username}`))\n\napp.listen(4000)\n```\n\nThen run:\n```\nnode index.js\n```\n\n# Annexes\n## A.1. CORS Refresher\n_COMING SOON..._\n\n## A.2. CORS Basic Errors\n_**WithCredentials \u0026 CORS**_\nThe following configuration is forbidden:\n```js\nconst { cors } = require('webfunc')\n\nconst restrictedAccess = cors({\n  origins: ['*'],\n  credentials: true\n})\n```\n\nYou cannot allow anybody to access a resource(`\"Access-Control-Allow-Origin\": \"*\"`) while at the same time allowing anybody to share cookies(`\"Access-Control-Allow-Credentials\": \"true\"`). This would be a huge security breach (i.e. [CSRF attach](https://en.wikipedia.org/wiki/Cross-site_request_forgery)). \n\nFor that reason, this configuration, though it allow your resource to be called from the same origin, would fail once your API is called from a different origin. A error similar to the following would be thrown by the browser:\n`The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'.`\n\n__*Solutions*__\n\nIf you do need to share cookies, you will have to explicitely list the origins that are allowed to do so:\n```js\nconst { cors } = require('webfunc')\n\nconst restrictedAccess = cors({\n  origins: ['http://your-allowed-origin.com'],\n  credentials: true\n})\n```\n\nIf you do need to allow access to anybody, then do not allow requests to send cookies:\n```js\nconst { cors } = require('webfunc')\n\nconst restrictedAccess = cors({\n  origins: ['*'],\n  allowedHeaders: ['Authorization'],\n  credentials: false\n})\n```\n\nIf you do need to pass authentication token, you will have to pass it using a special header(e.g. Authorization), or pass it in the query string if you want to avoid preflight queries (preflight queries happens in cross-origin requests when special headers are being used). However, passing credentials in the query string are considered a bad practice. \n\n# Contributing\n```\nnpm test\n```\n\n# This Is What We re Up To\nWe are Neap, an Australian Technology consultancy powering the startup ecosystem in Sydney. We simply love building Tech and also meeting new people, so don't hesitate to connect with us at [https://neap.co](https://neap.co).\n\nOur other open-sourced projects:\n#### Web Framework \u0026 Deployment Tools\n* [__*webfunc*__](https://github.com/nicolasdao/webfunc): Write code for serverless similar to Express once, deploy everywhere. \n* [__*now-flow*__](https://github.com/nicolasdao/now-flow): Automate your Zeit Now Deployments.\n\n#### GraphQL\n* [__*graphql-serverless*__](https://github.com/nicolasdao/graphql-serverless): GraphQL (incl. a GraphiQL interface) middleware for [webfunc](https://github.com/nicolasdao/webfunc).\n* [__*schemaglue*__](https://github.com/nicolasdao/schemaglue): Naturally breaks down your monolithic graphql schema into bits and pieces and then glue them back together.\n* [__*graphql-s2s*__](https://github.com/nicolasdao/graphql-s2s): Add GraphQL Schema support for type inheritance, generic typing, metadata decoration. Transpile the enriched GraphQL string schema into the standard string schema understood by graphql.js and the Apollo server client.\n* [__*graphql-authorize*__](https://github.com/nicolasdao/graphql-authorize.git): Authorization middleware for [graphql-serverless](https://github.com/nicolasdao/graphql-serverless). Add inline authorization straight into your GraphQl schema to restrict access to certain fields based on your user's rights.\n\n#### React \u0026 React Native\n* [__*react-native-game-engine*__](https://github.com/bberak/react-native-game-engine): A lightweight game engine for react native.\n* [__*react-native-game-engine-handbook*__](https://github.com/bberak/react-native-game-engine-handbook): A React Native app showcasing some examples using react-native-game-engine.\n\n#### Tools\n* [__*aws-cloudwatch-logger*__](https://github.com/nicolasdao/aws-cloudwatch-logger): Promise based logger for AWS CloudWatch LogStream.\n\n# License\nCopyright (c) 2018, Neap Pty Ltd.\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n* Neither the name of Neap Pty Ltd nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\nANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\nWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL NEAP PTY LTD BE LIABLE FOR ANY\nDIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\nLOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\nON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\nSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnicolasdao%2Fwebfunc","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnicolasdao%2Fwebfunc","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnicolasdao%2Fwebfunc/lists"}