cvmartin
1/8/2018 - 5:22 PM

Setting API with pm2

API with pm2, node js and Rstudio server

---
title: "Set API with pm2"
output: html_document
---

## Initial test

Start with a very simple file, saved under plumber.R: 

```{r eval=FALSE, include=FALSE}
# plumber.R

#' Echo the parameter that was sent in
#' @param msg The message to echo back.
#' @get /echo
function(msg=""){
  list(msg = paste0("The message is: '", msg, "'"))
}

#' Plot out data from the iris dataset
#' @param spec If provided, filter the data to only this species (e.g. 'setosa')
#' @get /plot
#' @png
function(spec){
  myData <- iris
  title <- "All Species"
  
  # Filter if the species was specified
  if (!missing(spec)){
    title <- paste0("Only the '", spec, "' Species")
    myData <- subset(iris, Species == spec)
  }
  
  plot(myData$Sepal.Length, myData$Petal.Length,
       main=title, xlab="Sepal Length", ylab="Petal Length")
}

```

Install plumber if you haven't already:
`install.packages("plumber")`

Make it run: 
```{r eval=FALSE, include=FALSE}
library(plumber)
pr <- plumber::plumb("plumber.R")
pr$run(port = 8001)
```

Note that, from a web server, the localhost where the API is accessible cannot be reached. However, you can check if it works using the terminal: `curl localhost:8001/echo?msg=howareyou`


## Install and setup pm2

pm2 is a process manager that will take care that the tasks are properly running. 

To install pm2 globally, you also need nodejs: 

```
sudo apt-get update
sudo apt-get install -y nodejs npm
sudo apt install nodejs-legacy
sudo npm install -g pm2
```

Start pm2:
```
sudo pm2 startup
```

## Deploy

First of all, it is important to wrap up the file, because pm2 doesn't understand R scripts natively. Instead, it is necessary to specify a custom interpreter. We can use this feature to launch an R-based wrapper for our plumber file. 

Save the following under `run-myfile.R`: 
```
#!/usr/bin/env Rscript

library(plumber)
pr <- plumb('plumber.R')
pr$run(host = INTERNAL_IP, port=PORT)
```

Probably you want something more elaborated for the location of the files, anyway. Something like a proper, immutable file system. 

Importantly, about host and port: 

- Host means the valid IP owned by the server (obviously, the one that shall be accessed through internet). A detail that is not mentioned elsewhere, is that in AWS, the EC2 instances have an IP accessible through the internet that is different from the localhost, therefore `pr$run(port = PORT)` doesn't work. The host has to be specified to the **internal IP of the instance**, and then it can be accessed through the public IP normally.

- The port where the server is listening to shall be open. This is, of course, unimportant for a local machine, where all ports of localhost are easily accessible. In the case of web servers, it is important to ensure that the server can be accesse. Normally you have to check the security group to ensure it. 

To launch, in the case above, run: 
`sudo pm2 start --interpreter="Rscript" /path/run-myfile.R`

Now it is reachable through the **public** address (and port) of the instance. For instance
`http://18.196.87.140:8001/echo?msg=howareyou`

----

BUT if you have SSL authentication and in the nginx file you have something like this: 

```
		location /api/ {
          proxy_pass http://127.0.0.1:8001/;
          proxy_http_version 1.1;
          proxy_set_header Upgrade $http_upgrade;
          proxy_set_header Connection "upgrade";
        }
```

This means that, upon contacting the (secured) server, you will be redirected to the port 8001 of the localhost. Therefore, you should have the API process running in the localhost(ip = 127.0.0.1). To achieve this, just ignore the parameter *host* in the file `run-myfile.R`.


## Save the progress

When done, save all the progress with `pm2 save`. At this point, whenever there is a reboot, or one of the processes crashes, pm2 will automatically restart the process.