1561018824
We’ll be using Node.js and the Express framework to build a simple registration form with basic validation, which persists its data to a MongoDB database. We’ll add a view to list successful registration, which we’ll protect with basic HTTP authentication, and we’ll use Bootstrap to add some styling. The tutorial is structured so that you can follow along step by step. However, if you’d like to jump ahead and see the end result, the code for this tutorial is also available on GitHub.
Before we can start coding, we’ll need to get Node, npm and MongoDB installed on our machines. I won’t go into depth on the various installation instructions, but if you have any trouble getting set up, please leave a comment below.
Many websites will recommend that you head to the official Node download page and grab the Node binaries for your system. While that works, I would suggest that you use a version manager instead. This is a program which allows you to install multiple versions of Node and switch between them at will. There are various advantages to using a version manager, for example it negates potential permission issues which would otherwise see you installing packages with admin rights.
If you fancy going the version manager route, please consult our quick tip: Install Multiple Versions of Node.js Using nvm. Otherwise, grab the correct binaries for your system from the link above and install those.
npm is a JavaScript package manager which comes bundled with Node, so no extra installation is necessary here. We’ll be making quite extensive use of npm throughout this tutorial, so if you’re in need of a refresher, please consult: A Beginner’s Guide to npm — the Node Package Manager.
MongoDB is a document database which stores data in flexible, JSON-like documents.
The quickest way to get up and running with Mongo is to use a service such as mLabs. They have a free sandbox plan which provides a single database with 496 MB of storage running on a shared virtual machine. This is more than adequate for a simple app with a handful of users. If this sounds like the best option for you, please consult their quick start guide.
You can also install Mongo locally. To do this, please visit the official download page and download the correct version of the community server for your operating system. There’s a link to detailed, OS-specific installation instructions beneath every download link, which you can consult if you run into trouble.
Although not strictly necessary for following along with this tutorial, you might also like to install Compass, the official GUI for MongoDB. This tool helps you visualize and manipulate your data, allowing you to interact with documents with full CRUD functionality.
At the time of writing, you’ll need to fill out your details to download Compass, but you won’t need to create an account.
To check that Node and npm are installed correctly, open your terminal and type:
node -v
followed by:
npm -v
This will output the version number of each program (8.9.4
and 5.6.0
respectively at the time of writing).
If you installed Mongo locally, you can check the version number using:
mongo --version
This should output a bunch of information, including the version number (3.6.2
at the time of writing).
If you have installed Mongo locally, you start the server by typing the following command into a terminal:
mongod
Next, open Compass. You should be able to accept the defaults (server: localhost
, port: 27017), press the CONNECTbutton, and establish a connection to the database server.
MongoDB Compass connected to localhost
Note that the databases admin
and local
are created automatically.
If you’re using mLabs, create a database subscription (as described in their quick-start guide), then copy the connection details to the clipboard. This should be in the form:
mongodb://<dbuser>:<dbpassword>@ds251827.mlab.com:51827/<dbname>
When you open Compass, it will inform you that it has detected a MongoDB connection string and asks if you would like to use it to fill out the form. Click Yes, noting that you might need to adjust the username and password by hand. After that, click CONNECT and you should be off to the races.
MongoDB Compass connected to mLabs
Note that I called my database sp-node-article
. You can call yours what you like.
With everything set up correctly, the first thing we need to do is initialize our new project. To do this, create a folder named demo-node-app
, enter that directory and type the following in a terminal:
npm init -y
This will create and auto-populate a package.json
file in the project root. We can use this file to specify our dependencies and to create various npm scripts, which will aid our development workflow.
Express is a lightweight web application framework for Node.js, which provides us with a robust set of features for writing web apps. These features include such things as route handling, template engine integration and a middleware framework, which allows us to perform additional tasks on request and response objects. There is nothing you can do in Express that you couldn’t do in plain Node.js, but using Express means we don’t have to re-invent the wheel and reduces boilerplate.
So let’s install Express. To do this, run the following in your terminal:
npm install --save express
By passing the --save
option to the npm install
command, Express will be added to the dependencies
section of the package.json
file. This signals to anyone else running our code that Express is a package our app needs to function properly.
nodemon is a convenience tool. It will watch the files in the directory it was started in, and if it detects any changes, it will automatically restart your Node application (meaning you don’t have to). In contrast to Express, nodemon is not something the app requires to function properly (it just aids us with development), so install it using:
npm install --save-dev nodemon
This will add nodemon to the dev-dependencies
section of the package.json
file.
We’re almost through with the setup. All we need to do now is create a couple of initial files before kicking off the app.
In the demo-node-app
folder create an app.js
file and a start.js
file. Also create a routes
folder, with an index.js
file inside. After you’re done, things should look like this:
.
├── app.js
├── node_modules
│ └── ...
├── package.json
├── routes
│ └── index.js
└── start.js
Now, let’s add some code to those files.
In app.js
:
const express = require('express');
const routes = require('./routes/index');
const app = express();
app.use('/', routes);
module.exports = app;
Here, we’re importing both the express
module and (the export value of) our routes file into the application. The require
function we’re using to do this is a built-in Node function which imports an object from another file or module. If you’d like a refresher on importing and exporting modules.
After that, we’re creating a new Express app using the express function and assigning it to an app
variable. We then tell the app that, whenever it receives a request from forward slash anything, it should use the routes file.
Finally, we export our app variable so that it can be imported and used in other files.
In start.js
:
const app = require('./app');
const server = app.listen(3000, () => {
console.log(`Express is running on port ${server.address().port}`);
});
Here we’re importing the Express app we created in app.js
(note that we can leave the .js
off the file name in the require
statement). We then tell our app to listen on port 3000 for incoming connections and output a message to the terminal to indicate that the server is running.
And in routes/index.js
:
const express = require('express');
const router = express.Router();
router.get('/', (req, res) => {
res.send('It works!');
});
module.exports = router;
Here, we’re importing Express into our routes file and then grabbing the router from it. We then use the router to respond to any requests to the root URL (in this case <a href="http://localhost:3000" target="_blank">http://localhost:3000</a>
) with an “It works!” message.
Finally, let’s add an npm script to make nodemon start watching our app. Change the scripts
section of the package.json
file to look like this:
"scripts": {
"watch": "nodemon ./start.js"
},
The scripts
property of the package.json
file is extremely useful, as it lets you specify arbitrary scripts to run in different scenarios. This means that you don’t have to repeatedly type out long-winded commands with a difficult-to-remember syntax. If you’d like to find out more about what npm scripts can do
Now, type npm run watch
from the terminal and visit http://localhost:3000.
You should see “It works!”
Returning an inline response from within the route handler is all well and good, but it’s not very extensible, and this is where templating engines come in. As the Express docs state:
In practice, this means we can define template files and tell our routes to use them instead of writing everything inline. Let’s do that now.
Create a folder named views
and in that folder a file named form.pug
. Add the following code to this new file:
form(action="." method="POST")
label(for="name") Name:
input(
type="text"
id="name"
name="name"
)
label(for="email") Email:
input(
type="email"
id="email"
name="email"
)
input(type="submit" value="Submit")
As you can deduce from the file ending, we’ll be using the pug templating engine in our app. Pug (formerly known as Jade) comes with its own indentation-sensitive syntax for writing dynamic and reusable HTML. Hopefully the above example is easy to follow, but if you have any difficulties understanding what it does, just wait until we view this in a browser, then inspect the page source to see the markup it produces.
If you’d like a refresher as to what JavaScript templates and/or templating engines are, and when you should use them
Next, we’ll need to install pug, saving it as a dependency:
npm i --save pug
Then configure app.js
to use Pug as a layout engine and to look for templates inside the views
folder:
const express = require('express');
const path = require('path');
const routes = require('./routes/index');
const app = express();
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');
app.use('/', routes);
module.exports = app;
You’ll notice that we’re also requiring Node’s native Path module, which provides utilities for working with file and directory paths. This module allows us to build the path to our views
folder using its join method and __dirname (which returns the directory in which the currently executing script resides).
Finally, we need to tell our route to use our new template. In routes/index.js
:
router.get('/', (req, res) => {
res.render('form');
});
This uses the render method on Express’s response object to send the rendered view to the client.
So let’s see if it worked. As we’re using nodemon to watch our app for changes, you should simply be able to refresh your browser and see our brutalist masterpiece.
If you open your browser and inspect the page source, you’ll see that Express only sent the HTML for the form: our page is missing a doc-type declaration, as well as a head and body section. Let’s fix that by creating a master layout for all our templates to use.
To do this, create a layout.pug
file in the views
folder and add the following code:
doctype html
html
head
title= `${title}`
body
h1 My Amazing App
block content
The first thing to notice here is the line starting title=
. Appending an equals sign to an attribute is one of the methods that Pug uses for interpolation. We’ll use this to pass the title dynamically to each template.
The second thing to notice is the line that starts with the block
keyword. In a template, a block is simply a “block” of Pug that a child template may replace. We’ll see how to use it shortly, but if you’re keen to find out more.
All that remains to do is to inform our form.pug
template that it should use the layout file. To do this, alter views/form.pug
, like so:
extends layout
block content
form(action="." method="POST")
label(for="name") Name:
input(
type="text"
id="name"
name="name"
)
label(for="email") Email:
input(
type="email"
id="email"
name="email"
)
input(type="submit" value="Submit")
And in routes/index.js
, we need to pass in an appropriate title for the template to display:
router.get('/', (req, res) => {
res.render('form', { title: 'Registration form' });
});
Now if you refresh the page and inspect the source, things should look a lot better.
Currently, if you hit our form’s Submit button, you’ll be redirected to a page with a message: “Cannot POST /”. This is because when submitted, our form POSTs its contents back to /
and we haven’t defined a route to handle that yet.
Let’s do that now. Add the following to routes/index.js
:
router.post('/', (req, res) => {
res.render('form', { title: 'Registration form' });
});
This is the same as our GET route, except for the fact that we’re using router.post
to respond to a different HTTP verb.
Now when we submit the form, the error message will be gone and the form should just re-render.
The next task is to retrieve whatever data the user has submitted via the form. To do this, we’ll need to install a package named body-parser, which will make the form data available on the request body:
npm install --save body-parser
We’ll also need to tell our app to use this package, so add the following to app.js
:
const bodyParser = require('body-parser');
...
app.use(bodyParser.urlencoded({ extended: true }));
app.use('/', routes);
module.exports = app;
Note that there are various ways to format the data you POST to the server, and using body-parser’s urlencoded method allows us to handle data sent as application/x-www-form-urlencoded
.
Then we can try logging the submitted data to the terminal. Alter the route handler like so:
router.post('/', (req, res) => {
console.log(req.body);
res.render('form', { title: 'Registration form' });
});
Now when you submit the form, you should see something along the lines of:
{name: 'Jim', email: 'jim@example.com'}
Form output logged to terminal
By now you’ve hopefully noticed the pattern we’re using to handle routes in Express.
router.METHOD(route, (req, res) => {
// callback function
});
The callback function is executed whenever somebody visits a URL that matches the route it specifies. The callback receives a req
and res
parameter, where req
is an object full of information that is coming in (such as form data or query parameters) and res
is an object full of methods for sending data back to the user. There’s also an optional next
parameter which is useful if you don’t actually want to send any data back, or if you want to pass the request off for something else to handle.
Without getting too deep into the weeds, this is a concept known as middleware (specifically, router-level middleware) which is very important in Express. If you’re interested in finding out more about how Express uses middleware.
Now let’s check that the user has filled out both our fields. We can do this using express-validator module, a middleware that provides a number of useful methods for the sanitization and validation of user input.
You can install it like so:
npm install express-validator --save
And require the functions we’ll need in routes/index.js
:
const { body, validationResult } = require('express-validator/check');
We can include it in our route handler like so:
router.post('/',
[
body('name')
.isLength({ min: 1 })
.withMessage('Please enter a name'),
body('email')
.isLength({ min: 1 })
.withMessage('Please enter an email'),
],
(req, res) => {
...
}
);
As you can see, we’re using the body method to validate two properties on req.body
— namely, name
and email
. In our case, it’s sufficient to just check that these properties exist (i.e. that they have a length greater than one), but express-validator offers a whole host of other methods that you can read about on the project’s home page.
In a second step, we can call the validationResult method to see if validation passed or failed. If no errors are present, we can go ahead and render out a “Thanks for registering” message. Otherwise, we’ll need to pass these errors back to our template, so as to inform the user that something’s wrong.
And if validation fails, we’ll also need to pass req.body
back to the template, so that any valid form inputs aren’t reset:
router.post(
'/',
[
...
],
(req, res) => {
const errors = validationResult(req);
if (errors.isEmpty()) {
res.send('Thank you for your registration!');
} else {
res.render('form', {
title: 'Registration form',
errors: errors.array(),
data: req.body,
});
}
}
);
Now we have to make a couple of changes to our form.pug
template. We firstly need to check for an errors
property, and if it’s present, loop over any errors and display them in a list:
extends layout
block content
if errors
ul
for error in errors
li= error.msg
...
If the li=
looks weird, remember that pug does interpolation by following the tag name with an equals sign.
Finally, we need to check if a data
attribute exists, and if so, use it to set the values of the respective fields. If it doesn’t exist, we’ll initialize it to an empty object, so that the form will still render correctly when you load it for the first time. We can do this with some JavaScript, denoted in Pug by a minus sign:
-data = data || {}
We then reference that attribute to set the field’s value:
input(
type="text"
id="name"
name="name"
value=data.name
)
That gives us the following:
extends layout
block content
-data = data || {}
if errors
ul
for error in errors
li= error.msg
form(action="." method="POST")
label(for="name") Name:
input(
type="text"
id="name"
name="name"
value=data.name
)
label(for="email") Email:
input(
type="email"
id="email"
name="email"
value=data.email
)
input(type="submit" value="Submit")
Now, when you submit a successful registration, you should see a thank you message, and when you submit the form without filling out both field, the template should be re-rendered with an error message.
We now want to hook our form up to our database, so that we can save whatever data the user enters. If you’re running Mongo locally, don’t forget to start the server with the command mongod
.
We’ll need somewhere to specify our database connection details. For this, we’ll use a configuration file (which should notbe checked into version control) and the dotenv package. Dotenv will load our connection details from the configuration file into Node’s process.env.
Install it like so:
npm install dotenv --save
And require it at the top of start.js
:
require('dotenv').config();
Next, create a file named .env
in the project root (note that starting a filename with a dot may cause it to be hidden on certain operating systems) and enter your Mongo connection details on the first line.
If you’re running Mongo locally:
DATABASE=mongodb://localhost:27017/node-demo-application
If you’re using mLabs:
mongodb://<dbuser>:<dbpassword>@ds251827.mlab.com:51827/<dbname>
Note that local installations of MongoDB don’t have a default user or password. This is definitely something you’ll want to change in production, as it’s otherwise a security risk.
To establish the connection to the database and to perform operations on it, we’ll be using Mongoose. Mongoose is an ORM for MongoDB.
Mongoose provides a straight-forward, schema-based solution to model your application data. It includes built-in type casting, validation, query building, business logic hooks and more, out of the box.
What this means in real terms is that it creates various abstractions over Mongo, which make interacting with our database easier and reduce the amount of boilerplate we have to write. If you’d like to find out more about how Mongo works under the hood, be sure to read our Introduction to MongoDB.
To install Mongoose:
npm install --save mongoose
Then, require it in start.js
:
const mongoose = require('mongoose');
The connection is made like so:
mongoose.connect(process.env.DATABASE, { useMongoClient: true });
mongoose.Promise = global.Promise;
mongoose.connection
.on('connected', () => {
console.log(`Mongoose connection open on ${process.env.DATABASE}`);
})
.on('error', (err) => {
console.log(`Connection error: ${err.message}`);
});
Notice how we use the DATABASE
variable we declared in the .env
file to specify the database URL. We’re also telling Mongo to use ES6 Promises (these are necessary, as database interactions are asynchronous), as its own default promise library is deprecated.
This is what start.js
should now look like:
require('dotenv').config();
const mongoose = require('mongoose');
mongoose.connect(process.env.DATABASE, { useMongoClient: true });
mongoose.Promise = global.Promise;
mongoose.connection
.on('connected', () => {
console.log(`Mongoose connection open on ${process.env.DATABASE}`);
})
.on('error', (err) => {
console.log(`Connection error: ${err.message}`);
});
const app = require('./app');
const server = app.listen(3000, () => {
console.log(`Express is running on port ${server.address().port}`);
});
When you save the file, nodemon will restart the app and, if all’s gone well, you should see something along the lines of:
Mongoose connection open on mongodb://localhost:27017/node-demo-application
MongoDB can be used as a loose database, meaning it’s not necessary to describe what data will look like ahead of time. However, out of the box it runs in strict mode, which means it’ll only allow you to save data it knows about beforehand. As we’ll be using strict mode, we’ll need to define the shape our data using a schema. Schemas allow you to define the fields stored in each document along with their type, validation requirements and default values.
To this end, create a models
folder in the project root, and within that folder, a new file named Registration.js
.
Add the following code to Registration.js
:
const mongoose = require('mongoose');
const registrationSchema = new mongoose.Schema({
name: {
type: String,
trim: true,
},
email: {
type: String,
trim: true,
},
});
module.exports = mongoose.model('Registration', registrationSchema);
Here, we’re just defining a type (as we already have validation in place) and are making use of the trim helper method to remove any superfluous white space from user input. We then compile a model from the Schema definition, and export it for use elsewhere in our app.
The final piece of boilerplate is to require the model in start.js
:
...
require('./models/Registration');
const app = require('./app');
const server = app.listen(3000, () => {
console.log(`Express is running on port ${server.address().port}`);
});
Now we’re ready to save user data to our database. Let’s begin by requiring Mongoose and importing our model into our routes/index.js
file:
const express = require('express');
const mongoose = require('mongoose');
const { body, validationResult } = require('express-validator/check');
const router = express.Router();
const Registration = mongoose.model('Registration');
...
Now, when the user posts data to the server, if validation passes we can go ahead and create a new Registration
object and attempt to save it. As the database operation is an asynchronous operation which returns a Promise, we can chain a .then()
onto the end of it to deal with a successful insert and a .catch()
to deal with any errors:
if (errors.isEmpty()) {
const registration = new Registration(req.body);
registration.save()
.then(() => { res.send('Thank you for your registration!'); })
.catch(() => { res.send('Sorry! Something went wrong.'); });
} else {
...
}
...
Now, if you enter your details into the registration form, they should be persisted to the database. You can check this using Compass (making sure to hit the refresh button in the top left if it’s still running).
Using Compass to check that our data was saved to MongoDB
To round the app off, let’s create a final route, which lists out all of our registrations. Hopefully you should have a reasonable idea of the process by now.
Add a new route to routes/index.js
, as follows:
router.get('/registrations', (req, res) => {
res.render('index', { title: 'Listing registrations' });
});
This means that we’ll also need a corresponding view template (views/index.pug
):
extends layout
block content
p No registrations yet :(
Now when you visit http://localhost:3000/registrations, you should see a message telling you that there aren’t any registrations.
Let’s fix that by retrieving our registrations from the database and passing them to the view. We’ll still display the “No registrations yet” message, but only if there really aren’t any.
In routes/index.js
:
router.get('/registrations', (req, res) => {
Registration.find()
.then((registrations) => {
res.render('index', { title: 'Listing registrations', registrations });
})
.catch(() => { res.send('Sorry! Something went wrong.'); });
});
Here, we’re using Mongo’s Collection#find method, which, if invoked without parameters, will return all of the records in the collection. Because the database lookup is asynchronous, we’re waiting for it to complete before rendering the view. If any records were returned, these will be passed to the view template in the registrations
property. If no records were returned registrations
will be an empty array.
In views/index.pug
, we can then check the length of whatever we’re handed and either loop over it and output the records to the screen, or display a “No registrations” message:
extends layout
block content
if registrations.length
table
tr
th Name
th Email
each registration in registrations
tr
td= registration.name
td= registration.email
else
p No registrations yet :(
The final feature we’ll add to our app is HTTP authentication, locking down the list of successful registrations from prying eyes.
To do this, we’ll use the http-auth module, which we can install using:
npm install --save http-auth
Next we need to require it in routes/index.js
, along with the Path module we met earlier:
const path = require('path');
const auth = require('http-auth');
Next, let it know where to find the file in which we’ll list the users and passwords (in this case users.htpasswd
in the project root):
const basic = auth.basic({
file: path.join(__dirname, '../users.htpasswd'),
});
Create this users.htpasswd
file next and add a username and password separated by a colon. This can be in plain text, but the http-auth module also supports hashed passwords, so you could also run the password through a service such as Htpasswd Generator.
For me, the contents of users.htpasswd
looks like this:
jim:$apr1$FhFmamtz$PgXfrNI95HFCuXIm30Q4V0
This translates to user: jim
, password: password
.
Finally, add it to the route you wish to protect and you’re good to go:
router.get('/registrations', auth.connect(basic), (req, res) => {
...
});
Let’s give the app some polish and add some styling using Twitter Bootstrap. We can serve static files such as images, JavaScript files and CSS files in Express using the built-in express.static middleware function.
Setting it up is easy. Just add the following line to app.js
:
app.use(express.static('public'));
Now we can load files that are in the public directory
.
Create a public
directory in the project root, and in the public
directory create a css
directory. Download the minified version of Bootstrap v4 into this directory, ensuring it’s named bootstrap.min.css
.
Next, we’ll need to add some markup to our pug templates.
In layout.pug
:
doctype html
html
head
title= `${title}`
link(rel='stylesheet', href='/css/bootstrap.min.css')
link(rel='stylesheet', href='/css/styles.css')
body
div.container.listing-reg
h1 My Amazing App
block content
Here, we’re including two files from our previously created css
folder and adding a wrapper div.
In form.pug
we add some class names to the error messages and the form elements:
extends layout
block content
-data = data || {}
if errors
ul.my-errors
for error in errors
li= error.msg
form(action="." method="POST" class="form-registration")
label(for="name") Name:
input(
type="text"
id="name"
name="name"
class="form-control"
value=data.name
)
label(for="email") Email:
input(
type="email"
id="email"
name="email"
class="form-control"
value=data.email
)
input(
type="submit"
value="Submit"
class="btn btn-lg btn-primary btn-block"
)
And in index.pug
, more of the same:
extends layout
block content
if registrations.length
table.listing-table.table-dark.table-striped
tr
th Name
th Email
each registration in registrations
tr
td= registration.name
td= registration.email
else
p No registrations yet :(
Finally, create a file called styles.css
in the css
folder and add the following:
body {
padding: 40px 10px;
background-color: #eee;
}
.listing-reg h1 {
text-align: center;
margin: 0 0 2rem;
}
/* css for registration form and errors*/
.form-registration {
max-width: 330px;
padding: 15px;
margin: 0 auto;
}
.form-registration {
display: flex;
flex-wrap: wrap;
}
.form-registration input {
width: 100%;
margin: 0px 0 10px;
}
.form-registration .btn {
flex: 1 0 100%;
}
.my-errors {
margin: 0 auto;
padding: 0;
list-style: none;
color: #333;
font-size: 1.2rem;
display: table;
}
.my-errors li {
margin: 0 0 1rem;
}
.my-errors li:before {
content: "! Error : ";
color: #f00;
font-weight: bold;
}
/* Styles for listing table */
.listing-table {
width: 100%;
}
.listing-table th,
.listing-table td {
padding: 10px;
border-bottom: 1px solid #666;
}
.listing-table th {
background: #000;
color: #fff;
}
.listing-table td:first-child,
.listing-table th:first-child {
border-right: 1px solid #666;
}
Now when you refresh the page, you should see all of the Bootstrap glory!
I hope you’ve enjoyed this tutorial. I hope help you in the world of Node-based web apps and offer you some solid takeaways for your next project in the process.
Thank you for reading.
#node-js #mongodb #mobile-apps #bootstrap
1632537859
Not babashka. Node.js babashka!?
Ad-hoc CLJS scripting on Node.js.
Experimental. Please report issues here.
Nbb's main goal is to make it easy to get started with ad hoc CLJS scripting on Node.js.
Additional goals and features are:
Nbb requires Node.js v12 or newer.
CLJS code is evaluated through SCI, the same interpreter that powers babashka. Because SCI works with advanced compilation, the bundle size, especially when combined with other dependencies, is smaller than what you get with self-hosted CLJS. That makes startup faster. The trade-off is that execution is less performant and that only a subset of CLJS is available (e.g. no deftype, yet).
Install nbb
from NPM:
$ npm install nbb -g
Omit -g
for a local install.
Try out an expression:
$ nbb -e '(+ 1 2 3)'
6
And then install some other NPM libraries to use in the script. E.g.:
$ npm install csv-parse shelljs zx
Create a script which uses the NPM libraries:
(ns script
(:require ["csv-parse/lib/sync$default" :as csv-parse]
["fs" :as fs]
["path" :as path]
["shelljs$default" :as sh]
["term-size$default" :as term-size]
["zx$default" :as zx]
["zx$fs" :as zxfs]
[nbb.core :refer [*file*]]))
(prn (path/resolve "."))
(prn (term-size))
(println (count (str (fs/readFileSync *file*))))
(prn (sh/ls "."))
(prn (csv-parse "foo,bar"))
(prn (zxfs/existsSync *file*))
(zx/$ #js ["ls"])
Call the script:
$ nbb script.cljs
"/private/tmp/test-script"
#js {:columns 216, :rows 47}
510
#js ["node_modules" "package-lock.json" "package.json" "script.cljs"]
#js [#js ["foo" "bar"]]
true
$ ls
node_modules
package-lock.json
package.json
script.cljs
Nbb has first class support for macros: you can define them right inside your .cljs
file, like you are used to from JVM Clojure. Consider the plet
macro to make working with promises more palatable:
(defmacro plet
[bindings & body]
(let [binding-pairs (reverse (partition 2 bindings))
body (cons 'do body)]
(reduce (fn [body [sym expr]]
(let [expr (list '.resolve 'js/Promise expr)]
(list '.then expr (list 'clojure.core/fn (vector sym)
body))))
body
binding-pairs)))
Using this macro we can look async code more like sync code. Consider this puppeteer example:
(-> (.launch puppeteer)
(.then (fn [browser]
(-> (.newPage browser)
(.then (fn [page]
(-> (.goto page "https://clojure.org")
(.then #(.screenshot page #js{:path "screenshot.png"}))
(.catch #(js/console.log %))
(.then #(.close browser)))))))))
Using plet
this becomes:
(plet [browser (.launch puppeteer)
page (.newPage browser)
_ (.goto page "https://clojure.org")
_ (-> (.screenshot page #js{:path "screenshot.png"})
(.catch #(js/console.log %)))]
(.close browser))
See the puppeteer example for the full code.
Since v0.0.36, nbb includes promesa which is a library to deal with promises. The above plet
macro is similar to promesa.core/let
.
$ time nbb -e '(+ 1 2 3)'
6
nbb -e '(+ 1 2 3)' 0.17s user 0.02s system 109% cpu 0.168 total
The baseline startup time for a script is about 170ms seconds on my laptop. When invoked via npx
this adds another 300ms or so, so for faster startup, either use a globally installed nbb
or use $(npm bin)/nbb script.cljs
to bypass npx
.
Nbb does not depend on any NPM dependencies. All NPM libraries loaded by a script are resolved relative to that script. When using the Reagent module, React is resolved in the same way as any other NPM library.
To load .cljs
files from local paths or dependencies, you can use the --classpath
argument. The current dir is added to the classpath automatically. So if there is a file foo/bar.cljs
relative to your current dir, then you can load it via (:require [foo.bar :as fb])
. Note that nbb
uses the same naming conventions for namespaces and directories as other Clojure tools: foo-bar
in the namespace name becomes foo_bar
in the directory name.
To load dependencies from the Clojure ecosystem, you can use the Clojure CLI or babashka to download them and produce a classpath:
$ classpath="$(clojure -A:nbb -Spath -Sdeps '{:aliases {:nbb {:replace-deps {com.github.seancorfield/honeysql {:git/tag "v2.0.0-rc5" :git/sha "01c3a55"}}}}}')"
and then feed it to the --classpath
argument:
$ nbb --classpath "$classpath" -e "(require '[honey.sql :as sql]) (sql/format {:select :foo :from :bar :where [:= :baz 2]})"
["SELECT foo FROM bar WHERE baz = ?" 2]
Currently nbb
only reads from directories, not jar files, so you are encouraged to use git libs. Support for .jar
files will be added later.
The name of the file that is currently being executed is available via nbb.core/*file*
or on the metadata of vars:
(ns foo
(:require [nbb.core :refer [*file*]]))
(prn *file*) ;; "/private/tmp/foo.cljs"
(defn f [])
(prn (:file (meta #'f))) ;; "/private/tmp/foo.cljs"
Nbb includes reagent.core
which will be lazily loaded when required. You can use this together with ink to create a TUI application:
$ npm install ink
ink-demo.cljs
:
(ns ink-demo
(:require ["ink" :refer [render Text]]
[reagent.core :as r]))
(defonce state (r/atom 0))
(doseq [n (range 1 11)]
(js/setTimeout #(swap! state inc) (* n 500)))
(defn hello []
[:> Text {:color "green"} "Hello, world! " @state])
(render (r/as-element [hello]))
Working with callbacks and promises can become tedious. Since nbb v0.0.36 the promesa.core
namespace is included with the let
and do!
macros. An example:
(ns prom
(:require [promesa.core :as p]))
(defn sleep [ms]
(js/Promise.
(fn [resolve _]
(js/setTimeout resolve ms))))
(defn do-stuff
[]
(p/do!
(println "Doing stuff which takes a while")
(sleep 1000)
1))
(p/let [a (do-stuff)
b (inc a)
c (do-stuff)
d (+ b c)]
(prn d))
$ nbb prom.cljs
Doing stuff which takes a while
Doing stuff which takes a while
3
Also see API docs.
Since nbb v0.0.75 applied-science/js-interop is available:
(ns example
(:require [applied-science.js-interop :as j]))
(def o (j/lit {:a 1 :b 2 :c {:d 1}}))
(prn (j/select-keys o [:a :b])) ;; #js {:a 1, :b 2}
(prn (j/get-in o [:c :d])) ;; 1
Most of this library is supported in nbb, except the following:
:syms
.-x
notation. In nbb, you must use keywords.See the example of what is currently supported.
See the examples directory for small examples.
Also check out these projects built with nbb:
See API documentation.
See this gist on how to convert an nbb script or project to shadow-cljs.
Prequisites:
To build:
bb release
Run bb tasks
for more project-related tasks.
Download Details:
Author: borkdude
Download Link: Download The Source Code
Official Website: https://github.com/borkdude/nbb
License: EPL-1.0
#node #javascript
1622719015
Front-end web development has been overwhelmed by JavaScript highlights for quite a long time. Google, Facebook, Wikipedia, and most of all online pages use JS for customer side activities. As of late, it additionally made a shift to cross-platform mobile development as a main technology in React Native, Nativescript, Apache Cordova, and other crossover devices.
Throughout the most recent couple of years, Node.js moved to backend development as well. Designers need to utilize a similar tech stack for the whole web project without learning another language for server-side development. Node.js is a device that adjusts JS usefulness and syntax to the backend.
Node.js isn’t a language, or library, or system. It’s a runtime situation: commonly JavaScript needs a program to work, however Node.js makes appropriate settings for JS to run outside of the program. It’s based on a JavaScript V8 motor that can run in Chrome, different programs, or independently.
The extent of V8 is to change JS program situated code into machine code — so JS turns into a broadly useful language and can be perceived by servers. This is one of the advantages of utilizing Node.js in web application development: it expands the usefulness of JavaScript, permitting designers to coordinate the language with APIs, different languages, and outside libraries.
Of late, organizations have been effectively changing from their backend tech stacks to Node.js. LinkedIn picked Node.js over Ruby on Rails since it took care of expanding responsibility better and decreased the quantity of servers by multiple times. PayPal and Netflix did something comparative, just they had a goal to change their design to microservices. We should investigate the motivations to pick Node.JS for web application development and when we are planning to hire node js developers.
The principal thing that makes Node.js a go-to environment for web development is its JavaScript legacy. It’s the most well known language right now with a great many free devices and a functioning local area. Node.js, because of its association with JS, immediately rose in ubiquity — presently it has in excess of 368 million downloads and a great many free tools in the bundle module.
Alongside prevalence, Node.js additionally acquired the fundamental JS benefits:
In addition, it’s a piece of a well known MEAN tech stack (the blend of MongoDB, Express.js, Angular, and Node.js — four tools that handle all vital parts of web application development).
This is perhaps the most clear advantage of Node.js web application development. JavaScript is an unquestionable requirement for web development. Regardless of whether you construct a multi-page or single-page application, you need to know JS well. On the off chance that you are now OK with JavaScript, learning Node.js won’t be an issue. Grammar, fundamental usefulness, primary standards — every one of these things are comparable.
In the event that you have JS designers in your group, it will be simpler for them to learn JS-based Node than a totally new dialect. What’s more, the front-end and back-end codebase will be basically the same, simple to peruse, and keep up — in light of the fact that they are both JS-based.
There’s another motivation behind why Node.js got famous so rapidly. The environment suits well the idea of microservice development (spilling stone monument usefulness into handfuls or many more modest administrations).
Microservices need to speak with one another rapidly — and Node.js is probably the quickest device in information handling. Among the fundamental Node.js benefits for programming development are its non-obstructing algorithms.
Node.js measures a few demands all at once without trusting that the first will be concluded. Many microservices can send messages to one another, and they will be gotten and addressed all the while.
Node.js was worked in view of adaptability — its name really says it. The environment permits numerous hubs to run all the while and speak with one another. Here’s the reason Node.js adaptability is better than other web backend development arrangements.
Node.js has a module that is liable for load adjusting for each running CPU center. This is one of numerous Node.js module benefits: you can run various hubs all at once, and the environment will naturally adjust the responsibility.
Node.js permits even apportioning: you can part your application into various situations. You show various forms of the application to different clients, in light of their age, interests, area, language, and so on. This builds personalization and diminishes responsibility. Hub accomplishes this with kid measures — tasks that rapidly speak with one another and share a similar root.
What’s more, Node’s non-hindering solicitation handling framework adds to fast, letting applications measure a great many solicitations.
Numerous designers consider nonconcurrent to be one of the two impediments and benefits of Node.js web application development. In Node, at whatever point the capacity is executed, the code consequently sends a callback. As the quantity of capacities develops, so does the number of callbacks — and you end up in a circumstance known as the callback damnation.
In any case, Node.js offers an exit plan. You can utilize systems that will plan capacities and sort through callbacks. Systems will associate comparable capacities consequently — so you can track down an essential component via search or in an envelope. At that point, there’s no compelling reason to look through callbacks.
So, these are some of the top benefits of Nodejs in web application development. This is how Nodejs is contributing a lot to the field of web application development.
I hope now you are totally aware of the whole process of how Nodejs is really important for your web project. If you are looking to hire a node js development company in India then I would suggest that you take a little consultancy too whenever you call.
Good Luck!
#node.js development company in india #node js development company #hire node js developers #hire node.js developers in india #node.js development services #node.js development
1616671994
If you look at the backend technology used by today’s most popular apps there is one thing you would find common among them and that is the use of NodeJS Framework. Yes, the NodeJS framework is that effective and successful.
If you wish to have a strong backend for efficient app performance then have NodeJS at the backend.
WebClues Infotech offers different levels of experienced and expert professionals for your app development needs. So hire a dedicated NodeJS developer from WebClues Infotech with your experience requirement and expertise.
So what are you waiting for? Get your app developed with strong performance parameters from WebClues Infotech
For inquiry click here: https://www.webcluesinfotech.com/hire-nodejs-developer/
Book Free Interview: https://bit.ly/3dDShFg
#hire dedicated node.js developers #hire node.js developers #hire top dedicated node.js developers #hire node.js developers in usa & india #hire node js development company #hire the best node.js developers & programmers
1561018824
We’ll be using Node.js and the Express framework to build a simple registration form with basic validation, which persists its data to a MongoDB database. We’ll add a view to list successful registration, which we’ll protect with basic HTTP authentication, and we’ll use Bootstrap to add some styling. The tutorial is structured so that you can follow along step by step. However, if you’d like to jump ahead and see the end result, the code for this tutorial is also available on GitHub.
Before we can start coding, we’ll need to get Node, npm and MongoDB installed on our machines. I won’t go into depth on the various installation instructions, but if you have any trouble getting set up, please leave a comment below.
Many websites will recommend that you head to the official Node download page and grab the Node binaries for your system. While that works, I would suggest that you use a version manager instead. This is a program which allows you to install multiple versions of Node and switch between them at will. There are various advantages to using a version manager, for example it negates potential permission issues which would otherwise see you installing packages with admin rights.
If you fancy going the version manager route, please consult our quick tip: Install Multiple Versions of Node.js Using nvm. Otherwise, grab the correct binaries for your system from the link above and install those.
npm is a JavaScript package manager which comes bundled with Node, so no extra installation is necessary here. We’ll be making quite extensive use of npm throughout this tutorial, so if you’re in need of a refresher, please consult: A Beginner’s Guide to npm — the Node Package Manager.
MongoDB is a document database which stores data in flexible, JSON-like documents.
The quickest way to get up and running with Mongo is to use a service such as mLabs. They have a free sandbox plan which provides a single database with 496 MB of storage running on a shared virtual machine. This is more than adequate for a simple app with a handful of users. If this sounds like the best option for you, please consult their quick start guide.
You can also install Mongo locally. To do this, please visit the official download page and download the correct version of the community server for your operating system. There’s a link to detailed, OS-specific installation instructions beneath every download link, which you can consult if you run into trouble.
Although not strictly necessary for following along with this tutorial, you might also like to install Compass, the official GUI for MongoDB. This tool helps you visualize and manipulate your data, allowing you to interact with documents with full CRUD functionality.
At the time of writing, you’ll need to fill out your details to download Compass, but you won’t need to create an account.
To check that Node and npm are installed correctly, open your terminal and type:
node -v
followed by:
npm -v
This will output the version number of each program (8.9.4
and 5.6.0
respectively at the time of writing).
If you installed Mongo locally, you can check the version number using:
mongo --version
This should output a bunch of information, including the version number (3.6.2
at the time of writing).
If you have installed Mongo locally, you start the server by typing the following command into a terminal:
mongod
Next, open Compass. You should be able to accept the defaults (server: localhost
, port: 27017), press the CONNECTbutton, and establish a connection to the database server.
MongoDB Compass connected to localhost
Note that the databases admin
and local
are created automatically.
If you’re using mLabs, create a database subscription (as described in their quick-start guide), then copy the connection details to the clipboard. This should be in the form:
mongodb://<dbuser>:<dbpassword>@ds251827.mlab.com:51827/<dbname>
When you open Compass, it will inform you that it has detected a MongoDB connection string and asks if you would like to use it to fill out the form. Click Yes, noting that you might need to adjust the username and password by hand. After that, click CONNECT and you should be off to the races.
MongoDB Compass connected to mLabs
Note that I called my database sp-node-article
. You can call yours what you like.
With everything set up correctly, the first thing we need to do is initialize our new project. To do this, create a folder named demo-node-app
, enter that directory and type the following in a terminal:
npm init -y
This will create and auto-populate a package.json
file in the project root. We can use this file to specify our dependencies and to create various npm scripts, which will aid our development workflow.
Express is a lightweight web application framework for Node.js, which provides us with a robust set of features for writing web apps. These features include such things as route handling, template engine integration and a middleware framework, which allows us to perform additional tasks on request and response objects. There is nothing you can do in Express that you couldn’t do in plain Node.js, but using Express means we don’t have to re-invent the wheel and reduces boilerplate.
So let’s install Express. To do this, run the following in your terminal:
npm install --save express
By passing the --save
option to the npm install
command, Express will be added to the dependencies
section of the package.json
file. This signals to anyone else running our code that Express is a package our app needs to function properly.
nodemon is a convenience tool. It will watch the files in the directory it was started in, and if it detects any changes, it will automatically restart your Node application (meaning you don’t have to). In contrast to Express, nodemon is not something the app requires to function properly (it just aids us with development), so install it using:
npm install --save-dev nodemon
This will add nodemon to the dev-dependencies
section of the package.json
file.
We’re almost through with the setup. All we need to do now is create a couple of initial files before kicking off the app.
In the demo-node-app
folder create an app.js
file and a start.js
file. Also create a routes
folder, with an index.js
file inside. After you’re done, things should look like this:
.
├── app.js
├── node_modules
│ └── ...
├── package.json
├── routes
│ └── index.js
└── start.js
Now, let’s add some code to those files.
In app.js
:
const express = require('express');
const routes = require('./routes/index');
const app = express();
app.use('/', routes);
module.exports = app;
Here, we’re importing both the express
module and (the export value of) our routes file into the application. The require
function we’re using to do this is a built-in Node function which imports an object from another file or module. If you’d like a refresher on importing and exporting modules.
After that, we’re creating a new Express app using the express function and assigning it to an app
variable. We then tell the app that, whenever it receives a request from forward slash anything, it should use the routes file.
Finally, we export our app variable so that it can be imported and used in other files.
In start.js
:
const app = require('./app');
const server = app.listen(3000, () => {
console.log(`Express is running on port ${server.address().port}`);
});
Here we’re importing the Express app we created in app.js
(note that we can leave the .js
off the file name in the require
statement). We then tell our app to listen on port 3000 for incoming connections and output a message to the terminal to indicate that the server is running.
And in routes/index.js
:
const express = require('express');
const router = express.Router();
router.get('/', (req, res) => {
res.send('It works!');
});
module.exports = router;
Here, we’re importing Express into our routes file and then grabbing the router from it. We then use the router to respond to any requests to the root URL (in this case <a href="http://localhost:3000" target="_blank">http://localhost:3000</a>
) with an “It works!” message.
Finally, let’s add an npm script to make nodemon start watching our app. Change the scripts
section of the package.json
file to look like this:
"scripts": {
"watch": "nodemon ./start.js"
},
The scripts
property of the package.json
file is extremely useful, as it lets you specify arbitrary scripts to run in different scenarios. This means that you don’t have to repeatedly type out long-winded commands with a difficult-to-remember syntax. If you’d like to find out more about what npm scripts can do
Now, type npm run watch
from the terminal and visit http://localhost:3000.
You should see “It works!”
Returning an inline response from within the route handler is all well and good, but it’s not very extensible, and this is where templating engines come in. As the Express docs state:
In practice, this means we can define template files and tell our routes to use them instead of writing everything inline. Let’s do that now.
Create a folder named views
and in that folder a file named form.pug
. Add the following code to this new file:
form(action="." method="POST")
label(for="name") Name:
input(
type="text"
id="name"
name="name"
)
label(for="email") Email:
input(
type="email"
id="email"
name="email"
)
input(type="submit" value="Submit")
As you can deduce from the file ending, we’ll be using the pug templating engine in our app. Pug (formerly known as Jade) comes with its own indentation-sensitive syntax for writing dynamic and reusable HTML. Hopefully the above example is easy to follow, but if you have any difficulties understanding what it does, just wait until we view this in a browser, then inspect the page source to see the markup it produces.
If you’d like a refresher as to what JavaScript templates and/or templating engines are, and when you should use them
Next, we’ll need to install pug, saving it as a dependency:
npm i --save pug
Then configure app.js
to use Pug as a layout engine and to look for templates inside the views
folder:
const express = require('express');
const path = require('path');
const routes = require('./routes/index');
const app = express();
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');
app.use('/', routes);
module.exports = app;
You’ll notice that we’re also requiring Node’s native Path module, which provides utilities for working with file and directory paths. This module allows us to build the path to our views
folder using its join method and __dirname (which returns the directory in which the currently executing script resides).
Finally, we need to tell our route to use our new template. In routes/index.js
:
router.get('/', (req, res) => {
res.render('form');
});
This uses the render method on Express’s response object to send the rendered view to the client.
So let’s see if it worked. As we’re using nodemon to watch our app for changes, you should simply be able to refresh your browser and see our brutalist masterpiece.
If you open your browser and inspect the page source, you’ll see that Express only sent the HTML for the form: our page is missing a doc-type declaration, as well as a head and body section. Let’s fix that by creating a master layout for all our templates to use.
To do this, create a layout.pug
file in the views
folder and add the following code:
doctype html
html
head
title= `${title}`
body
h1 My Amazing App
block content
The first thing to notice here is the line starting title=
. Appending an equals sign to an attribute is one of the methods that Pug uses for interpolation. We’ll use this to pass the title dynamically to each template.
The second thing to notice is the line that starts with the block
keyword. In a template, a block is simply a “block” of Pug that a child template may replace. We’ll see how to use it shortly, but if you’re keen to find out more.
All that remains to do is to inform our form.pug
template that it should use the layout file. To do this, alter views/form.pug
, like so:
extends layout
block content
form(action="." method="POST")
label(for="name") Name:
input(
type="text"
id="name"
name="name"
)
label(for="email") Email:
input(
type="email"
id="email"
name="email"
)
input(type="submit" value="Submit")
And in routes/index.js
, we need to pass in an appropriate title for the template to display:
router.get('/', (req, res) => {
res.render('form', { title: 'Registration form' });
});
Now if you refresh the page and inspect the source, things should look a lot better.
Currently, if you hit our form’s Submit button, you’ll be redirected to a page with a message: “Cannot POST /”. This is because when submitted, our form POSTs its contents back to /
and we haven’t defined a route to handle that yet.
Let’s do that now. Add the following to routes/index.js
:
router.post('/', (req, res) => {
res.render('form', { title: 'Registration form' });
});
This is the same as our GET route, except for the fact that we’re using router.post
to respond to a different HTTP verb.
Now when we submit the form, the error message will be gone and the form should just re-render.
The next task is to retrieve whatever data the user has submitted via the form. To do this, we’ll need to install a package named body-parser, which will make the form data available on the request body:
npm install --save body-parser
We’ll also need to tell our app to use this package, so add the following to app.js
:
const bodyParser = require('body-parser');
...
app.use(bodyParser.urlencoded({ extended: true }));
app.use('/', routes);
module.exports = app;
Note that there are various ways to format the data you POST to the server, and using body-parser’s urlencoded method allows us to handle data sent as application/x-www-form-urlencoded
.
Then we can try logging the submitted data to the terminal. Alter the route handler like so:
router.post('/', (req, res) => {
console.log(req.body);
res.render('form', { title: 'Registration form' });
});
Now when you submit the form, you should see something along the lines of:
{name: 'Jim', email: 'jim@example.com'}
Form output logged to terminal
By now you’ve hopefully noticed the pattern we’re using to handle routes in Express.
router.METHOD(route, (req, res) => {
// callback function
});
The callback function is executed whenever somebody visits a URL that matches the route it specifies. The callback receives a req
and res
parameter, where req
is an object full of information that is coming in (such as form data or query parameters) and res
is an object full of methods for sending data back to the user. There’s also an optional next
parameter which is useful if you don’t actually want to send any data back, or if you want to pass the request off for something else to handle.
Without getting too deep into the weeds, this is a concept known as middleware (specifically, router-level middleware) which is very important in Express. If you’re interested in finding out more about how Express uses middleware.
Now let’s check that the user has filled out both our fields. We can do this using express-validator module, a middleware that provides a number of useful methods for the sanitization and validation of user input.
You can install it like so:
npm install express-validator --save
And require the functions we’ll need in routes/index.js
:
const { body, validationResult } = require('express-validator/check');
We can include it in our route handler like so:
router.post('/',
[
body('name')
.isLength({ min: 1 })
.withMessage('Please enter a name'),
body('email')
.isLength({ min: 1 })
.withMessage('Please enter an email'),
],
(req, res) => {
...
}
);
As you can see, we’re using the body method to validate two properties on req.body
— namely, name
and email
. In our case, it’s sufficient to just check that these properties exist (i.e. that they have a length greater than one), but express-validator offers a whole host of other methods that you can read about on the project’s home page.
In a second step, we can call the validationResult method to see if validation passed or failed. If no errors are present, we can go ahead and render out a “Thanks for registering” message. Otherwise, we’ll need to pass these errors back to our template, so as to inform the user that something’s wrong.
And if validation fails, we’ll also need to pass req.body
back to the template, so that any valid form inputs aren’t reset:
router.post(
'/',
[
...
],
(req, res) => {
const errors = validationResult(req);
if (errors.isEmpty()) {
res.send('Thank you for your registration!');
} else {
res.render('form', {
title: 'Registration form',
errors: errors.array(),
data: req.body,
});
}
}
);
Now we have to make a couple of changes to our form.pug
template. We firstly need to check for an errors
property, and if it’s present, loop over any errors and display them in a list:
extends layout
block content
if errors
ul
for error in errors
li= error.msg
...
If the li=
looks weird, remember that pug does interpolation by following the tag name with an equals sign.
Finally, we need to check if a data
attribute exists, and if so, use it to set the values of the respective fields. If it doesn’t exist, we’ll initialize it to an empty object, so that the form will still render correctly when you load it for the first time. We can do this with some JavaScript, denoted in Pug by a minus sign:
-data = data || {}
We then reference that attribute to set the field’s value:
input(
type="text"
id="name"
name="name"
value=data.name
)
That gives us the following:
extends layout
block content
-data = data || {}
if errors
ul
for error in errors
li= error.msg
form(action="." method="POST")
label(for="name") Name:
input(
type="text"
id="name"
name="name"
value=data.name
)
label(for="email") Email:
input(
type="email"
id="email"
name="email"
value=data.email
)
input(type="submit" value="Submit")
Now, when you submit a successful registration, you should see a thank you message, and when you submit the form without filling out both field, the template should be re-rendered with an error message.
We now want to hook our form up to our database, so that we can save whatever data the user enters. If you’re running Mongo locally, don’t forget to start the server with the command mongod
.
We’ll need somewhere to specify our database connection details. For this, we’ll use a configuration file (which should notbe checked into version control) and the dotenv package. Dotenv will load our connection details from the configuration file into Node’s process.env.
Install it like so:
npm install dotenv --save
And require it at the top of start.js
:
require('dotenv').config();
Next, create a file named .env
in the project root (note that starting a filename with a dot may cause it to be hidden on certain operating systems) and enter your Mongo connection details on the first line.
If you’re running Mongo locally:
DATABASE=mongodb://localhost:27017/node-demo-application
If you’re using mLabs:
mongodb://<dbuser>:<dbpassword>@ds251827.mlab.com:51827/<dbname>
Note that local installations of MongoDB don’t have a default user or password. This is definitely something you’ll want to change in production, as it’s otherwise a security risk.
To establish the connection to the database and to perform operations on it, we’ll be using Mongoose. Mongoose is an ORM for MongoDB.
Mongoose provides a straight-forward, schema-based solution to model your application data. It includes built-in type casting, validation, query building, business logic hooks and more, out of the box.
What this means in real terms is that it creates various abstractions over Mongo, which make interacting with our database easier and reduce the amount of boilerplate we have to write. If you’d like to find out more about how Mongo works under the hood, be sure to read our Introduction to MongoDB.
To install Mongoose:
npm install --save mongoose
Then, require it in start.js
:
const mongoose = require('mongoose');
The connection is made like so:
mongoose.connect(process.env.DATABASE, { useMongoClient: true });
mongoose.Promise = global.Promise;
mongoose.connection
.on('connected', () => {
console.log(`Mongoose connection open on ${process.env.DATABASE}`);
})
.on('error', (err) => {
console.log(`Connection error: ${err.message}`);
});
Notice how we use the DATABASE
variable we declared in the .env
file to specify the database URL. We’re also telling Mongo to use ES6 Promises (these are necessary, as database interactions are asynchronous), as its own default promise library is deprecated.
This is what start.js
should now look like:
require('dotenv').config();
const mongoose = require('mongoose');
mongoose.connect(process.env.DATABASE, { useMongoClient: true });
mongoose.Promise = global.Promise;
mongoose.connection
.on('connected', () => {
console.log(`Mongoose connection open on ${process.env.DATABASE}`);
})
.on('error', (err) => {
console.log(`Connection error: ${err.message}`);
});
const app = require('./app');
const server = app.listen(3000, () => {
console.log(`Express is running on port ${server.address().port}`);
});
When you save the file, nodemon will restart the app and, if all’s gone well, you should see something along the lines of:
Mongoose connection open on mongodb://localhost:27017/node-demo-application
MongoDB can be used as a loose database, meaning it’s not necessary to describe what data will look like ahead of time. However, out of the box it runs in strict mode, which means it’ll only allow you to save data it knows about beforehand. As we’ll be using strict mode, we’ll need to define the shape our data using a schema. Schemas allow you to define the fields stored in each document along with their type, validation requirements and default values.
To this end, create a models
folder in the project root, and within that folder, a new file named Registration.js
.
Add the following code to Registration.js
:
const mongoose = require('mongoose');
const registrationSchema = new mongoose.Schema({
name: {
type: String,
trim: true,
},
email: {
type: String,
trim: true,
},
});
module.exports = mongoose.model('Registration', registrationSchema);
Here, we’re just defining a type (as we already have validation in place) and are making use of the trim helper method to remove any superfluous white space from user input. We then compile a model from the Schema definition, and export it for use elsewhere in our app.
The final piece of boilerplate is to require the model in start.js
:
...
require('./models/Registration');
const app = require('./app');
const server = app.listen(3000, () => {
console.log(`Express is running on port ${server.address().port}`);
});
Now we’re ready to save user data to our database. Let’s begin by requiring Mongoose and importing our model into our routes/index.js
file:
const express = require('express');
const mongoose = require('mongoose');
const { body, validationResult } = require('express-validator/check');
const router = express.Router();
const Registration = mongoose.model('Registration');
...
Now, when the user posts data to the server, if validation passes we can go ahead and create a new Registration
object and attempt to save it. As the database operation is an asynchronous operation which returns a Promise, we can chain a .then()
onto the end of it to deal with a successful insert and a .catch()
to deal with any errors:
if (errors.isEmpty()) {
const registration = new Registration(req.body);
registration.save()
.then(() => { res.send('Thank you for your registration!'); })
.catch(() => { res.send('Sorry! Something went wrong.'); });
} else {
...
}
...
Now, if you enter your details into the registration form, they should be persisted to the database. You can check this using Compass (making sure to hit the refresh button in the top left if it’s still running).
Using Compass to check that our data was saved to MongoDB
To round the app off, let’s create a final route, which lists out all of our registrations. Hopefully you should have a reasonable idea of the process by now.
Add a new route to routes/index.js
, as follows:
router.get('/registrations', (req, res) => {
res.render('index', { title: 'Listing registrations' });
});
This means that we’ll also need a corresponding view template (views/index.pug
):
extends layout
block content
p No registrations yet :(
Now when you visit http://localhost:3000/registrations, you should see a message telling you that there aren’t any registrations.
Let’s fix that by retrieving our registrations from the database and passing them to the view. We’ll still display the “No registrations yet” message, but only if there really aren’t any.
In routes/index.js
:
router.get('/registrations', (req, res) => {
Registration.find()
.then((registrations) => {
res.render('index', { title: 'Listing registrations', registrations });
})
.catch(() => { res.send('Sorry! Something went wrong.'); });
});
Here, we’re using Mongo’s Collection#find method, which, if invoked without parameters, will return all of the records in the collection. Because the database lookup is asynchronous, we’re waiting for it to complete before rendering the view. If any records were returned, these will be passed to the view template in the registrations
property. If no records were returned registrations
will be an empty array.
In views/index.pug
, we can then check the length of whatever we’re handed and either loop over it and output the records to the screen, or display a “No registrations” message:
extends layout
block content
if registrations.length
table
tr
th Name
th Email
each registration in registrations
tr
td= registration.name
td= registration.email
else
p No registrations yet :(
The final feature we’ll add to our app is HTTP authentication, locking down the list of successful registrations from prying eyes.
To do this, we’ll use the http-auth module, which we can install using:
npm install --save http-auth
Next we need to require it in routes/index.js
, along with the Path module we met earlier:
const path = require('path');
const auth = require('http-auth');
Next, let it know where to find the file in which we’ll list the users and passwords (in this case users.htpasswd
in the project root):
const basic = auth.basic({
file: path.join(__dirname, '../users.htpasswd'),
});
Create this users.htpasswd
file next and add a username and password separated by a colon. This can be in plain text, but the http-auth module also supports hashed passwords, so you could also run the password through a service such as Htpasswd Generator.
For me, the contents of users.htpasswd
looks like this:
jim:$apr1$FhFmamtz$PgXfrNI95HFCuXIm30Q4V0
This translates to user: jim
, password: password
.
Finally, add it to the route you wish to protect and you’re good to go:
router.get('/registrations', auth.connect(basic), (req, res) => {
...
});
Let’s give the app some polish and add some styling using Twitter Bootstrap. We can serve static files such as images, JavaScript files and CSS files in Express using the built-in express.static middleware function.
Setting it up is easy. Just add the following line to app.js
:
app.use(express.static('public'));
Now we can load files that are in the public directory
.
Create a public
directory in the project root, and in the public
directory create a css
directory. Download the minified version of Bootstrap v4 into this directory, ensuring it’s named bootstrap.min.css
.
Next, we’ll need to add some markup to our pug templates.
In layout.pug
:
doctype html
html
head
title= `${title}`
link(rel='stylesheet', href='/css/bootstrap.min.css')
link(rel='stylesheet', href='/css/styles.css')
body
div.container.listing-reg
h1 My Amazing App
block content
Here, we’re including two files from our previously created css
folder and adding a wrapper div.
In form.pug
we add some class names to the error messages and the form elements:
extends layout
block content
-data = data || {}
if errors
ul.my-errors
for error in errors
li= error.msg
form(action="." method="POST" class="form-registration")
label(for="name") Name:
input(
type="text"
id="name"
name="name"
class="form-control"
value=data.name
)
label(for="email") Email:
input(
type="email"
id="email"
name="email"
class="form-control"
value=data.email
)
input(
type="submit"
value="Submit"
class="btn btn-lg btn-primary btn-block"
)
And in index.pug
, more of the same:
extends layout
block content
if registrations.length
table.listing-table.table-dark.table-striped
tr
th Name
th Email
each registration in registrations
tr
td= registration.name
td= registration.email
else
p No registrations yet :(
Finally, create a file called styles.css
in the css
folder and add the following:
body {
padding: 40px 10px;
background-color: #eee;
}
.listing-reg h1 {
text-align: center;
margin: 0 0 2rem;
}
/* css for registration form and errors*/
.form-registration {
max-width: 330px;
padding: 15px;
margin: 0 auto;
}
.form-registration {
display: flex;
flex-wrap: wrap;
}
.form-registration input {
width: 100%;
margin: 0px 0 10px;
}
.form-registration .btn {
flex: 1 0 100%;
}
.my-errors {
margin: 0 auto;
padding: 0;
list-style: none;
color: #333;
font-size: 1.2rem;
display: table;
}
.my-errors li {
margin: 0 0 1rem;
}
.my-errors li:before {
content: "! Error : ";
color: #f00;
font-weight: bold;
}
/* Styles for listing table */
.listing-table {
width: 100%;
}
.listing-table th,
.listing-table td {
padding: 10px;
border-bottom: 1px solid #666;
}
.listing-table th {
background: #000;
color: #fff;
}
.listing-table td:first-child,
.listing-table th:first-child {
border-right: 1px solid #666;
}
Now when you refresh the page, you should see all of the Bootstrap glory!
I hope you’ve enjoyed this tutorial. I hope help you in the world of Node-based web apps and offer you some solid takeaways for your next project in the process.
Thank you for reading.
#node-js #mongodb #mobile-apps #bootstrap
1624450037
Web development has been controlling the JavaScript system features for many years. Many big online sites use Java Script for their everyday operations. And recently there has been a change and a shift towards cross-platform mobile application development. The main software frameworks in work these days are React native, apache Cordova, native script and hybrid tools. In the last ten years, Node.JS has been used as a backend development framework. Developers nowadays want to learn and use the same technologies for one entire website. They do not want to learn an entire language for server development. And Node.JS is able to adapt all the functions and syntaxes to the backend services from JavaScript. If you do not know the languages or syntaxes for Node JS development, you can look for an online guide. These guides have a detailed overview of the additional functions and basic systems. You will also find simple tasks in these guides. To read more click on the link.
#node js development services #node js development #node js development company #hire node js developers #node js mobile app developmen #website developer