Notoriously Pedantic Magistrate


    0.6.2 • Public • Published


    Logo made by Yana Smirnova

    npm version


    npm install -g pjs-cli



    By default, pjs will listen on the port 8080 (or process.env.PORT) and read the current folder. You can change this settings in http://localhost/_/, take a look at the usage documentation.

    Inside the folder where pjs is reading you can prototype any node.js applications thanks to the .pjs files (see examples below).

    For using pjs as a daemon, your can use these commands:

    • pjs start (start pjs as a daemon)
    • pjs stop
    • pjs restart
    • pjs status


    To launch the examples on your computer, install first pjs-cli and then :

    git clone
    cd pjs/examples/

    You can visit http://localhost:8080 too run all the examples presented below.

    What are these .pjs files ?

    It's 90% of EJS templates with a done() method for making every asynchronous operations possible! (take a look at the examples #2)

    Example 1 - Basis

    Let's say that I have a file name hello.pjs in my current folder:

    <% var foo = 'PJS'; %>
    Hello <%= foo %>!

    I launch the cli in my current folder: pjs

    Then, I visit : http://localhost:8080/hello.pjs

    Result: Hello PJS!

    Example 2 - Asynchronous

    What about if we want something a little bit more asynchronous?

    File async.pjs:

    var request = require('request'),
    request.get('', function (err, res, body) {
      // body = { "name": "PJS" }
      name = JSON.parse(body).name;
    Hello <%= name %>!

    As you can see, I can require a module, for that, please make sure to run npm install request in the current directory before visiting http://localhost:8080/async.pjs

    The result will be : Hello PJS!

    Notice the done(); method, it is important here for PJS to wait until the request is completed and the name set before going further in the template.

    Example 3 - Includes

    You may also want to include other .pjs files, that's why you can use the <% include your_file.pjs %> directly in your templates.

    File include.pjs:

    <p>I'm including hello.pjs</p>
    <%- include hello.pjs %>

    Result on http://localhost:8080/include.pjs

    I'm including hello.pjs
    Hello bar!

    Actually, only this pre-processor include works. <%- include(file, { ... }); %> is not available yet.

    Example 4 - REQUEST

    Inside each .pjs file, you have access of the REQUEST variable. It contains some of the properties listed from Express (;

    File: request.pjs

    <% if (REQUEST.method === 'POST') { %>
      <p><b>New todo:</b> <%= REQUEST.body.todo %></p>
    <% } %>
    <form method="post">
      <input type="text" name="todo" placeholder="Learn Piano..." />
      <button type="submit">Add todo</button>

    If you fill the input and click on "Add todo", the condition will pass and "New todo:" will be displayed on the screen with the content of the input.

    List of the properties available in REQUEST:

    - baseUrl
    - body
    - headers
    - hostname
    - fresh
    - ip
    - ips
    - method
    - originalUrl
    - path
    - protocol
    - query
    - secure
    - stale
    - subdomains
    - url
    - xhr


    • REQ is equal to REQUEST.
    • FORM which is REQ.body merge into REQ.query
    • METHOD is equal to REQUEST.method

    Example 5 - RESPONSE

    Sometimes you want to send back a custom status code or even a custom header. You can use the RESPONSE (alias: RES) object for this, here the current method available:

    - .status(code) // Set the HTTP status for the response
    - .header(field [, value]) // Same as res.set() for Express
    - .type(type) // Same as res.type() for Express

    File response.pjs:

    RESPONSE.header('Custom', 'Hi!');
    I'm a page with a custom status code and header!

    If you go on http://localhost:8080/response.pjs with POSTMAN, you will see the status code 404 with the custom header and the Content-Type set to plain/text.

    Example 6 - SESSION

    In your PJS templates, you have also access to SESSION. This is useful for creating small apps for prototyping.

    Here a Todo app with the use of the sessions with PJS.

    File session.pjs:

    SESSION.todos = SESSION.todos || [];
    if (REQUEST.method === 'POST' && REQUEST.body.todo) {
    if (REQUEST.method === 'GET' && REQUEST.query.index) {
      SESSION.todos = SESSION.todos.filter(function (todo, index) {
        return index !== Number(REQUEST.query.index);
    <form method="post">
      <input type="text" name="todo" placeholder="Learn Piano..." />
      <button type="submit">Add todo</button>
      <% SESSION.todos.forEach(function (todo, index) { %>
        <li><%= todo %> - <a href="?index=<%- index %>">x</a></li>
      <% }); %>

    Go to http://localhost:8080/session.pjs to se it working.

    SESSION is equivalent of req.session in Express. PJS use session-file-store to persist the sessions in files, so you can restart pjs without worrying about the sessions.

    Example 7 - JSON

    For sending back a JSON object, simply display it with <%- yourJSON %>.

    File json.pjs:

    <%- FORM %>

    FORM is an alias of REQ.body merged into REQ.query.

    Try to visit http://localhost:8080/json?foo=bar&why=not

    As you can see in the url, the .jps is optional.

    Example 8 - Files

    PJS let you upload files via multer. The maximum number of files is 10 and each file size 5 MB. The files are stored in the /tmp folder and are automatically deleted when the page is rendered.

    You can access the files via REQUEST.files. Important, the form as to be multipart (multipart/form-data).

    File files.pjs:

    <% if (METHOD === 'POST' && REQ.files) { %>
    <pre><%- JSON.stringify(REQ.files, undefined, 2) %></pre>
    <% } %>
    <form method="post" enctype="multipart/form-data">
      <p>File: <input type="file" name="file" /></p>
      <button type="submit">Upload</button>

    You can go on http://localhost:8080/files.pjs, choose a file and click Upload, you will see all the data inside REQUEST.files.


        "fieldname": "file",
        "originalname": "pjs-image.jpg",
        "encoding": "7bit",
        "mimetype": "image/jpeg",
        "destination": "/tmp",
        "filename": "b52fb2303738fa17c0a2c8593de76798",
        "path": "/tmp/b52fb2303738fa17c0a2c8593de76798",
        "size": 71448

    About PJS

    As said before, PJS is mostly for quick prototyping and has no use case for production.

    The idea was born after reading this article by VJEUX:


    • Trying vhost for rendering different folders (maybe not using res.render instead with a vhost middleware?)
    • Handling 404, 500 errors via templates (404.pjs, 500.pjs) if exists, use: app.use(function(err, req, res, next) {...});
    • Config WinstonJS log system
    • ./.pjs/config.json have a lastUpdate field to know on the ping when the recluster has reloaded
    • Add tests + jslint (start server on test/pjs/ + call request and check source code) + Travis + + Bithound (dependencies + code) +
    • Documentation with gitbook
    • Central UI (only links to the modules): url/_ + Add basic auth on _ and _/*
    • UI for configuring PJS (url/_/config): Authentification, Server (default port, proxy, SSL, folder for public views + for each vhost), Upload (nbFiles, fileSize, folder), Sessions (Secret key, Store (Mongo/Redis/Memory/Files + config), Sockets (enable/disable + socket config file), Errors Handler (Winstog log files, Sentry config, etc.)
    • UI for configuring the Routes (url/_/routes): Router /api/users/:id -> render file api/users.pjs + add req.params inside REQ + FORM
    • UI for configuring the Node modules (url/_/npm): Listing installed modules, Install/Update/Remove (,version,repository,description,author)
    • UI for viewing/searching the logs (url/_/logs)
    • Finding a way of using the sockets and configuring them server-side (path of the file to use) + try/catch to avoid server error on startup
    • Video of presentation


    npm i pjs-cli

    DownloadsWeekly Downloads






    Last publish


    • atinux