Index › 6. Kitten’s Interactive Shell (REPL) 🐢
Explore Kitten’s interactive shell (REPL) to inspect your database and get introduced to the concept of Kitten components.
Topics covered
- How to launch and use Kitten’s interactive shell (REPL).
- Exploring the global
kittenobject. - Kitten components and component properties (props).
- Inspecting your app’s database.
- Listening for events on database tables.
- More insight into Kitten’s router and how to manipulate it with the shell at runtime.
Let’s take a little aside to explore another neat feature of Kitten: its interactive shell.
If you’re of a geekier persuasion, you may know this by a different name, a Read-Evaluate-Print Loop or a REPL.
When you start Kitten, you will notice a message telling you that you can press the s key to start an interactive shell.
If you want to connect to the interactive shell in production (when your app is running as a systemd service/daemon), ssh into your server and run kitten shell.
Kitten’s interactive shell allows you to use a shell like Node.js’s own shell (the one you get when you type node without any arguments) to interactively explore that state of your running Kitten app.
In fact, Kitten uses Node.js’s REPL.
So let’s use it to take a look under the covers to play with Kitten’s features interactively, from the command-line, and also to see how the database is being updated.
While Kitten is running, press the s key to launch Kitten’s shell.

Kitten welcomes you to the REPL and shows you a key of the icons used in it. The welcome message tells you that you can use the
.helpcommand for assistance. Run that, and you will see a list that looks like the following:.break Sometimes you get stuck, this gets you out .clear Alias for .break .editor Enter editor mode .exit Exit the REPL .help Print this help message .id Show your Small Web place ID (and create it and your secret if necessary). .load Load JS from a file into the REPL session .ls List keys of global kitten object. .save Save all evaluated commands in this REPL session to a file .settings Display the Kitten Settings page link .sign-in Display the Kitten sign-in page link .sign-out Display the Kitten sign out page link .stats Show current statistics on Kitten serverThe entries highlighted above are the ones that are specific to Kitten’s REPL. All the others are ones inherited from the Node.js REPL that Kitten’s REPL is built on.
Let’s use the
.lscommand to see the keys of the globalkittenobject:[ 'version', 'log', '__db', 'domain', 'port', 'requests', 'Component', 'Page', 'Upload', 'slugify', 'yaml', 'pages', 'utils', 'app', 'projectIdentifier', 'paths', 'deploy', 'uploadLimits', 'WebSocket', 'html', 'markAsTrusted', 'icons', 'crypto', 'libraries', 'page', 'events', 'css', 'js', 'markdown', 'md', 'safelyAddUntrustedHtml', 'sanitise', 'components', '$showEventsOnTable', '___valueAtKeypath', '_db', 'uploads', 'package', 'db', '__replIsActive' ]Look at all those lovely objects you have to play with!
The most useful ones for debugging a running Kitten app are shown as highlighted in the list above.
The
.lscommand is just an alias for entering the more verboseObject.keys(kitten)statement into to the shell.Through the Kitten REPL, you have full access to the running Kitten server’s internals.
Try the following statement:
kitten.versionYou should see the output of an object with Kitten’s version information.
You can use the ↑ and ↓ (up and down arrow keys) to cycle through your shell’s history. History is saved scoped to your Kitten application’s project identifier (which includes the domain and port that it is running on) so it will still be there the next time you serve your app using Kitten.
How about we explore how things work interactively? We haven’t seen it yet, but you can use components in Kitten to break up your pages into reusable parts. Let’s create a simple one that greets you by name. Enter the following statement into Kitten’s shell:
hello = ({ name = 'Aral' }={}) => kitten.html`<h1>Hello, ${name}</h1>`You should see the shell confirm that you’ve just created a function called hello:

