Git Product home page Git Product logo

serverless-middleware's Introduction

Serverless Middleware

Serverless npm version npm monthly downloads Node.js CI Coverage Status license

Serverless plugin to allow middleware handlers configured directly in serverless.yaml

Requirements:

  • Serverless v3
  • AWS provider
  • Node.js 14+

Supported runtimes

  • nodejs12.x (both Javascript and Typescript)
  • nodejs14.x (both Javascript and Typescript)
  • nodejs16.x (both Javascript and Typescript)
  • nodejs18.x (both Javascript and Typescript)
  • nodejs20.x (both Javascript and Typescript)
  • dotnetcore2.1
  • java8
  • java11
  • go1.x
  • python2.7
  • python3.7
  • ruby2.5
  • provided

Installation

Install via npm in the root of your Serverless service:

npm install serverless-middleware --save-dev

Add the plugin to the plugins array in your Serverless serverless.yaml:

plugins:
  - serverless-middleware

How it works

Middleware allows you to set up multiple handlers to be executed sequentially including error handlers that will capture any exception in the chain.

Middlewares are just standard AWS lambda handlers that return a promise (or are async). Handlers using callback will NOT work.

const myMiddleware = async (event, context) => { ... };

Once serverless-middleware is installed you can set the function.middleware property to an array and skip the function.handler property. Each middleware handler can be a string (like a standard handler would be) or an object containing the properties then and/or catch.

For example:

provider:
  name: aws
  runtime: nodejs20.x
  
functions:
  myFunction:
    middleware:
      - auth.authenticate
      - auth.authorize
      - then: myFunction.handler # `then:` is unnecessary here.
      - catch: utils.handlerError
      - # or both can be combined
        then: logger.log
        catch: utils.handlerLoggerError

will result in an execution like:

Promise.resolve()
  .then(require('./auth').authenticate)
  .then(require('./auth').authorize)
  .then(require('./myFunction').handler)
  .catch(require('./utils').handlerError)
  .then(require('./logger').log)
  .catch(require('./utils').handlerLoggerError);

As with standard promises, catch handlers are only executed when there are exceptions. The resulting lambda will return the result returned by the last middleware handler executed.

The event and context objects are passed from handler to handler so you can attach new properties to be accessed by subsequent handlers. context always contains the result of the previous handler in the prev property. The user can also stop at any point in the chain by calling the end method in the context argument. After context.end() is called, no more handlers will be executed.

For example:

const myMiddleware = async (event, context) => {
  if (context.prev === undefined) {
    // Previous middleware handler didn't return. End execution.
    context.end();
    return {
      statusCode: 200,
      body: 'No results',
    };
  }

  ...
};

You can also add pre/pos- middleware handlers and maintain the function.handler. These middleware are just prepended/appended to the main handler.

For example:

provider:
  name: aws
  runtime: nodejs20.x
  
functions:
  myFunction:
    events:
      - http:
          path: my-function
          method: get
    handler: myFunction.handler
    middleware:
      pre:
        - auth.authenticate
        - auth.authorize
      pos:
        - catch: utils.handlerError

You can also add pre/pos- middleware handlers at the package level using the custom.middleware section of serverless.yaml. These middleware are just prepended/appended to all the function middleware handlers chain.

For example:

provider:
  name: aws
  runtime: nodejs20.x

custom:
  middleware:
    pre:
      - auth.authenticate
    pos:
      - catch: utils.handlerError

functions:
  myAnonymousFunction:
    events:
      - http:
          path: my-anonymous-function
          method: get
    handler: myAnonymousFunction.handler
  myFunction:
    events:
      - http:
          path: my-function
          method: get
    handler: myFunction.handler
    middleware:
      pre:
        - auth.authorize

will result in a similar promise chain as above.

Packaging

In most cases, you shouldn't need to change the default packaging configuration. For edge cases, Middleware can be configured to use a specific intermediary folder and to not clear it after creating the serverless package.

These settings are also set in the custom.middleware section of serverless.yaml

custom:
  middleware:
    folderName: my_custom_folder  # defaults to '.middleware'
    cleanFolder: false            # defaults to 'true'

This might be useful if you are using sls package and building your own artifacts.

Migrations

v1.0.0 to 2.0.0

Use function.middleware instead fo function.custom.middleware

v0.0.14 to v0.0.15

Use function.custom.middleware instead fo function.handler

Passing an array to the handler property is not allowed anymore since Serverless is getting stricter with it's types and it also causes issues with Typescript.

So

functions:
  myFunction:
    handler:
      - auth.authenticate
      - auth.authorize
      - then: myFunction.handler # `then:` is unnecessary here.
      - catch: utils.handlerError
      - # or both can be combined
        then: logger.log
        catch: utils.handlerLoggerError

becomes

functions:
  myFunction:
    custom:
      middleware:
        - auth.authenticate
        - auth.authorize
        - then: myFunction.handler # `then:` is unnecessary here.
        - catch: utils.handlerError
        - # or both can be combined
          then: logger.log
          catch: utils.handlerLoggerError

Contribute

Help us to make this plugin better.

  • Clone the code
  • Install the dependencies with npm install
  • Create a feature branch git checkout -b new_feature
  • Add your code and add tests if you implement a new feature
  • Validate your changes npm run lint and npm test (or npm run test-with-coverage)

License

This software is released under the MIT license. See the license file for more details.

serverless-middleware's People

Contributors

alesiosinopoli avatar juanjodiaz avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

serverless-middleware's Issues

Configuration warning at 'functions.home_get.handler': should be string

Problem

Serverless says handler should be a string. So does this mean that this middleware might not work with upcoming versions ?

Log

Serverless: Configuration warning at 'functions.home_get.handler': should be string
Serverless:
Serverless: Learn more about configuration validation here: http://slss.io/configuration-validation
Serverless:
Serverless: Deprecation warning: Starting with next major, Serverless will throw on configuration errors by default. Adapt to this behavior now by adding "configValidationMode: error" to service configuration
More Info: https://www.serverless.com/framework/docs/deprecations/#CONFIG_VALIDATION_MODE_DEFAULT

Middleware function not invoked

I have started the application locally and in a function getUser i want to add a middleware and i have added it like shown below

