HTTP server for the Lisp-Stat system
Explore the docs »
Report Bug
·
Request Feature
LS-Server is an HTTP server for the Lisp-Stat system, based on the Hunchentoot HTTP server. It provides four capabilities:
- Plot viewing — serve plots created with the
plot/vegasystem as interactive Vega-Embed pages, with a single-page application (SPA) plot gallery featuring PNG thumbnails and a sidebar navigator - Data frame viewing and editing — view data frames in an editable Handsontable grid with batch editing
- CSV / JSON data serving — serve data-frame contents in CSV, JSON, Vega-JSON, or s-expression format via Accept header content negotiation
- Vega-Lite spec serving — serve Vega-Lite plot specifications for existing plots
- REPL integration — when the server is running,
print-objectdisplays clickable URLs for data frames and plots
Content negotiation is used to provide the best matching format. For example a client can request a data-frame in CSV or Vega JSON format. CSV is the default for data.
- Hunchentoot — HTTP server
- Yason — JSON encoding/decoding
- CL-WHO — HTML generation
- CL-PPCRE — Regular expressions
- data-frame — Data frame library
- dfio — Data frame I/O (CSV, etc.)
- plot/vega — Vega-Lite plot specifications
(ql:quickload :ls-server- Clone the repository into a directory ASDF knows about:
cd ~/common-lisp && \
git clone https://github.com/Lisp-Stat/ls-server.git- Reset the ASDF source-registry (from the REPL):
(asdf:clear-source-registry)- Load the system and its dependencies:
(ql:quickload :ls-server) ;get dependices via quicklispStart the server on the default port (20202):
(ls-server:start-server)Start on a custom port:
(ls-server:start-server :port 8080)Stop the server:
(ls-server:stop-server)Once started, open http://localhost:20202/ in a browser to see the landing page with links to plots and data frames.
When the server is running, print-object automatically appends URLs to data frame and plot output:
LS-USER> mtcars
#<DATA-FRAME (32 observations of 11 variables)
Motor Trend Car Road Tests
http://localhost:20202/table/MTCARS>
LS-USER> my-plot
#<VEGA-PLOT "my-plot"
http://localhost:20202/plot/my-plot>Depending on your terminal, you may be able to click on the link directly and open it.
Load the Lisp-Stat example datasets to have data frames available:
(ql:quickload :lisp-stat)
(ql:quickload :quick-plot)
(data :vgcars)This loads standard datasets like mtcars and iris which are then accessible through the server's data and table endpoints.
Now create a plot:
(qplot 'cars-basic vgcars
`(:title "Horsepower vs. MPG")
(point :horsepower :miles-per-gallon))You should see several data sets in the web interface.
| Method | Path | Description |
|---|---|---|
GET |
/data |
JSON array of available data frame names |
GET |
/data/<name> |
Data frame contents (format via Accept header) |
PUT |
/data/<name> |
Replace data frame with JSON array of row objects |
PATCH |
/data/<name> |
Update specific cells: {"updates": [{"row": N, "column": "col", "value": V}]} |
Supported Accept types for GET /data/<name>:
| Accept Header | Format |
|---|---|
text/csv (default) |
CSV with header row |
application/json |
JSON array of row objects |
application/vega-json |
Vega row-oriented JSON |
text/s-expression |
S-expression (Lisp-readable) |
(none or */*) |
CSV (default) |
| Method | Path | Description |
|---|---|---|
GET |
/table/<name> |
Interactive Handsontable HTML page for the data frame |
The table page displays the data-frame documentation (if any) in a collapsible section and loads data via the /data/<name> JSON endpoint. Edits are made in the grid and submitted as a batch via Save/Cancel buttons.
| Method | Path | Description |
|---|---|---|
GET |
/plot |
JSON array of available plot names |
GET |
/plot/<name> |
Plot page (HTML with Vega-Embed or raw Vega-JSON spec) |
DELETE |
/plot/<name> |
Remove plot from the plot registry |
Supported Accept types for GET /plot/<name>:
| Accept Header | Format |
|---|---|
text/html (default) |
Full HTML page with embedded Vega-Embed viewer |
application/vega-json |
Raw Vega-Lite JSON specification |
| Method | Path | Description |
|---|---|---|
GET |
/ |
Landing page with links to plots and tables |
GET |
/plots |
SPA plot gallery with thumbnail sidebar |
GET |
/tables |
Data frames index page with sidebar |
GET |
/health |
Health check endpoint (returns {"status":"ok"}) |
ls-server/
├── ls-server.asd # ASDF system definition
├── src/
│ ├── pkgdcl.lisp # Package declaration (exports)
│ ├── config.lisp # Configuration parameters (*default-port*, logging, etc.)
│ ├── negotiation.lisp # Accept header parsing & content negotiation
│ ├── server.lisp # Server start/stop, *server*
│ ├── health.lisp # Health check endpoint
│ ├── data-routes.lisp # /data endpoints (GET/PUT/PATCH), Yason extensions
│ ├── html.lisp # HTML page generators (landing, table, plot, index pages)
│ ├── plot-routes.lisp # /plot endpoints (GET/DELETE), plot lookup
│ ├── table-routes.lisp # /table endpoint
│ ├── landing-routes.lisp # /, /plots, /tables route handlers
│ └── print-extensions.lisp # print-object :around methods for REPL URLs
└── tests/
├── test-package.lisp # Test package declaration
├── main.lisp # Test runner, root suite
├── health-tests.lisp # Health endpoint tests, test server helpers
├── server-tests.lisp # Server lifecycle tests
├── negotiation-tests.lisp # Content negotiation unit tests
├── data-tests.lisp # Data endpoint integration tests
├── plot-tests.lisp # Plot endpoint integration tests
├── table-tests.lisp # Table endpoint integration tests
├── landing-tests.lisp # Landing & index page tests
└── print-tests.lisp # print-object URL extension tests
All configuration parameters are defined in src/config.lisp and can be set before starting the server:
| Parameter | Default | Description |
|---|---|---|
*default-port* |
20202 |
Default port for the HTTP server |
*server-host* |
"localhost" |
Hostname used in printed URLs |
*access-log-destination* |
nil |
Hunchentoot access log destination (pathname string or nil for stderr) |
*message-log-destination* |
nil |
Hunchentoot message log destination (pathname string or nil for stderr) |
Example — redirect logs to files:
(setf ls-server:*access-log-destination* "/tmp/ls-access.log")
(setf ls-server:*message-log-destination* "/tmp/ls-messages.log")
(ls-server:start-server)Run all tests:
(asdf:test-system "ls-server")Run a specific test suite:
(let ((clunit:*test-output-stream* *standard-output*))
(clunit:run-suite ('ls-server-tests::data-suite) :report-progress t))The test framework is clunit2. Integration tests start a test server on port 20293 and use Dexador for HTTP requests.
This system is part of the Lisp-Stat project; that should be your first stop for information. Also see the resources and community page for more information.
Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are greatly appreciated. Please see CONTRIBUTING for details on the code of conduct, and the process for submitting pull requests.
Distributed under the MS-PL License. See LICENSE for more information.
Project Link: https://github.com/lisp-stat/ls-server