DCMS is a REST API serving the Dyalog website and video library. It provides search and recommendation endpoints over a database of videos, presenters, and events, plus a CRUD interface for managing that content. The service is written in Dyalog APL and runs under Docker, using Jarvis for HTTP and MariaDB for storage. DCMS doubles as a worked example of how these Dyalog tools fit together.
This README is for developers building a frontend against the API. Contributors working on DCMS itself should start at docs/README.md.
- Live service with interactive API documentation (Swagger UI)
- OpenAPI specification
- DYNA talk: "An APL App End to End"
You need Docker and a local clone. No YouTube credentials are required: the mock service Admin.MOCKYT.Run starts automatically with the tests so that you can test and develop offline.
-
Create
secrets/secrets.json5. For local dummy-data use, placeholder values are fine — see config.md for the format. -
Build dependencies and start the stack:
./dev -i
The
-iinstalls the Tatin and NuGet dependencies; drop it on later runs. -
Connect to RIDE at
http://localhost:4502and seed the database:Admin.RunTests 1
RunTestsclears the database, inserts 100 rows of generated dummy data (viaInsertDummyData), builds the search and recommendation caches, and runs the test suite. The1keeps the session alive afterwards.
The API is now at http://localhost:8081. Reset to an empty database at any time with:
Admin.TESTS.ClearDataTo work against live YouTube data instead of MockYT, obtain a YouTube Data API v3 key and set youtube_key in secrets/secrets.json5 (see config.md). Point the YOUTUBE configuration key at the real API; dev.dcfg overrides it to the local MockYT server by default. With a valid key, POST /admin/refresh imports live video data. This is optional — MockYT covers normal local use.
GET /videos searches the video library. It needs no authentication and returns a paginated JSON result. Query parameters include search (full-text), event, presenter_id, from, to, sort (relevance, newest, oldest), page, and per_page. presenter_id takes a comma-separated list, matched inclusively: presenter_id=12,34 returns videos presented by either. The Swagger UI and OpenAPI spec document every endpoint and field.
The examples below hit the local stack. Against the live service, replace http://localhost:8081 with https://dcms.dyalog.com.
APL, with HttpCommand:
⎕←(HttpCommand.Get 'http://localhost:8081/videos?search=apl&per_page=5').Datacurl:
curl 'http://localhost:8081/videos?search=apl&per_page=5'JavaScript, with fetch:
const res = await fetch("http://localhost:8081/videos?search=apl&per_page=5");
const data = await res.json();The query routes (/videos, /events, /presenters, /version) are public. The /crud, /schema and /admin routes require an X-API-Key request header. A path in neither group is rejected with 404 before it reaches the router.
The read/query endpoints are served from an in-memory cache rebuilt daily, and carry validation headers:
| Header | Value |
|---|---|
ETag |
Weak tag, e.g. W/"20260811094500000", changing only when the cached data changes |
Last-Modified |
HTTP-date of the cache the response was served from |
Cache-Control |
public, no-cache means store the response, but revalidate before reusing it |
Vary |
Accept-Encoding, Origin |
A request sent with the tag in an If-None-Match header, or the date as If-Modified-Since, and an unchanged cache answers 304 Not Modified with no body:
curl -H 'If-None-Match: W/"20260811094500000"' 'http://localhost:8081/videos?per_page=5'On a cold start with no cache yet to serve, these endpoints answer 503 with Cache is being built. Retry shortly.
While the system does not itself collect or store any user data, this system uses YouTube API Services to access videos and their descriptions. By using this system, you agree to the YouTube Terms of Service.