getUsers:
    handler: ORM.lambdas.createUser.getUsers
    events:
      - http:
        path: /getUser/{id}
        method: get
        cors: true
    middleware: 
     pre:
      - ORM.lambdas.createUser.middleware2
      
     "ORM.lambdas.createUser.middleware2" this is the path of the middleware function 
     but middleware does not get invoked
     
     here is the whole yml file
     
     org: sardik
app: serverless-auth-mongo
service: Auth-MongoDB
frameworkVersion: '3'

plugins:
  - serverless-python-requirements
  - serverless-offline
  - serverless-middleware

custom:
  pythonRequirements:
    dockerizePip: false
  serverless-offline:
    httpPort: 4000
  middleware: 
    pre:
      - ORM.lambdas.createUser.middleware2

provider:
  name: aws
  runtime: python3.9
  environment:
    JWT_ALGORITHM: 'HS256'
    JWT_SECRET: 'secret'
    
functions:
  hello:
    handler: handler.hello
    events:
      - httpApi:
          path: /
          method: get
  createUser:
    handler: ORM.lambdas.createUser.createUser
    events:
      - http:
          path: /createUser
          method: post
          cors: true
  login:
    handler: ORM.lambdas.createUser.login
    events:
      - http:
          path: /login
          method: post
          cors: true
  getUsers:
    handler: ORM.lambdas.createUser.getUsers
    events:
      - http:
        path: /getUser/{id}
        method: get
        cors: true
    middleware: 
     pre:
      - ORM.lambdas.createUser.middleware2
  updateUsers:
    handler: ORM.lambdas.createUser.updateUsers
    events:
      - http:
          path: /updateUser/{id}
          method: put
          cors: true
  deleteUsers:
    handler: ORM.lambdas.createUser.deleteUsers
    events:
      - http:
          path: /deleteUsers/{id}
          method: delete
          cors: true
          
          serverless middleware version => ^3.1.0

generated middleware causes typescript errors

Hi, first of all nice work! :)

I have an issue. I am using custom tsconfig.json file and generated middleware causes conflicts (e.g. when noImplicitAny is set to true).

Any thoughts? :)

v1.0.0 with Serverless V3 throws error on deploy

Hey, thanks for the speedy update to v3. I'm getting an error on deploy though:

Error:
UPDATE_FAILED: WebhookLambdaFunction (AWS::Lambda::Function)
Properties validation failed for resource WebhookLambdaFunction with message:
#/Handler: expected type: String, found: JSONArray

My yml config looks something like:

  auth:
    handler:
      - middleware/getConfig.handler
      - middleware/validateAuthRequest.handler
      - handlers/auth/auth.handler
    events:
      - httpApi:
          path: /{appName}/auth
          method: get

Serverless also complains about this config (as it always has) when you run deploy:

Warning: Invalid configuration encountered
  at 'functions.auth.handler': must be string

Thank you!

I can't setup serverless middleware

I have function

getCategories:
    middleware:
      pre:
        - ./src/middleware/auth.middleware
    handler: ./src/handlers/categories/getAll.handler
    events:
      - http:
          path: /api/categories
          method: GET
          cors: true

src/middleware/auth.ts

export const middleware = async (event: any, context: any) => {
	try {
		console.log("hello from auth middleware");
		console.log(event);
		console.log(context);
	} catch (err) {
		console.log(err);
	}
};

when i trigger my function it ignores the middleware

Plugin not working in AWS

Hi guys, i am experimenting issues in AWS that i dont have in local (running serverless offline). When i hit an endpoint with middleware in local, everything works as expected, not the case in AWS, that i got 502 Bad Gateway and :

{ "message": "Internal server error" }
Looking at the cloudwatch service in the lambda function i see this:

Bad handler .middleware/remoteIndex.handler
This is my lambda function, basically all of them return a promise:

remoteIndex:
  handler: 
    - api/controllers/AuthController.authorize
    - api/controllers/advanced/SiteOptionsController.remoteIndex
    - catch: api/controllers/ErrorController.unauthorize
  events:
    - http:
        path: api/siteOptions/

And the generated remoteIndex.js

const api_controllers_AuthController = require('../api/controllers/AuthController');
const api_controllers_advanced_SiteOptionsController = require('../api/controllers/advanced/SiteOptionsController');
const api_controllers_ErrorController = require('../api/controllers/ErrorController');

module.exports.handler = async (event, context) => {
  let end = false;
  context.end = () => end = true;

  const wrappedHandler = handler => prev => {
    if (end) return prev;
    context.prev = prev;
    return handler(event, context);
  };

  return Promise.resolve()
    .then(wrappedHandler(api_controllers_AuthController.authorize.bind(api_controllers_AuthController)))
    .then(wrappedHandler(api_controllers_advanced_SiteOptionsController.remoteIndex.bind(api_controllers_advanced_SiteOptionsController)))
    .catch(wrappedHandler(api_controllers_ErrorController.unauthorize.bind(api_controllers_ErrorController)));};

If you guys could take a look to this issue.

Serverless stops working after adding middleware (only in production)

serverless.yml has this configuration:

    - serverless-middleware # Must stay before serverless-offline and serverless-webpack
    - serverless-webpack
    - serverless-offline
    - serverless-prune-plugin

custom:
    middleware: 
        pre: 
            - src/common/request_middleware/index.beforeRequest
        pos: 
            - then: src/common/request_middleware/index.afterRequest
            - catch: src/common/request_middleware/index.errorHandler

It works well when running locally, but when running in production, all api requests return a status code 500, Internal Server Error. What might be the cause?

In serverless offline the webpack copy folder .middlerware to root project instead of copy to .webpack

Every time that i run serverless offline, the webpack creates a folder .webpack in root and i get this is error:


