Yet another web applications tutorial

While teaching myself how to develop a web application with SWI-Prolog, I’ve taken notes for future reference, which others may find useful since I’ve had to figure much of this out through trial and error.

My tutorial differs from Anne Ogborn’s at http://www.pathwayslms.com/swipltuts/html/index.html in that I’ve not used the html_write library (I’m ideologically opposed to templating), and have gone in directions I personally found initially difficult to grasp from the documentation (though which turned out to be very simple once I went aha!).

I’ve put my progress so far here for any suggestions or corrections. I don’t think it’s suitable for https://swish.swi-prolog.org since I assume one can’t run server code on it, so any suggestions for a good place to make it available once complete would be appreciated.

Step 1: Creating a simple SWI-Prolog web server

:- use_module(library(http/thread_httpd)).
:- use_module(library(http/http_dispatch)).
:- use_module(library(http/http_unix_daemon)).
:- use_module(library(http/http_files)).

:- multifile http:location/3.
http:location(files, root(files), []).
user:file_search_path(folders, library('images/styles/scripts')).

:- http_handler(root(.), http_reply_from_files('.', []), [prefix]). % defaults to index.html
:- http_handler(files(.), serve_files_in_directory(folders), [prefix]).

:- initialization http_daemon.

The lines of prolog above in a file called server.pl create a web server which can be started with, say:

swipl server.pl --port=3030 --pidfile=http.pid

and stopped with:

kill $(cat http.pid)

Assuming there is an index.html file in the same directory as server.pl and its associated files are in subdirectories called images, styles, and scripts, simply point your browser to http://localhost:3030/

One of the many advantages of serving static files rather than programmatically generated output is you can start with a skeleton index.html file,
say:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Page Title</title>
  <link rel="stylesheet" href="/styles/mystyle.css">
</head>
<body>
  <p>Hello World!</p>
  <script src="/scripts/myscript.js"></script>
</body>
</html>

and then see your site develop by simply refreshing your browser as you edit and add files without needing to constantly start and stop the server —
unless you add handlers by editing in the server.pl file, as in the next section.

If you’re new to html coding or need a refresher, I suggest Mozilla’s
Getting started with the Web.

Step 2: Advancing to AJAX

Since I last worked through developing a simple web application, the Javascript world has moved on a lot, with fetch simplifying the verbose syntax still used in
Mozilla’s getting started with Ajax tutorial.

I’ve added a handler linked to url /test in server.pl called test_handler which receives a Json object ‘{“userName”: some value}’.

Using library(http/http_json)'s http_read_json_dict/2, the value of userName is read, prefixed with "Hello, " and then returned housed in a Json object with key computedString.

This is hopefully sufficient to demonstrate how simple it is to use Ajax and Json with a SWI-Prolog server.

:- use_module(library(http/thread_httpd)).
:- use_module(library(http/http_dispatch)).
:- use_module(library(http/http_unix_daemon)).
:- use_module(library(http/http_files)).
:- use_module(library(http/http_json)).

:- multifile http:location/3.
http:location(files, root(files), []).
user:file_search_path(folders, library('images/styles/scripts')).

:- http_handler(root(.), http_reply_from_files('.', []), [prefix]). % defaults to index.html
:- http_handler(files(.), serve_files_in_directory(folders), [prefix]).
:- http_handler('/test', test_handler, []).

test_handler(Request) :-
  member(method(post), Request), !,
  http_read_json_dict(Request, _{userName:NameIn}),
  string_concat("Hello, ", NameIn, NameOut),
  reply_json_dict(_{computedString:NameOut}).

:- initialization http_daemon.

I’ve updated Mozilla’s beginner example to use fetch
instead of XMLHttpRequest in a file called main.js in the scripts subdirectory:

(function() {

  document.getElementById("ajaxButton").onclick = function() { 
      var userName = document.getElementById("ajaxTextbox").value;
      makeRequest('/test', userName); 
  };

  function makeRequest(url, userName) {
    fetch(url, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({"userName": userName})
    })
    .then(res => res.json())
    .then(response => alert(response.computedString))
    .catch(error => alert('Caught Exception: ' + error.description));
  }

})();

and the index.html file looks like this:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Ajax 1</title>
  <link rel="stylesheet" href="/styles/basic.css">
</head>
<body>
  <label>Your name: 
    <input type="text" id="ajaxTextbox" />
  </label>
  <span id="ajaxButton" style="cursor: pointer; text-decoration: underline">
    Make a request
  </span>
  <script src="/scripts/main.js"></script>
</body>
</html>

Step 3: Linking the server to a database (Postgresql in this example).

This is still work in progress. For the impatient, here’s the start of the next
section:

SWI-Prolog communicates with Postgres via the ODBC Interface.

Getting this to work requires having the PostgreSQL ODBC driver installed and an ~/.odbc.ini file. Details are at http://www.unixodbc.org/odbcinst.html

NOTE: MaxVarcharSize = 500 (or bigger) is needed, else ODBC truncates strings at 255 as explained at https://odbc.postgresql.org/docs/config.html

[postgresql]
Description         = Test to Postgres
Driver              = /usr/lib/psqlodbcw.so
Trace               = Yes
TraceFile           = sql.log
Database            = games
Servername          = localhost
UserName            = my_username
Password            = my_password
Port                = 5432
Protocol            = 9.4
ReadOnly            = No
RowVersioning       = No
ShowSystemTables    = No
ShowOidColumn       = No
FakeOidIndex        = No
ConnSettings        =
MaxVarcharSize      = 500

