1

I would like to know how to make a running endpoint as RunKit does.

In that page of RunKit, it generates a URL associated with a code. Opening that URL in a browser will see the response defined by the code. Moreover, changes to the code will be reflected in real time.

Does anyone know how they achieve this?

I know defining a fixed api in a mean-stack website. Eg, https://myexample.com/api/add/3/5 can be called because of the following code. But I don't know how to make an API whose function can be changed in real time.

router.get('/api/add/:a/:b', function (req, res, next) {
    var r = Number(req.params.a) + Number(req.params.b);
    res.json(r)
})
SoftTimur
  • 5,630
  • 38
  • 140
  • 292
  • thank you... it is the "auto-update" part that i don't see how they implement... – SoftTimur Sep 05 '17 at 02:16
  • I see... I have seen people use nodemon. Now, what I want to know more is how they pass the content of the function updated in the browser into `router.get(...)` before triggering the auto-update. – SoftTimur Sep 05 '17 at 02:30

2 Answers2

1

Express middlewares are just regular JS functions.

In order to have routes with changing functionalities, you don't need to change the middleware function at runtime, you just need to change how it behaves.

I'll try to demonstrate a possible solution here.
Please note that it is just a proof-of-concept, not a production-ready and/or secure solution.


Let's assume you have some kind of online code editor on your website which allows your users to write JS code and save it on your database.

You can set up your endpoint routes like this:

router.get('/endpoint/:id', (req, res) => {
  // this id identifies a previously saved code wrote by a user
  let id = req.params.id        

  // NOTE: THIS IS NOT ACTUAL WORKING JS CODE
  // fetch the previously saved code from your database as a string
  let code = database.getDocumentById(id)

  // you can construct an actual JS function form the string, using the `Function()` constructor
  let f = Function(code)

  // now you can call the function, do whatever you like with it and finally send the user the results
  let result = f()
  res.send(result)
})

You probably need to create a wrapper function for user provided code and control what will be executed and what will be the results.


P.S.
Also check out this SO post about creating JS functions from strings:
Is there a way to create a function from a string with javascript?

Kayvan Mazaheri
  • 2,447
  • 25
  • 40
0

The basic idea is, you create a database of functions stored as strings. Since JS allows you to create a function from a string using the Function() constructor, you can dynamically generate a function on demand. I've provided a POST endpoint to register the function.

var express = require("express");
var app = express();
var bodyParser = require('body-parser');
var router = express.Router();
var mongoose = require('mongoose');

db = mongoose.connect('mongodb://localhost:27017/login1');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

var data = new mongoose.Schema({
    "name": String,
    "content": String
});
model = mongoose.model("functions", data);

router.post('/create', (req, res) => {
    // Insert validation for security and removing duplicates etc.
    model.insertMany({"name": req.body.name, "content": req.body.content}).then((d, err) => {
        res.status(200).send({'result': "Added"});
    });
});

router.get('/:func/*', (req, res) => {
    var func = req.params.func;
    var args = req.params['0'].split('/');
    model.findOne({"name": func}).then((d, err) => {
        if (err) {return res.status(404).send({'result': "Error with database"});}
        else if (d === null) {return res.status(202).send({'result': "Function not present"});}
        var func = Function(d.content);
        res.status(200).send({'result': func(...args)});
    });
});

app.use("/", router);
var port = process.env.PORT || 3000;

var server = app.listen(port, function() {
    console.log('Express server listening on port ' + port);
});

Lets test this:

$ node server.js
Express server listening on port 3000

From postman or another terminal window:

$ curl -X GET "http://localhost:3000/add/1/2/3"
{"result":"Function not present"} 

Now lets register the add function (which is sum of arguments):

$ curl -X POST "http://localhost:3000/create/" -H "content-type:application/json" -d '{"name": "add", "content": "return [...arguments].reduce( (i, j) => i+=parseInt(j), 0);"}'
{'result': "Added"}

The function registered here can be far more complicated. Just remember that you can get all the arguments passed to the function using the Agruments variable.

Test it:

$ curl -X GET "http://localhost:3000/add/1/2/3"
{"result": 6}

$ curl -X GET "http://localhost:3000/add/100/3/5/10/9/2"
{"result": 129}
TheChetan
  • 4,440
  • 3
  • 32
  • 41
  • Thank you for this, and sorry for the late reply. What you do is to store all the functions and their parameters in a hash object, and then call the right function based on the router. But, I want something more flexible: I want a function to be dynamically created with the string entered by users in browser. I guess somewhere constructing a function with a JS String is needed. – SoftTimur Sep 11 '17 at 03:08
  • I'm storing the functions in a hash object, which means that if I support n functions, then you can dynamically call them from the api endpoint. The parameters for the function come straight from the url itself. They additionally support a way to add a function to the hash object, which makes it dynamic. I'll try to add that also. – TheChetan Sep 11 '17 at 03:31
  • If you can add a function dynamically constructed by a JS String, that will be good. If you can make sure your code works, that will be great. – SoftTimur Sep 11 '17 at 03:39
  • @SoftTimur See the modified answer! – TheChetan Sep 11 '17 at 06:56
  • Ask if you need anything else – TheChetan Sep 11 '17 at 07:12