"Error: Cannot find module 'c:\\Users\\xpto\\Desktop\\PQM\\aurora-serverless-api\\.webpack\\service\\.middleware\\proxy'",
        "Require stack:",
        "- c:\\Users\\sbtrry\\Desktop\\PQM\\aurora-serverless-api\\node_modules\\serverless-offline\\src\\functionHelper.js",
        "- c:\\Users\\sbtrry\\Desktop\\PQM\\aurora-serverless-api\\node_modules\\serverless-offline\\src\\createAuthScheme.js",
        "- c:\\Users\\sbtrry\\Desktop\\PQM\\aurora-serverless-api\\node_modules\\serverless-offline\\src\\index.js",
        "- c:\\Users\\sbtrry\\Desktop\\PQM\\aurora-serverless-api\\node_modules\\serverless\\lib\\classes\\PluginManager.js",
        "- c:\\Users\\sbtrry\\Desktop\\PQM\\aurora-serverless-api\\node_modules\\serverless\\lib\\Serverless.js",
        "- c:\\Users\\sbtrry\\Desktop\\PQM\\aurora-serverless-api\\node_modules\\serverless\\lib\\utils\\autocomplete.js",
        "- c:\\Users\\sbtrry\\Desktop\\PQM\\aurora-serverless-api\\node_modules\\serverless\\bin\\serverless.js",
        "- c:\\Users\\sbtrry\\Desktop\\PQM\\aurora-serverless-api\\node_modules\\serverless\\bin\\serverless",
        "at Function.Module._resolveFilename (internal/modules/cjs/loader.js:794:15)",
        "at Function.Module._load (internal/modules/cjs/loader.js:687:27)",
        "at Module.require (internal/modules/cjs/loader.js:849:19)",
        "at require (internal/modules/cjs/helpers.js:74:18)",
        "at Object.createHandler (c:\\Users\\sbtrry\\Desktop\\PQM\\aurora-serverless-api\\node_modules\\serverless-offline\\src\\functionHelper.js:192:17)",
        "at handler (c:\\Users\\sbtrry\\Desktop\\PQM\\aurora-serverless-api\\node_modules\\serverless-offline\\src\\index.js:624:40)",
        "at Object.internals.handler (c:\\Users\\sbtrry\\Desktop\\PQM\\aurora-serverless-api\\node_modules\\hapi\\lib\\handler.js:101:51)",
        "at c:\\Users\\sbtrry\\Desktop\\PQM\\aurora-serverless-api\\node_modules\\hapi\\lib\\handler.js:32:23",
        "at module.exports.internals.Protect.internals.Protect.run (c:\\Users\\sbtrry\\Desktop\\PQM\\aurora-serverless-api\\node_modules\\hapi\\lib\\protect.js:60:12)",
        "at exports.execute (c:\\Users\\sbtrry\\Desktop\\PQM\\aurora-serverless-api\\node_modules\\hapi\\lib\\handler.js:26:22)",
        "at each (c:\\Users\\sbtrry\\Desktop\\PQM\\aurora-serverless-api\\node_modules\\hapi\\lib\\request.js:401:16)",
        "at iterate (c:\\Users\\sbtrry\\Desktop\\PQM\\aurora-serverless-api\\node_modules\\items\\lib\\index.js:36:13)",
        "at done (c:\\Users\\sbtrry\\Desktop\\PQM\\aurora-serverless-api\\node_modules\\items\\lib\\index.js:28:25)",
        "at internals.Auth.payload (c:\\Users\\sbtrry\\Desktop\\PQM\\aurora-serverless-api\\node_modules\\hapi\\lib\\auth.js:235:16)",
        "at each (c:\\Users\\sbtrry\\Desktop\\PQM\\aurora-serverless-api\\node_modules\\hapi\\lib\\request.js:401:16)",
        "at iterate (c:\\Users\\sbtrry\\Desktop\\PQM\\aurora-serverless-api\\node_modules\\items\\lib\\index.js:36:13)",
        "at done (c:\\Users\\sbtrry\\Desktop\\PQM\\aurora-serverless-api\\node_modules\\items\\lib\\index.js:28:25)",
        "at onParsed (c:\\Users\\sbtrry\\Desktop\\PQM\\aurora-serverless-api\\node_modules\\hapi\\lib\\route.js:341:20)",
        "at c:\\Users\\sbtrry\\Desktop\\PQM\\aurora-serverless-api\\node_modules\\hapi\\lib\\route.js:380:20",
        "at next (c:\\Users\\sbtrry\\Desktop\\PQM\\aurora-serverless-api\\node_modules\\subtext\\lib\\index.js:46:26)",
        "at c:\\Users\\sbtrry\\Desktop\\PQM\\aurora-serverless-api\\node_modules\\subtext\\lib\\index.js:243:16",
        "at finish (c:\\Users\\sbtrry\\Desktop\\PQM\\aurora-serverless-api\\node_modules\\wreck\\lib\\index.js:374:20)",
        "at wrapped (c:\\Users\\sbtrry\\Desktop\\PQM\\aurora-serverless-api\\node_modules\\wreck\\node_modules\\hoek\\lib\\index.js:879:20)",
        "at module.exports.internals.Recorder.onReaderFinish (c:\\Users\\sbtrry\\Desktop\\PQM\\aurora-serverless-api\\node_modules\\wreck\\lib\\index.js:449:16)",
        "at Object.onceWrapper (events.js:299:28)",
        "at module.exports.internals.Recorder.emit (events.js:215:7)",
        "at module.exports.internals.Recorder.EventEmitter.emit (domain.js:499:23)",
        "at finishMaybe (_stream_writable.js:656:14)",
        "at endWritable (_stream_writable.js:673:3)",
        "at module.exports.internals.Recorder.Writable.end (_stream_writable.js:604:5)",
        "at IncomingMessage.onend (_stream_readable.js:691:10)",
        "at Object.onceWrapper (events.js:299:28)",
        "at IncomingMessage.emit (events.js:215:7)",
        "at IncomingMessage.EventEmitter.emit (domain.js:476:20)",
        "at endReadableNT (_stream_readable.js:1183:12)"

Handler typescript typings

Thanks for library mate @juanjoDiaz

So for my project I have a custom Handler interface as I JSON.parse every body request in a middleware:

interface APIGatewayEvent<IBody = null> extends Omit<AWSLambda.APIGatewayEvent, 'body'> {
  body: IBody;
}

export type AWSLambdaHandler<IBody = {}> = AWSLambda.Handler<APIGatewayEvent<IBody>>;

Usage in functions like

interface Body {
  content: string;
  attachment: string;
}

const create: AWSLambdaHandler<Body> = async (event) => { ... }