Queries via SWI-Prolog can then be done like so:

/*
roblaing=> select * from accounts;
 owner | balance | amount 
-------+---------+--------
 Mary  |  214.00 |       
 Bob   |   86.00 |       
*/

:- use_module(library(odbc)).

dbquery(Owners) :-
  odbc_connect('postgresql' , Connection, []),
  odbc_query(Connection, 'SELECT owner FROM accounts', Owners, [findall(Owner, row(Owner)]),
  odbc_disconnect(Connection).
4 Likes

Good job, this is nice and succint!

2 Likes

No advance on the tutorial, but I have made fairly good progress on a website I’m developing which I think is a good example of where Prolog is a better choice than Ruby/Python/Node etc because it uses AI to play strategy games.

The site is http://www.newsgames.biz/

So far I’ve got it to play checkers (not brilliantly, but reasonably well) and now I’m working on chess.

Developing it has been very educational for me. The trick is to explain what I’ve learned to other people, besides a computer.

4 Likes

Hi Joe,

I’ve found this pretty useful. Have you considered using Github/ Gitlab pages to host it? At least for now. Definitely write it down, I want to read it!

I’m a bit overwhelmed at the moment with my own application development, but I do like the idea of a SWI-Prolog blog where we can share posts on topics of interest as well as tutorials. If others in the community want such a service, perhaps we can develop it as a community?

Kind regards,
Paul

1 Like

Many thanks for the encouragement Paul. I found your blog on using SWI Prolog’s unit testing package very helpful.

I’ve started what will hopefully grow into a seven part series at https://github.com/roblaing/swipl-webapp-howto

Thanks Joe, I still check that post myself more often than I’m prepared to admit! But then this is why I made the blog, it’s as much notes for myself as others.

Keep us up-to-date as you write up the next 6 units!

1 Like

I’ve completed unit 2 which covers the basics of forms.

This all looks incredible.
If I can help, just let me know how. As soon as it finds a stable home, I’ll add a link to it from my tutorial if that’s OK.
If you want to host it on pathways, I’d be happy to give you shell access.
email me at anne@swi-prolog.org and I’ll set you up.

1 Like

I had some unexpected free time today, so I set about building a multi-user blog platform. My intention is to create a place where folks can easily share how-to’s, news, and other information. It’s to be an aid to community and publicity.

Only had one day on it, but it can handle editors and moderators. We’ve got rich-text editing, code-blocks, images, categories and tags. There’s no executable code segments, no comments, and no content yet! It’s also sans domain name, I was considering prologhub.pl and borrowing Poland’s extension seen as it’s the common Prolog file extension.

A sneak preview is available at: http://139.162.216.240/ But there’s really nothing to see until I can get some posts on there. I’ve got a full-on schedule for the next couple of days, but I’ll try to transfer a couple of my old posts over to kick-start it. I’d love to start adding some user accounts if you’re interested in creating content.

Update: OK, I put one old post on there, but now I really must get some sleep!

2 Likes

Looks nice. Just, there is a typo that needs correction, since it’s likely to confuse beginners:

my_append([H|Tail1], List, [H|Tail2]) :- append(Tail1, List, Tail2).

should be

my_append([H|Tail1], List, [H|Tail2]) :- my_append(Tail1, List, Tail2).

Many thanks Anne. I think where it is on github is a good permanent home for it since it’s easy for people to find the code. My knowledge of how to use git and github is fairly basic, but that’s all part of the learning exercise.

Thank you! Shows the perils of working 'til the early hours.

I only discovered the swish site through this forum which I found very user friendly while putting up:

https://swish.swi-prolog.org/p/yeQhnQSk.swinb

A problem I have with it is finding what other tutorials people have written, so a key issue seems to be the fragmentation of information out there, so I’m not sure if yet another website is a good idea.

Though I spend a lot of time on http://www.swi-prolog.org, I only found the page on quasiquoting via chance and google. I’ve subsequently become a big fan of quasiquoting, rewriting Unit 1 of my web app tutorial instead of soldiering ahead with the database stuff in Unit 3.

Anyone know how to use quasiquoting with odbc_query?

Someone has to write a quasi quoter for SQL. It isn’t needed that much as you can use parameterized queries though. Writing such a beast typically requires either writing a full parser or at least a tokenizer such that you know in what context you are interpolating Prolog values. This is required to embed the Prolog value in a safe way in the target language.

Handling statements from other languages as plain strings and manipulating them using simple string operations is one of the most well understood routes to security issues. It is very much common practice as it seems so easy. Still, don’t The main design guideline for the web services and most other interaction with other languages through strings is to avoid this and assemble expressions in the target language from data structures using code that fully supports the target language syntax and thus properly escapes special characters.

3 Likes

Agreed. It doesn’t make much difference for SQL since the queries are usually short. The key thing is to get examples of the various ways to do things out there, which is what I’m trying to do.

I’ve finally completed Unit 4 of my web application tutorial at https://github.com/roblaing/swipl-webapp-howto, which involved writing a safe user authentication system which involved learning both SWI Prolog’s and Javascript’s encryption libraries, along with brushing up my understanding of cookies… very educational for me which I hope other people can understand.

As far as I recall the Python-based Udacity course I’m redoing in SWI Prolog, the authentication unit was the toughest, and the rest just involved tying the previous parts together to create a fully functional blog.

But I now need to go back to Unit 2 and redo form basics correctly with http_redirect using my improved knowledge from writing the registration and login pages.

2 Likes