Thus far, we have been focused on building application that run in the browser without consideration for what we can do on the server. It's time to rectify that.
A web server can simply be thought of as a server that delivers web pages (HTML, CSS, JavaScript, images, etc) to a browser when requested; but that would be too reductionist. Modern web servers do much much more from handling user management and logins, to storing and making data available, to handling messaging between users, and so on.
node.js is a JavaScript engine that can be used to make a variety of applications but it especially excels at server side web development.
We won't be using node.js straight up just yet but will instead be leveraging the Servi IDE and Servi.js.
Servi.js is a "micro-framework" for creating web servers using node.js, Servi IDE is a graphical user interface/integrated development environment for building applications that use servi.js.
One of the nicest things about the Servi IDE is that is is a self contained node.js environment, application runner, and browser all in one. Simply write a line of code, click the run button and test it right there.
The simplest thing that we can do with Servi is to serve flat files, such as regular HTML pages. In order to do so, save your servi project wherever you would like and create a directory inside of it. Call it something like "files" or "public" and place your HTML, CSS, JavaScript, images, and any other assets you need to be sent to the browser as is inside that directory. Then go into the server.js file and write this line of code:
serveFiles("directory_containing_the_files");After you save and run it, you should be able to request any files that are in that directory through your browser.
Of course, serving flat files is all well and good but to really get anything out of server side web programming, we have to go a step further.
To dynamically serve content, we can create function called "run" and invoking the "respond" method with the contents we want to serve:
function run(request) { request.respond("Hello World"); }
We can also create what are called "routes" to run different functions depending on the request
route('/one',handleOne); route('/two',handleTwo); route('/buckle/my/shoe',buckleMyShoe); function handleOne(request) { console.log("one"); request.respond("thanks one"); } function handleTwo(request) { console.log("two"); request.respond("thanks two"); } function buckleMyShoe(request) { console.log("buckle my shoe"); request.respond("buckle my shoe"); }
Query strings are a portion of a URL that is used to pass data in from the client side to the server side.
http://www.hithere.com/somescript?myname=shawn&city=brooklynIn the above example, we are sending 2 name/value pairs to the server. The first has the name "name" and the value "shawn" and the second has the name "city" and the value "brooklyn".
Servi can be used to access that data:
route('/somescript',handleQuery); function handleQuery(request) { // request.params.name // request.params.city var response = "The name submitted was: " + request.params.name + "\n"; response += "The city submitted was: " + request.params.city; request.respond(response); }
Forms can be used to create these types of queries. The form "method" is "GET" and the content will show up in the URL submitted to the server. This makes it easy to bookmark or share URLs with results:
<form method="GET" action="/somescript"> <input type="text" name="name" value="Your Name"> <input type="text" name="city" value="Your City"> <input type="submit" name="submit" value="Submit"> </form>
If we change the form "method" to "POST" the content is sent in a manner that we can't see in the URL. You would use this method for information that shouldn't be submitted more than once (such as entering a comment) or that shouldn't be visible in the URL such as username/password:
<form method="POST" action="/somescript"> <input type="text" name="name" value="Your Name"> <input type="text" name="city" value="Your City"> <input type="submit" name="submit" value="Submit"> </form>In Servi, we access data that comes in via a POST query slightly differently:
route('/somescript',handleQuery); function handleQuery(request) { // request.params.name // request.params.city var response = "The name submitted was: " + request.fields.name + "\n"; response += "The city submitted was: " + request.fields.city; request.respond(response); }
Servi has a built in database which uses flat JSON files and has the same API as MongoDB.
// Create or use the existing database var infoDB = useDatabase("namescities"); route('/somescript',handleQuery); function handleQuery(request) { // request.params.name // request.params.city var response = "The name submitted was: " + request.fields.name + "\n"; response += "The city submitted was: " + request.fields.city; // Create a JSON object to hold the data var dataToSave = {name: request.fields.name, city: request.fields.city}; // Save the data to the database infoDB.add(dataToSave); request.respond(response); }
To pull data out of the database:
// Create or use the existing database var infoDB = useDatabase("namescities"); route('/viewdata',viewData); function viewData(request) { infoDB.getAll(gotData); function gotData(theData) { var response = ""; for (i = 0; i < theData.length; i++) { response += theData[i].name + " lives in " + theData.city + "\n"; } request.respond(response); } }
You can render the data retrieved into an HTML file for display as well. By default, Servi uses the EJS template rendering system which is a way that you can actually write JavaScript code to bind data within HTML:
// Create or use the existing database var infoDB = useDatabase("namescities"); route('/viewdata',viewData); function viewData(request) { infoDB.getAll(gotData); function gotData(theData) { // Give the data from the database a name that we can access in our template var toRender = {cityData: theData}; // Render it request.render("template.html",toRender); } }Here is the HTML
<html> <head> <title>Names and Cities</title> </head> <body> <h1>Names and Cities</h1> <% for (i = 0; i < cityData.length; i++) { %> <%= cityData[i].name %> lives in <%= cityData[i].city %><br /> <% } %> </body> </html>As you can see, the HTML contains JavaScript code to access the data we are passing in. This code is surrounded by <% and %> symbols. We use <= variable %> to print or output data.
Here is a full example of a commenting system: server.js:
var commentsdb = useDatabase("comments"); serveFiles("public"); route('/', getComments); route('/comments', getComments); route('/savecomment', saveComment); function getComments(request) { console.log("getComments"); commentsdb.getAll(gotComments); function gotComments(allComments) { // Format the data from the database so we can access it easily in our template var toRender = {title: "Comments", allComments: allComments}; request.render("index.html",toRender); } } function saveComment(request) { console.log("saveComment: " + request.params.name + " " + request.params.comment); commentsdb.add({name: request.params.name, comment: request.params.comment}); request.redirect("/comments"); }index.html:
<html> <head> <title><%=title%></title> </head> <body> <h1>Comments</h1> <% for (i = 0; i < allComments.length; i++) { %> <%= allComments[i].name %> <%= allComments[i].comment %><br /> <% } %> <form method="GET" action="/saveComment"> <input type="text" name="name" value="Your Name"> <input type="text" name="comment" value="Comments"> <input type="submit" name="submit" value="Submit"> </form> </body> </html>