However i get typing errors as you declare a custom Handler in typescript.js which I can't import and set my own custom Event. How can have my own custom handler typings?

Maybe is it better if you just drop your Handler interface and leave typings to be sorted out by us? So maybe just give handler any type so typescript.js becomes

/**
 * @description Create TypeScript middleware handler
 *
 * @param {Array<string>} handlers - handlers to be run as middleware
 *np
 * @return {string} TypeScript Middleware handler
 * */
function createTSMiddlewareHandler(handlers, pathToRoot) {
  const handlersInfo = handlers
    .reduce((modules, handler) => {
      if (handler.then && handler.catch) {
        const { name, module } = handler.then;
        const { name: name2, module: module2 } = handler.catch;
        return { ...modules, [module]: name, [module2]: name2 };
      }
      if (handler.then) {
        const { name, module } = handler.then;
        return { ...modules, [module]: name };
      }

      const { name, module } = handler.catch;
      return { ...modules, [module]: name };
    }, {});

  const imports = Object.keys(handlersInfo)
    .map((handler) => `import * as ${handlersInfo[handler]} from '${pathToRoot}/${handler}';`).join('\n');

  const promiseChain = handlers.map((handler) => {
    if (handler.then && handler.catch) {
      const { name, fn } = handler.then;
      const { name: name2, fn: fn2 } = handler.catch;
      return `    .then(wrappedHandler(${name}.${fn}.bind(${name})))
    .catch(wrappedHandler(${name2}.${fn2}.bind(${name2})))`;
    }

    if (handler.then) {
      const { name, fn } = handler.then;
      return `    .then(wrappedHandler(${name}.${fn}.bind(${name})))`;
    }

    const { name, fn } = handler.catch;
    return `    .catch(wrappedHandler(${name}.${fn}.bind(${name})))`;
  }).join('\n');

  return `'use strict';
    
${imports}

export const handler = (event: any, context: any) => {
  let end = false;
  context.end = () => end = true;

  const wrappedHandler = (handler: any) => (prev: any): Promise<any> => {
    if (end) return prev;
    context.prev = prev;
    return handler(event, context);
  };

  return Promise.resolve()
${promiseChain};
};`;
}

module.exports = createTSMiddlewareHandler;

Cannot do dynamic imports when using a middleware

Hi,

I've encountered an issue today when using "serverless-middleware". The plugin works well in general, but I cannot seem to do any dynamic imports in my app using "import()".

The problem is that the path in the compiled app changes to "/.middleware", but the imports are file in the level below.
Here is an example error:

GET /accounts/me (λ: getAccount)
× Cannot find module './8.js'
  Require stack:
  - /usr/src/app/services/accounts/.webpack/service/.middleware/accounts-api-local-getAccount.js
  - /usr/src/app/node_modules/serverless-offline/dist/lambda/handler-runner/in-process-runner/InProcessRunner.js
  - /usr/src/app/node_modules/serverless-offline/dist/lambda/handler-runner/in-process-runner/index.js
  - /usr/src/app/node_modules/serverless-offline/dist/lambda/handler-runner/HandlerRunner.js
  - /usr/src/app/node_modules/serverless-offline/dist/lambda/handler-runner/index.js
  - /usr/src/app/node_modules/serverless-offline/dist/lambda/LambdaFunction.js
  - /usr/src/app/node_modules/serverless-offline/dist/lambda/LambdaFunctionPool.js
  - /usr/src/app/node_modules/serverless-offline/dist/lambda/Lambda.js
  - /usr/src/app/node_modules/serverless-offline/dist/lambda/index.js
  - /usr/src/app/node_modules/serverless-offline/dist/ServerlessOffline.js
  - /usr/src/app/node_modules/serverless-offline/dist/index.js
  - /usr/src/app/node_modules/serverless-offline/dist/main.js
  - /usr/src/app/node_modules/serverless/lib/utils/import-module.js
  - /usr/src/app/node_modules/serverless/lib/classes/plugin-manager.js
  - /usr/src/app/node_modules/serverless/lib/serverless.js
  - /usr/src/app/node_modules/serverless/scripts/serverless.js
  - /usr/src/app/node_modules/serverless/bin/serverless.js

In this case the "8.js" would be in "/usr/src/app/services/accounts/.webpack/service/" and not in "/usr/src/app/services/accounts/.webpack/service/.middleware/".

Is there any known work around to this?

Thank you.

serverless-middleware incompatibility with serverless-webpack

I've been trying to use middleware with serverless-webpack and encountered some issues that almost make it impossible to combine serverless-middleware, serverless-webpack, and TypeScript.

The problem arises when placing serverless-middleware before serverless-webpack in the plugins list within serverless.yml. This causes the function's entry point to change from /path/to/invoked.handler to .middleware/functionxxx/, a clever behavior. However, a hurdle emerges: the outcome of the middleware contains imports like:

import * as src_middlewares_logger from '../src/middlewares/logger.js';
import * as src_handlers_index from '../src/handlers/index.js';
import * as src_middlewares_errorHandler from '../src/middlewares/errorHandler.js';

Later, when Webpack attempts to bundle these files, it results in an error. The cause of the error is that, unlike serverless-typescript-plugin which transpiles TypeScript to JavaScript and doesn't mind if files exist or not, Webpack expects JS files and throws a MODULE NOT FOUND error.

To address this, I need to either transpile those files using tsc to create JS files, or set up a separate Webpack build to bundle them into JS before invoking serverless-middleware. This way, when the actual serverless-webpack processes the serverless-middleware-wrapped functions, it encounters JS files and avoids the MODULE NOT FOUND error.

Another approach is to reorder the plugins, placing serverless-webpack after serverless-plugin-typescript. This resolves the issue by generating JS files, but it contradicts the initial intent of using serverless-webpack to avoid the overhead introduced by serverless-plugin-typescript.

A potential solution might involve modifying how files are imported in serverless-middleware wrapped functions, and removing the file extension to ensure compatibility with middleware placement. Additionally, if serverless-middleware could be configured to read bundled files by specifying the root, it might offer a cleaner solution.

For now, I've positioned serverless-middleware after serverless-plugin-typescript, though this creates unnecessary overhead due to middleware wrapping, building, and bundling in sequence, which doesn't seem ideal.