We just created a Kitten component interactively. Simple, stateless Kitten components like the one we just created are just render functions that return
kitten.html. Later, we’ll see how you can also create stateful components, which are classes.You should type the above all as one line. However, if you want to use multiple lines, you can enter the shell’s editor mode using the
.editorcommand. For a full list of available commands, use the.helpcommand.Now, let’s use the component:
kitten.html`<${hello} />`You should see it print out:
<h1>Hello, Aral</h1>That’s pretty neat. We called it without passing a name, and it defaulted to Aral. Given it’s highly unlikely that your name is Aral too, let’s now call it while passing it your name as a property (or prop for short):
kitten.html`<${hello} name='your name'/>`Once you’ve entered that, replacing your name with, well, your name, you should see:
<h1>Hello, your name</h1>And now you pretty much know how components work in Kitten. And you learned it using the command-line. Don’t you feel geeky?
But let’s not get too distracted… we were going to look at our
kittenstable in the database. Let’s do that next.Enter the following statement:
kitten.db.kittensYou should see output that resembles the following:
Proxy [ { count: 20 }, { get: [Function: bound getHandler], set: [Function: bound setHandler], defineProperty: [Function: bound definePropertyHandler], deleteProperty: [Function: bound deletePropertyHandler] } ]So that’s what your
kittenstable looks like in memory.Don’t worry too much about the
Proxystuff, it’s how JSDB works its magic. If you’re interested in how it all works under the hood, read the dispelling the magic and pointing out a couple of gotchas section of the JSDB documentation.Take a look at the
countproperty.Let’s change it and see what happens:
kitten.db.kittens.count = 1Now refresh the page in your browser, and you should see that you’re back to having one kitten. (The page shows the count you just set and then adds one to it for your next visit.)
So you can directly update your database from the shell. That’s fun!
Like all fun things, laughter can turn to tears if you try this out on your production servers. This is not to say that you shouldn’t but just be aware of what you’re doing and maybe take a backup of your database before you go poking around it.
Next, let’s see how we can see the database being updated in real time as you hit your page. We’ll use the Kitten Introspection API to get notified whenever a property changes on the
kittenstable:kitten.$showEventsOnTable('kittens')When you refresh the page in the browser, you should see output similar to the following in the Kitten shell:
💾 Kittens updated in memory. { count: 20 } => count = 21 💾 Kittens persisted to disk. kitten.db.kittens.count = 21 Persisted as: '_[[`count`]] = 21;\n'We borrow the relational database term “table” to refer to such objects when they’re created directly on the database (
kitten.db). Each JSDB table is stored in its own file.So how does the
$showEventsOnTable()method work?In JSDB, we can listen for events on tables. One of these events is the
persistevent that tells us when a value has been persisted to disk.Follow along in the shell to see how it works as this is a feature you might want to use when building Kitten apps:
First, enter editor mode in the shell:
.editorThen, add the following code and press ctrl+d:
function onPersist ({ type, table, keypath, value }) { console.info(`${type} ${table.tableName}.${keypath}: ${value}`) } kittensTable = kitten.db.kittens['__table__'] JSTable = kittensTable.constructor kittensTable.addListener(JSTable.PERSIST, onPersist)Finally, go and refresh the page in the browser, and you should see updates similar to the following logged in your console:
update kittens.count: 22 update kittens.count: 23 update kittens.count: 24 update kittens.count: 25
And now for the reason we created the listener separately instead of inline, so we can remove it and stop getting updates:
kittensTable.removeListener(JSTable.PERSIST, onPersist)Finally, let’s dive in really deep and actually call the route interactively from the shell.
You will rarely have to do this but here’s a little example to show you how powerful Kitten’s interactive shell is. We’re now going to dive into Kitten’s app router (which uses Trouter as extended by Polka) and call our index page route interactively from the shell.
First, let’s see which handlers run for
GETrequest for the base route of our app (/):kitten.app.router.find('GET', '/')You should see output similar to the following:
{ params: {}, handlers: [ [Function: nosniff], [Function: requestLog], [Function: corsMiddleware], [Function: stats], [Function: requestAndResponseHelpers], [Function: domainRedirect], [Function: trailingSlashes], [Function: localRedirect], [AsyncFunction: sessions],[Function: wrappedHandler] { filePath: '/Users/aral/Projects/kitten/app/examples/persisted-kitten-count/index.page.js' }] }So there are quite a few handlers (called “middleware”) that run before the handler we defined in index.page.js (which is highlighted in the list above), and they do important things that let us concentrate on just what we want to do.
If you want to see exactly what any of them do, you can list their source. Let’s see what the ninth one does (remember, arrays are indexed starting at zero):
console.log( kitten.app.router.find('GET', '/').handlers[8].toString() )You should see a function listing similar to the following:
async function sessions(request, response, next) { const session = await Sessions.getInstance().session(request); request.session = request.upgrade === true ? session : storeOnUse(session, request, response); next(); }That’s the piece of middleware that puts the magic
sessionproperty into yourrequestobjects. It doesn’t store the session. For a request that isn’t an upgrade, it wraps the session in a proxy that stores it the first time your route writes to it or reads itsid.So maybe there isn’t actually any magic here at all, and you could, if you wanted to, learn exactly how Kitten does things. (Either interactively, through the shell, or by browsing Kitten’s source code.)
To learn how to use sessions in Kitten, please see the Sessions Tutorial.
The route we’re interested is the one we wrote, however, and if you count the items in the array, it’s the one at index
9. (Kitten wraps each route’s handler inwrappedHandler, which decides whether the request requires sign-in and then runs your route.)Before we call it, we need to know that it expects a
KittenRequestinstance and aKittenResponseinstance as positional arguments. However, all it will access in this call is the request’surl,path, andsessionproperties and the response’ssetHeader(),writeHead(), andend()methods, so we can simply mock those objects for our purposes.Again in
.editormode, add the following code and press ctrl+d to run it:getRouteHandlers = kitten.app.router.find('GET', '/').handlers indexPageHandler = getRouteHandlers[9] indexPageHandler( /* request = */ { url: '/', path: '/', session: {} }, /* response = */ { setHeader: () => {}, writeHead: () => {}, end: html => console.info(html) } )Mocking aspects of requests and responses is something we do quite often when writing Kitten’s unit tests. For an example, see this PageRoute test.
By issuing that command, you’re simulating a
GETrequest to your route. And, instead of the browser rendering it, we simply output the resulting HTML to the console. So you should see output that resembles the following in your terminal (with the boot id of your Kitten process in place of the…indata-kitten-boot-id):<!doctype html> <html lang="en"> <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><link rel="icon" href="data:,"><title>Kitten count</title><script type="module" src="/🐱/library/kitten-1.js"></script><script type='module' src='/🐱/library/kitten-hot-reload-1.js' data-kitten-boot-id='…'></script></head> <body><h1>Kitten count</h1> <p>🐱️🐱️🐱️🐱️🐱️🐱️🐱️🐱️🐱️🐱️🐱️🐱️🐱️🐱️🐱️🐱️🐱️🐱️🐱️🐱️🐱️🐱️🐱️🐱️🐱️🐱️🐱️🐱️🐱️🐱️🐱️🐱️🐱️🐱️🐱️🐱️🐱️🐱️🐱️🐱️🐱️🐱️</p><div id="kitten-toast" aria-live="assertive" hx-swap-oob="morph settle:1.25s" hx-ext="morph"><style inline> #kitten-toast.settling { opacity: 1; top: 1em; } …If you are still introspecting the
kittenstable, you should also see a message telling you that the kitten count has been updated.Run the command a few more times and notice that the number of kittens increases, just as if we had hit it in the browser.
In fact, go hit it in the browser to confirm that you’re working with the same kitten count.
There’s a lot you can do with Kitten’s interactive shell and I hope you play around with it both to help debug your Small Web apps and also to learn more about how Kitten works in general.
Well, that got geeky very quickly. How about we return to the relative safety of our Kitten Count example again in the next tutorial and use it to learn about conditional statements. If you want to that is. Yes? Then let’s proceed…
Next tutorial: Conditionals