--- title: "Get Started" knitr: opts_chunk: collapse: false comment: "#>" vignette: > %\VignetteIndexEntry{Get Started} %\VignetteEngine{quarto::html} %\VignetteEncoding{UTF-8} --- ```{r} #| include: false me <- normalizePath( if (Sys.getenv("QUARTO_DOCUMENT_PATH") != "") { Sys.getenv("QUARTO_DOCUMENT_PATH") } else if (file.exists("_helpers.R")) { getwd() } else if (file.exists("vignettes/_helpers.R")) { "vignettes" } else if (file.exists("articles/_helpers.R")) { "articles" } else { "vignettes/articles" }) source(file.path(me, "_helpers.R")) readLines <- function(x) base::readLines(file.path(me, x)) ``` Plumber2 allows you to create APIs by merely decorating your existing R code with special annotations. The example below shows a file named `plumber.R` (the conventional name for Plumber APIs) which defines an API. ```{r} #| eval: false #| code: !expr readLines("files/apis/01-01-quickstart.R") ``` This file defines two Plumber handlers. One is hosted at the path `/echo/{msg}` and simply echoes the message passed in as the second part of the path; the other is hosted at the path `/plot` and returns an image showing a simple R plot. You can use the `api()` function to translate this R file into a Plumber API: ``` r pa <- api("plumber.R") pa ``` The `api` object now encapsulates all the logic represented in your `plumber.R` file. The next step is to bring the API to life using the `api_run()` method: ``` r pa |> api_run() ``` You should see a message about your API running on your computer on port `8080`. The API will continue running in your R session even though you can still interact with the session. To stop the API from running, use `api_stop()`. If you're running this code locally on your personal machine, you should be able to open http://localhost:8080/echo/test or http://localhost:8080/plot in a web browser to test your new API endpoints. > If you're using a tool like RStudio Server to run your R code on a remote machine, you should see the [networking section](./security.html#networking) for help with visiting your API. The `/echo/test` endpoint should show output resembling the following. ```{r} #| echo: false #| results: asis pa <- api(file.path(me, "files/apis/01-01-quickstart.R")) req <- fiery::fake_request("http://localhost:8080/echo/test") res <- pa$test_request(req) code_chunk(res$body, "json") ``` The `/plot` endpoint will show you a simple plot of some data from the palmerpenguins dataset. ```{r} #| echo: false #| results: asis if (packageVersion("base") < "4.5.0") { cat("| The `plot/` endpoint requires the penguins dataset first introduced in R 4.5.0") } else { req <- fiery::fake_request("http://localhost:8080/plot") res <- pa$test_request(req) cat('') } ``` If you see something like the above: congratulations! You've just created your first Plumber API! You've already exercised your API from a web browser, but there's nothing stopping you from leveraging this API from third-party tools or a client developed in R or any other programming language. ## Specifying the Inputs You may have noticed that the functions that define our endpoints accept parameters. These parameters allow us to customize the behavior of our endpoints. In the example above we use two different ways of specifying the outputs. For the `/echo/...` handler we take a variable part of the path and use this as an argument. You can see above that "test" is used in the resulting json object and had we provided a different path, e.g. `/echo/plumber` then "plumber" would have been used instead. In the other handler we rely on the "query string" to pass in an optional argument. If you visit http://localhost:8080/plot?spec=Adelie, you should see a similar graph to the one you saw before, but now the dataset has been filtered to only include the "Adelie" species in the palmerpenguins dataset. ```{r} #| echo: false #| results: asis if (packageVersion("base") < "4.5.0") { cat("| The `plot/` endpoint requires the penguins dataset first introduced in R 4.5.0") } else { req <- fiery::fake_request("http://localhost:8080/plot?spec=Adelie") res <- pa$test_request(req) cat('') } ``` As you might have guessed, the `spec=Adelie` portion of the URL sets the `spec` element in `query` to `Adelie`. More details on how Plumber processes inputs are available in the [Routing & Input article](./routing-and-input.html). ## Customizing The Output In the previous example, you saw one endpoint that rendered into JSON and one that produced an image. However, if you visited the the `/echo/test` path from a web browser you probably saw the result rendered as HTML. Plumber2 comes with many different serializers that can convert the result of your R code into a variety of formats. It is possible for the client (e.g. the web browser) to request a specific format, and if the server knows the format it will generally oblige. A browser usually prefers HTML, so that request wins over the default JSON representation that plumber2 uses. If you wish to ensure that JSON is always returned you can make that explicit using `@serializer json` which sets JSON as the only known output format. For graphical output you have to always be explicit so that plumber knows to set up a graphic device and capture the output. In the example above we use `@serializer png` but other graphic serializers are available as well, e.g. `@serializer jpeg`. While it is neat to be able to provide results in a variety of formats it is unlikely that all formats makes sense to the output you produce so you should generally be mindful of the serializers you provide ```{r} #| eval: false #| code: !expr readLines("files/apis/01-02-html.R") ``` This handler would always produce HTML when called. ```{r} #| echo: false #| results: asis pa <- api(file.path(me, "files/apis/01-02-html.R")) req <- fiery::fake_request("http://localhost:8080/hello") res <- pa$test_request(req) code_chunk(res$body, "html") ``` You can even provide your own custom serializers and define how to translate the R object produced by your handler into the bits that will produce Plumber's HTTP response. You can find more details in the [Rendering & Output article](./rendering-output.html).