also, there should be a way to include middleware in webpack build, by reading middleware directory or providing them from middleware plugin to webpack cause right now my approach to include middleware in webpack is by adding them to entry object in webpack, it's not necessary to do it if serverless-middleware was mentioned after serverless-typescrypt-plugin (which is not a good practice at all)

    entry: {
      ...slsw.lib.entries,
      'src/middlewares/errorHandler': './src/middlewares/errorHandler.js',
      'src/middlewares/logger': './src/middlewares/logger.js',
    },

Error when using serverless-offline

I'm on windows, I'm not putting the code since it's just an empty project with nothing more than copy/paste from the docs of serverless-middleware, so it's not remarkable special code

When using serverless-middleware with serverless offline there are some problems:

  • whenever I try to run the command serverless offline it would throw an error regarding the handler being an array, when this should be normal when using middlewares

    • "Solution": this previous problem solve when using serverless offline start as the starting command, but then another problem appears
  • followed the previous steps, now whenever I try to cancel the process in git bash or powershell I got the error:
    Error: ENOTEMPTY: directory not empty, rmdir 'C:\Users\ ... \.middleware'

    • "Solution": mark cleanFolder: false in the serverless.yml (option that I actually don't want)

ERR_MODULE_NOT_FOUND when using Typescript and targeting ES6 or higher

Node supports require without an extension, but does not support import without an extension. The built middleware files all have no extension on the imports. This is fine when they're compiled to an ES5 target because they are rewritten to requires, but when targeting ES6 they are not rewritten and remain without an extension. This means running a function in Serverless throws a ERR_MODULE_NOT_FOUND.

My tsconfig.json looks like this:

{
  "compilerOptions": {
    "esModuleInterop": true,
    "preserveConstEnums": true,
    "strictNullChecks": true,
    "sourceMap": true,
    "allowJs": true,
    "target": "es6",
    "outDir": ".build",
    "moduleResolution": "node",
    "lib": ["es2021"],
    "rootDir": "./",
    "forceConsistentCasingInFileNames": true,
    "noImplicitReturns": true,
    "strict": true,
    "noUnusedLocals": true
  }
}

And Serverless is targetting nodejs18.x.

It seems that adding .js in the import template path in /src/typescript.js:26 solves this. It may also need adding to /src/javascript.js:26. I'm not sure if this will cause more problems than it solves if people are importing non-js files here (which they shouldn't be as middleware), but changing this line makes everything work for me.

Update docs for sls offline

Hi nice work with this, it is awesome.

I spent a good while figuring out why this wasn't working, it came down to the project I am working was being started with sls offline and needed to be started with sls offline start in order for this project to be initiated.

Get underlying exception object on pos-catch middleware

Given a serverless configuration like:

provider:
  name: aws
  runtime: nodejs18.x
  stage: ${opt:stage, 'local'}
  region: us-east-1
middleware:
   pos:
     - catch: handlers/middleware/error.handler
plugins:
  - serverless-middleware 

How can I get the error object thrown by the caller handler?

Looking at the generated code in .middleware folder I see this:

return Promise.resolve().then(wrappedHandler(handler.bind(auth_exports))).then(wrappedHandler(handler2.bind(profile_id_exports))).then(wrappedHandler(handler3.bind(list_exports))).catch(wrappedHandler(handler4.bind(error_exports)));

The .catch method is there but I am not sure how to get hold of the actual error object in the error handler function.

not working since 3.2.0

upgraded to 3.2.0 from 3.1.0, and not working since, throwing errors like

  in ./.middleware/SERVICENAME-dev-validateUnit.ts 4:0-119
  Module not found: Error: Can't resolve '../PATH/validate-unit.js' in '/Users/zooli/SERVICEDIR/.middleware'
  resolve '../PATH/validate-unit.js' in '/Users/zooli/SERVICEDIR/.middleware'
    using description file: /Users/zooli/SERVICEDIR/package.json (relative path: ./.middleware)
      using description file: /Users/zooli/SERVICEDIR/package.json (relative path: ./PATH/validate-unit.js)
        no extension
          /Users/zooli/SERVICEDIR/PATH/validate-unit.js doesn't exist
        .ts
          /Users/zooli/SERVICEDIR/PATH/validate-unit.js.ts doesn't exist
        .js
          /Users/zooli/SERVICEDIR/PATH/validate-unit.js.js doesn't exist
        as directory
          /Users/zooli/SERVICEDIR/PATH/validate-unit.js doesn't exist

it's a TS project, node 18, serverless 3.33.0

Which version of the plugin serverless-middleware supports function level middleware and whats the syntax

I am trying to implement middleware at function level by following this doc.
But the middleware function I have written in a separate file and referenced in the serverless.yml is not even getting deployed (I checked by downloading the lambda function) i.e. middleware at function level not even getting registered.
Any idea where I am missing something?
Note: the middleware at package level is working perfectly but I have a requirement to implement it at function level.

Node version used: 14
"serverless": "^2.66.0",
"serverless-domain-manager": "^5.1.0",
"serverless-dotenv-plugin": "^3.10.0",
"serverless-middleware": "^0.0.15",
"serverless-offline": "^8.5.0",
"serverless-openapi-documentation-v2": "^0.4.6",
"serverless-plugin-typescript": "2.1.0",
"serverless-prune-plugin": "^1.6.1",

Below is the sample code of the serveless file:

provider:
  name: aws
  runtime: nodejs14.x

functions:
  my-function:
  middleware:
    pre:
      - src/authMiddleware.authenticate
    post:
      - catch: src/catchHandler.catchErrors
  handler: src/handler.users	
  timeout: 30
  events:
    -service: your-service-name

plugins:
  - serverless-dotenv-plugin
  - serverless-offline
  - serverless-middleware
  - serverless-domain-manager
  - serverless-plugin-typescript
  - serverless-openapi-documentation-v2
  - serverless-prune-plugin

custom:
  middleware:
    pre:
      - src/initialMiddleware.init
    pos:
      - catch: src/catchHandler.catchErrors

Issue with lambda callbacks

We are using non-async handlers with a callback function. Not a fan of it, but it's what I've been given. Anyways, it seems the handler is only sending back event and context and not the callback so the callback keeps getting the error done is not a function. Done being our callback name.

No matching handler found for "path to files"

When using serverless-middleware Im getting this error when i want to start serverless offline

 No matching handler found for 'src/middlewares/verifyToken.verifyToken,src/functions/resetPassword/resetPassword'

Here is how i have it setup at the moment:

serverless.yml:

service: app

# Create an optimized package for our functions
package:
  individually: true

plugins:
  - serverless-webpack # Package our functions with Webpack
  - serverless-middleware
  - serverless-offline
  - serverless-dotenv-plugin # Load .env as environment variables

provider:
  name: aws
  runtime: nodejs12.x
  stage: dev
  region: eu-west-1
 

functions:
  resetPassword: ${file(src/functions/resetPassword/resetPassword.yml):resetPassword}

resetPassword.yml

resetPassword: 
  handler:
    - src/middlewares/verifyToken.verifyToken
    - src/functions/resetPassword/resetPassword.resetExport
  events: 
    - http:
        path: resetPassword
        method: put
    - schedule: 
        rate: rate(5 minutes)
        enabled: true

verifyToken.ts

const verifyToken = async (event, context) => {
     // my logic here
};

export { verifyToken };

resetPassword.ts

const resetPassword = async (event) => {
  // after middleware logic here
};

const resetExport = runWarm(resetPassword);

export { resetExport };

When i leave only one handler the error does not appear, however then i cannot use the middleware as described in the docs.

Serverless-offilne version: 6.8.0
Serverless-middleware version: 0.0.10
Node version: 14.0.0

P.S: When i try to do it with the then: keyword like this:

resetPassword: 
  handler:
    - src/middlewares/verifyToken.verifyToken
    - then: src/functions/resetPassword/resetPassword.resetExport
  events: 
    - http:
        path: resetPassword
        method: put
    - schedule: 
        rate: rate(5 minutes)
        enabled: true

I'm getting this error:

TypeError: handler.substr is not a function

other lambda functions are in .middleware.

getItems:
  handler: src/serverless/items/handler.getItems
  events:
    - http:
        path: items
        method: get
        cors: true
  middleware:
    pre:
      - src/middleware/index.checkHeader

setItems:
  handler: src/serverless/items/handler.setItesm
  events:
    - http:
        path: items
        method: post
        cors: true
  middleware:
    pre:
      - src/middleware/index.checkHeader

when applying the code above, in the code of getItems, there are setItems.js and map files in .middleware, setItems also has js and map files for getItems.

what should i check more?

Path of .middleware folder when searching for modules is broken with default folderName in Serverless 3.0

The path of the .middleware folder using the default configuration is broken in serverless 3.0 + serverless-middleware 3.0.

Framework Core: 3.22.0 (local) 3.21.0 (global)
Plugin: 6.2.2
SDK: 4.3.2

Error: Error: Cannot find module '/project-name/.build/.middleware/service-name-functionName'

Serverless for some reason is assuming that the /.middleware/ directory is located inside of /.build/.middleware/ and that is not the default location of the folder.

The pathToRoot variable resolves to just .. without a preceding slash, (../) and I thought that was the problem although manually altering the following did not fix it. Although, it does print out the preceding slash in the logs and doesn't seem to hurt anything. Not sure if not adding a preceding slash was intentional or not.

const pathToRoot = path.relative(pathFolder, this.serviceDir); - original
const pathToRoot = path.relative(pathFolder, this.serviceDir) + '/'; - modified

If I find a fix, I'm happy to commit it but since you likely can pinpoint it faster, I'm posting this for visibility into the issue as well as for archival purposes.

ERR_MODULE_NOT_FOUND using serverless-offline

I want to test my application locally so I use serverless-online.
Without attaching a middleware, any endpoint work fine, but when I attach the middleware it fails to find the module built:

here there is the releant part of my serverless.yaml file

  processGet:
    handler: src/process/getOne.handler
    events:
      - http:
          path: /${env:APP_DOMAIN}/process/{pid}
          method: GET
          cors: true
          documentation:
            summary: 'Retrieves one process by its unique identifier'
            pathParams:
              - name: pid
                description: 'The process id'
                in: path
                schema:
                  type: 'string'
                  format: 'uuid'
            methodResponses:
              - statusCode: 200
                responseBody:
                  description: 'The process with that identifier'
                responseModels:
                  application/json: 'Process'
              - statusCode: 400
                responseBody:
                  description: 'Bad Request'
                responseModels:
                  application/json: 'BadRequest'
              - statusCode: 404
                responseBody:
                  description: 'Not Found'
                responseModels:
                  application/json: 'ErrorResponse'
              - statusCode: 500
                responseBody:
                  description: 'Internal Server Error'
                responseModels:
                  application/json: 'ErrorResponse'
          private: true
    middleware:
      pre:
        - src/middleware/logger.handler

I noticed that running npm install && serverless offline works fine until I call my endpoint

✖ Unhandled exception in handler 'processGet'.
✖ Runtime.ImportModuleError: Error: Cannot find module 'fa-api-proxy-dev-processGet'
  Require stack:
  - <omitted>/node_modules/serverless-offline/src/lambda/handler-runner/in-process-runner/aws-lambda-ric/UserFunction.js
      at _loadUserApp (<omitted>/node_modules/serverless-offline/src/lambda/handler-runner/in-process-runner/aws-lambda-ric/UserFunction.js:310:15)
      at async module.exports.load (<omitted>/node_modules/serverless-offline/src/lambda/handler-runner/in-process-runner/aws-lambda-ric/UserFunction.js:341:21)
      at async InProcessRunner.run (<omitted>/node_modules/serverless-offline/src/lambda/handler-runner/in-process-runner/InProcessRunner.js:41:21)
      at async MessagePort.<anonymous> (<omitted>/node_modules/serverless-offline/src/lambda/handler-runner/worker-thread-runner/workerThreadHelper.js:24:14)
✖ Error: Cannot find module 'fa-api-proxy-dev-processGet'
  Require stack:
  - /<omitted>/node_modules/serverless-offline/src/lambda/handler-runner/in-process-runner/aws-lambda-ric/UserFunction.js

The module is correctly created in the directory, but seems that cannot be loaded correctly.

Not working with typescript: handler as string error

Hi,
I am getting an error when adding the middleware as a handler array in my function.
I am using typescript therefore my serverless file is a typescript file instead of yml:

Cannot load "serverless.ts": Initialization error: TSError:  Unable to compile TypeScript:
serverless.ts:45:4 - error TS2322: Type 'string[]' is not assignable to type 'string'.
functions: {
  index: {
	  handler: [
		  'lambdas/endpoints/common/middleware.publicAuth', 
		  'lambdas/endpoints/common/default.index'],
	  events: [{
		  http: {
			  method: 'get',
			  path: '',
		  }
	  }]
  },

I am doing something wrong?
Thanks!

Support for ECR images

I'm deploying serverless app using nodeJS and trigger functions inside a ECR container. How can I add middleware for those functions since it only support either handler or image for each function
image

serverless-typescript or serverless-esbuild

Does this plugin work along with serverless-typescript or serverless-esbuild? It doesn't seem like the auth and log is being called at all for me

plugins:
  - serverless-esbuild
  - serverless-middleware
 hello:
    handler: src/Hello/hello.hello
    events:
      - http:
          path: hello
          method: get
          cors: true
    middleware:
      pre:
        - src/Middleware/auth.auth
      pos:
        - catch: src/Middleware/log.log

Allow `nodejs16.x`

After a long wait, Serverless finally supports nodejs16.x, but this plugin errors if that value is used.

Support Node 12.*

@juanjoDiaz Thanks once again for a great package. Since AWS Lambda now supports Node v12, are there plans to support Node v12?

"pre" configuration doesn't work for http handlers

we're on Serverless 2.5 and trying to configure the middleware plugin.
here is the test configuration

custom:
  middleware:
    pre:
      - api/helpers/version-manager-2.checkVersion

functions:
  test-middleware:
    handler: api/http/discover/testmw.handler
    description: GET test-middleware
    events:
      - http:
          path: test-middleware
          method: get

and it works fine when we call a function like this:

serverless invoke local --function test-middleware

but when we're trying to start sls offline and call the same function using postman GET http://localhost:3000/test-middleware


the middleware function is not being called.

what are we doing wrong?

Doesn't work with Serverless v3

When running this plugin with serverless v3, the following error shows:

Error:
At least one of the plugins defines a validation schema that is invalid. Try disabling plugins one by one to identify the problematic plugin and report it to the plugin maintainers.

I don't know what level of effort this involves or what exactly SLS 3 doesn't like, just wanted to post here so it could be tracked.

Env info: Environment: darwin, node 16.10.0, framework 3.0.1 (local), plugin 6.0.0, SDK 4.3.0

Middleware not being called

Firstly nice plugin @juanjoDiaz

I'm using serverless-webpack to compile a typescript serverless app. So my directory is something like

serverless.yml
webpack.config.js
src/
   functions/
      create.ts
   middleware.ts

My serverless.yml looks like

service: serverless-stack

package:
  individually: true

plugins:
  - serverless-middleware
  - serverless-webpack
  - serverless-offline
  - serverless-dotenv-plugin

provider:
  name: aws
  runtime: nodejs12.14
  stage: prod
  region: us-east-1
  iamRoleStatements:
    - Effect: Allow
      Action:
        - lambda:InvokeFunction
      Resource: "*"

custom:
  middleware:
    pre:
      - src/middleware.bodyJson
  webpack:
    webpackConfig: ./webpack.config.js
    includeModules: true
    packager: "yarn"

functions:
  create:
    handler: src/functions/create.default
    events:
      - http:
          path: notes
          method: post
          cors: true
          authorizer: aws_iam

And my middleware.ts is simply

export const bodyJson: AWSLambda.Handler = async (event, _context) => {
  console.log('ZE MIDDLE--->', event.body);
  event.body = JSON.parse(event.body);
  return event;
}

Having set the pre hook for the custom middleware plugin options, the bodyJson in middleware.ts should run before every function, but it isn't. This is when I'm running using the serverless-offline plugin. Any ideas why this would be?
Complete project here

Exclude/include rule for pre an pos keys

Hi @juanjoDiaz , thanks for the support the last time. Now, I have to set a middleware for most of my lambda functions and i would like to know if the plugin supports a standard way to introduce a blacklist for excluding lambda functions in pre and pos, something like this:

custom:
  middleware:
    pre:
      - auth.authenticate
      - auth.authorize
      exclude:
       - myExcludedFunction.handler
    pos:
      - catch: utils.handlerError

Middleware only runs on HTTP API

I have configured both pre and pos middleware handlers which work fine when invoking the function that hosts our HTTP API. But when I invoke any other function, it appears the middleware does not run.

Plugins are configured like this:

plugins:
  - serverless-middleware
  - serverless-domain-manager
  - serverless-offline
  - serverless-plugin-aws-alerts
  - serverless-plugin-typescript
  - serverless-plugin-datadog
  - serverless-plugin-resource-tagging

Middleware is configured universally:

custom:
  middleware:
    pre:
      - src/lambdas/middleware/initialize.handler
    pos:
      - src/lambdas/middleware/teardown.handler

The initialize function loads async bindings into my IOC container, and teardown unbinds them. The function that behaves correctly is declared like this:

functions:
  api:
    name: ${self:service}-api-${sls:stage}
    handler: src/api/Api.handle
    timeout: 30 # 30 seconds, limited by API gateway
    events:
      - http:
          path: /{proxy+}
          method: any

When I add console.log statements in my initialize and teardown handlers, I can see the console output when I make a request with Postman. But it does not work correctly when I use aws lambda invoke to test this other function:

  transferOrderShippedHandler:
    handler: src/transfer/queue/transfer-order-shipped-handler/TransferOrderShippedHandlerLambda.handle
    name: transfer-order-shipped-handler-${sls:stage}
    reservedConcurrency: 1 # configured to cap max concurrency, to protect downstreams
    description: Handler for processing transfer order shipments.
    timeout: 300 # 5 min; Not an expensive operation, no need for setting to max timeout
    events:
      - sqs:
          arn:
            Fn::GetAtt:
              - FulfillmentOrderShippedQueue
              - Arn
          batchSize: 1

When I invoke this function, I know that initialize does not run because. Partly because I don't see the console output, but also because the exception that gets thrown by the handler indicates that my IOC container was not initialized. Take a look at the bottom line of that stack trace:

    '    at Object.<anonymous> (/Users/sam/src/github.com/deliverr/transfer-service/.build/.middleware/transfer-order-shipped-handler-dev.js:5:93)'

That line shows that the new handler that was generated by serverless-middleware is the one that's being invoked. The contents of that file look exactly like you'd expect them to:

'use strict';
Object.defineProperty(exports, "__esModule", { value: true });
exports.handler = void 0;
const src_lambdas_middleware_initialize = require("../src/lambdas/middleware/initialize");
const src_transfer_queue_transfer_order_shipped_handler_TransferOrderShippedHandlerLambda = require("../src/transfer/queue/transfer-order-shipped-handler/TransferOrderShippedHandlerLambda");
const src_lambdas_middleware_teardown = require("../src/lambdas/middleware/teardown");
const handler = (event, context) => {
    let end = false;
    context.end = () => end = true;
    const wrappedHandler = (handler) => (prev) => {
        if (end)
            return prev;
        context.prev = prev;
        return handler(event, context);
    };
    return Promise.resolve()
        .then(wrappedHandler(src_lambdas_middleware_initialize.handler.bind(src_lambdas_middleware_initialize)))
        .then(wrappedHandler(src_transfer_queue_transfer_order_shipped_handler_TransferOrderShippedHandlerLambda.handle.bind(src_transfer_queue_transfer_order_shipped_handler_TransferOrderShippedHandlerLambda)))
        .then(wrappedHandler(src_lambdas_middleware_teardown.handler.bind(src_lambdas_middleware_teardown)));
};
exports.handler = handler;
//# sourceMappingURL=transfer-order-shipped-handler-dev.js.map%

They chain the original handler together with the middleware handlers, in the exact same way that they do in the handler defined by .build/.middleware/transfer-api-dev.js. So it is extremely puzzling to me that the initialize function is not getting invoked.

conflict with serverless-plugin-warmup plugin

Hi, I've spotted that the serverless-plugin-warmup plugin stopped working after I've installed serverless-middleware and configured a global 'pre' function.
the idea was to extract the warmup check into a global middleware 'pre' function like this:

// ./utils/warmup.js
exports.handler = async (event) => {
  if (event.source === 'serverless-plugin-warmup') {
    console.log(`Keeping lambda warm`);
    await new Promise(r => setTimeout(r, 25));
    throw new Error("WarmupOK"); // this is to stop the rest of the function from executing
  }
}

then I configured serverless.yml like this:

plugins:
  - serverless-offline
  - serverless-plugin-warmup
  - serverless-middleware

custom:
  warmup:
    officeHours:
      enabled: false
      events:
        - schedule: cron(0/3 3-18 ? * MON-FRI *)
  middleware:
    pre:
      - api/utils/warmup.handler

functions:
  callback:
    handler: api/http/callback.handler
    warmup:
      officeHours:
        enabled: false
    events:
      - http:
          path: callback
          method: get

after deploy I have the warmup lambda function created, but it looks like the warmup function was wrapped up by the middleware plugin and in the lambda runtime settings I have HandlerInfo set to .middleware/influencer-index-api-staging-warmup-plugin-officeHours.handler whereas normally it would be set to .warmup/officeHours/index.warmUp

therefore I now have the next error in the warmup lambda logs:

{
    "errorType": "Runtime.ImportModuleError",
    "errorMessage": "Error: Cannot find module 'api-staging-warmup-plugin-officeHours'\nRequire stack:\n- /var/runtime/UserFunction.js\n- /var/runtime/index.js",
    "stack": [
        "Runtime.ImportModuleError: Error: Cannot find module 'api-staging-warmup-plugin-officeHours'",
        "Require stack:",
        "- /var/runtime/UserFunction.js",
        "- /var/runtime/index.js",
        "    at _loadUserApp (/var/runtime/UserFunction.js:100:13)",
        "    at Object.module.exports.load (/var/runtime/UserFunction.js:140:17)",
        "    at Object.<anonymous> (/var/runtime/index.js:43:30)",
        "    at Module._compile (internal/modules/cjs/loader.js:1072:14)",
        "    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1101:10)",
        "    at Module.load (internal/modules/cjs/loader.js:937:32)",
        "    at Function.Module._load (internal/modules/cjs/loader.js:778:12)",
        "    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:76:12)",
        "    at internal/main/run_main_module.js:17:47"
    ]
}

is there anything I can do to make it work?

p.s. sorry for duplicating this issue here, just wasn't sure where this should be reported...

probably a configuration mistake: TypeError: handler.substr is not a function

we're on Serverless 2.5 and trying to configure the middleware plugin.
here is the test configuration

test-middleware:
    handler: 
      - api/helpers/version-manager-2.checkVersion
      - api/http/discover/testmw.handler
    description: GET test-middleware
    events:
      - http:
          path: test-middleware
          method: get

but when we're starting sls offline we get this error:

TypeError: handler.substr is not a function
      at splitHandlerPathAndName (/code/pr_2/node_modules/serverless-offline/dist/utils/splitHandlerPathAndName.js:13:24)
      at HttpServer.createRoutes (/code/pr_2/node_modules/serverless-offline/dist/events/http/HttpServer.js:371:63)
      at Http._create (//code/pr_2/node_modules/serverless-offline/dist/events/http/Http.js:43:65)
      at /code/pr_2/node_modules/serverless-offline/dist/events/http/Http.js:52:12
      at Array.forEach (<anonymous>)
      at Http.create (/code/pr_2/node_modules/serverless-offline/dist/events/http/Http.js:47:12)
      at ServerlessOffline._createHttp (/code/pr_2/node_modules/serverless-offline/dist/ServerlessOffline.js:230:53)
      at processTicksAndRejections (internal/process/task_queues.js:93:5)
      at async Promise.all (index 0)
      at ServerlessOffline.start (/code/pr_2/node_modules/serverless-offline/dist/ServerlessOffline.js:145:5)
      at ServerlessOffline._startWithExplicitEnd (/code/pr_2/node_modules/serverless-offline/dist/ServerlessOffline.js:194:5)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.