# Grandjs
A backend framework for solid web apps based on node.js
You want to build a solid web application but you don't want to use express and a lot of packages you need to build a basic website, Grandjs is one framework includes all main functionalities you need to build amazing, solid and secured web application without need for a ton of packages and libraries.
- Grandjs is built for solid and extended web apps
Prerequisites
1- you need to install node.js on your system
2- init a new project using npm, so then we will be able to install this framework
Features
- fewer packages and more productivity
- Framework includes the most needed functionalities to build a perfect web app
- depends on Handlebars template Engine which is friendly with many developers
- solid routing system built on Javascript object-oriented programming and you can extend it as you need
- controlled handling for every error page in your app
- Grandjs is built for solid and extended web apps
const http = require("http");
const Grandjs = require("grandjs");
Grandjs.setConfig({
port: process.env.PORT || 3000,
http: http,
httpsMode: {
key: fs.readFileSync('test/fixtures/keys/agent2-key.pem'),
cert: fs.readFileSync('test/fixtures/keys/agent2-cert.pem')
}
staticFolder: "public",
session: true,
ENCRYPTION_KEY: "ncryptiontestforencryptionproces",
setCookie: {
expires: {
days: 20,
minutes: 30
}
},
logger: true,
errorPage(req, res) {
res.end("error page");
}
});
Grandjs.initServer();
content
Installing
open the command prompt and navigate to the project folder and just say
npm install grandjs --save
Getting Started
Server configuration
to start with Grandjs just install it and call it in your file as the following
const Grandjs = require("grandjs");
if you want to require HTTP or HTTPS module to pass it to Grand js you can, in all cases, Grandjs behind the seen requires HTTP module as a default.
Now you need to call setConfig function to set some configuration for the project
Grandjs.setConfig({});
this function takes one parameter as an object
Example on all configuration
const http = require("http");
const Grandjs = require("Grand");
Grandjs.setConfig({
port: 3000,
http: http,
staticFolder: "public",
session: true,
httpsMode: {
key: fs.readFileSync('test/fixtures/keys/agent2-key.pem'),
cert: fs.readFileSync('test/fixtures/keys/agent2-cert.pem')
}
ENCRYPTION_KEY: "ncryptiontestforencryptionproces",
setCookie: {
expires: {
days: 20,
minutes: 30
}
},
logger: true,
errorPage(req, res) {
res.end("error page");
}
})
until now Grandjs doesn't work so you need to call "init function" to initialize the server
Grandjs.initServer();
Router
- Grandjs Routing system is built upon object-oriented programming, so Router is a class inside Grandjs you extend it and add routes to it as you wanna
to work with router class you need to extend it or instatiate it directly and every class implies a group of routes have a specific basename
extend Router class
class HomeRouter extends Grandjs.Router{
constructor(options) {
super(options);
}
}
As any extended class, you should call the super inside the constructor and pass the options parameter
parameter | type |
---|
options | Object |
Now after defining the class, you should define the get routers which related to this class
Router class has a property called "getRouters" this is an array and you push inside it the routers you add within the class with the GET method
Example
class HomeRouter extends Grandjs.Router{
constructor(options) {
super(options);
this.getRouters = []
}
}
you can add routers which related to this class as methods inside the class and every method you use it as a router it should return an object has the following properties
Property | type | description |
---|
URL | string (required) | the URL of the router |
method | string (required) | HTTP method get / post / patch / put / delete |
handler | function (required) | the function you want to run when the request URL matches the router url |
middleWares | array (optional) | if you want to run a function or more to check about something before running the final handler of the router |
Define get routers
Example
class HomeRouter extends Grandjs.Router{
constructor(options) {
super(options);
this.getRouters = [this.homePage()];
}
homePage() {
return {
url: "/",
method: "GET",
handler: () => {
}
}
}
}
Define post routers
Example
class HomeRouter extends Grandjs.Router{
constructor(options) {
super(options);
this.postRouters = [this.homePagePost()];
}
homePagePost() {
return {
url: "/",
method: "POST",
handler: () => {
}
}
}
}
instantiate router class
Example
class HomeRouter extends Grandjs.Router{
constructor(options) {
super(options);
this.getRouters = [this.homePage()];
}
homePage() {
return {
url: "/",
method: "GET",
handler: () => {
}
}
}
}
const homeRoters = new HomeRouter({});
when you instantiate the class you should define the options parameter as an object includes two properties
property | type | descriptions |
---|
base | string (required) | implies the base URL you want to add routers to it. so if you defined it as "/admin" all routers inside this class would be added to /admin |
staticFolder | string(optional) | the name of the folder you want to serve assets and static files from it. The default value of it is the global staticFolder that you specified in setConfig function |
Example
const homeRoters = new HomeRouter({
base: "/"
});
Access on request and response objects
to handle the routers and check requests and responses you need to access them, so these objects are accessible using different ways
1- req & res objects are properties inside the class
Example
class HomeRouter extends Grandjs.Router{
constructor(options) {
super(options);
this.getRouters = [this.homePage()];
}
homePage() {
return {
url: "/",
method: "GET",
handler: () => {
console.log(this.req.headers);
this.res.end("hello home page");
}
}
}
}
2- request & response are accessable as parameters inside handler function
Example
homePage() {
return {
url: "/",
method: "get",
handler: (req, res) => {
console.log(req.headers);
res.end("hello home page");
}
}
}
Specify a separated folder for static files for this group of routes
const homeRoters = new HomeRouter({
base: "/",
staticFolder: "homePublic"
});
Router add route
This function enables you to add routers to the class from outside it
homeRoters.addRoute(obj);
this function takes one (required) parameter as an object has the following properties
Property | type | description |
---|
URL | string (required) | the URL of the router |
method | string (required) | HTTP method get / post / patch / put / delete |
handler | function (required) | the function you want to run when the request URL matches the router url |
middleWares | array (optional) | if you want to run a function or more to check about something before running the final handler of the router |
Example
const adminRoute = new Grandjs.Router({
baes: "/admin"
})
adminRoute.addRoute({
url: "/",
method: "get",
handler: (req, res) => {
res.end("hello admin");
}
});
homePage.addRoute({
url: "/profile",
method: "get",
handler: (req, res) => {
res.end("hello profile");
}
});
Router class errorPage
you can specify a custom error page for every class you instantiate it to control on error links in a group of routes
to do that you need to define "errorPage" method to the class
1- define it inside the class
Example
class HomeRouter extends Grandjs.Router{
constructor(options) {
super(options);
}
homePage() {
return {
url: "/",
method: "get",
handler: () => {
this.res.end("hello home page");
}
}
}
aboutPage() {
return {
url: "/about",
method: "get",
handler: () => {
this.res.end("hello about page");
}
}
}
errorPage() {
this.res.end("error page")
}
}
2-Define error page from outside the class
Example
const homeRouter = new Grandjs.Router({
baes: "/"
})
homeRouter.addRoute({
url: "/",
method: "get",
handler: (req, res) => {
res.end("hello homepage");
}
});
homeRouter.addRoute({
url: "/about",
method: "get",
handler: (req, res) => {
res.end("hello aboutpage");
}
});
homeRouter.errorPage = (req, res) => {
res.end("error page");
}
*not that if you didn't define error page for the router class it automatically call error page which you specified in setConfig function
Global middleWares
Global middleware is a way to apply middlewares on a class which includes a group of routers
globalMiddleWares is an array inside the class you can define it inside the constructor and put inside it functions that you want to run when the client requests the base name of that class
Example
class HomeRouter extends Grandjs.Router{
constructor(options) {
super(options);
this.globalMiddleWares = [this.sendMessage]
}
sendMessage(req, res, next) {
res.write("message from middleWare");
next();
}
homePage() {
return {
url: "/",
method: "get",
handler: () => {
this.res.end("hello home page");
}
}
}
aboutPage() {
return {
url: "/about",
method: "get",
handler: () => {
this.res.end("hello about page");
}
}
}
errorPage() {
this.res.end("error page")
}
}
not that the middlWares array can include many middleWare functions and the middleWares are applied according to the indexing inside the array
Every middleWare has three parameters
parameter | type | description |
---|
req | object | implies the coming request object contains all information about the request |
res | object | represents the response object |
next | function | is a function you can execute it to continue to the next middleware or to the final handler of the router |
Router URL define params
you can add params to the URL of the router to make dynamic routes like the following:
url: "/posts/:postId"
to access the parameters inside the URL using request.params property
Example
const homePage = new Grandjs.Router({
baes: "/"
})
home.addRoute({
url: "/posts/:postId",
method: "get",
handler: (req, res) => {
console.log(req.params);
}
});
Request Object
this is an object you can access on it inside the handler of the route
and the object contains all information about the request which is coming
Property | type | description |
---|
req.method | string | returns the method of the current request |
req.pathname | string | returns the requested URL without query string |
req.path | string | returns the requested URL with query string |
req.href | string | returns the requested URL with query string |
req.url | object | returns object contains the parsed URL |
req.query | object | contains the query & search in the URL(it parsed as key and value) |
req.params | object | returns the query parameters if it exists as key and value |
req.validation | object | returns an object contains some helper functions to validate the email and string |
req.data | object | returns an object contains the posted data if the method is "post" |
req.headers | object | returns the headers of the coming request |
req.flash | object | this object enables you to set messages to send it to handlebars to show to the user |
Request params
returns an object contains the parameters of the router URL and it's value if it exists
if there are no params so it returns an empty object
Example
const homePage = new Grandjs.Router({
baes: "/"
})
home.addRoute({
url: "/posts/:category",
method: "get",
handler: (req, res) => {
console.log(req.params);
console.log(req.query)
console.log(req.pathname)
console.log(req.path)
console.log(req.href)
console.log(req.method)
console.log(req.url)
}
});
Handle post request
Grandjs handles all types of post requests and submitted data and returns them as an object called req.data
this object contains all the submitted data and you can access on it inside the handler function if the method of the router is post
Example
postAdminPage() {
return {
method: "post",
url: "/admin/addinfo",
handler: (req, res) => {
console.log(req.data)
}
}
}
Response Object
this is an object you can access on it inside the handler of the route
This object contains all methods that you need to send a response or content to the coming request
res.render
function
This function you use it to render HTML content using handlebars template engine
res.render()
takes one parameter as an object, this object should contain the following properties
property | type | description |
---|
container | string (required) | this property you specify the container folder which includes all handlebars files |
layout | string (required) | should be the path of the layout file |
body | string (optional) | should be the path of the body file |
partials | Array (optional) | every item inside this array should be a string refers to the path of the partial file that you want to append to the layout |
data | object (optional) | object contains the data you want to render inside the handlbars files |
Example
homePageRouter() {
return {
url: "/",
method: "get",
handler: () => {
this.res.render({
container: "views",
layout: "/layouts/layout.hbs",
body: "/pages/home.hbs"
})
}
}
}
res.write
function
this function is like the native api of node.js, it allows you to send strings to the client
res.write("hello world");
res.end("");
res.end
function
this function is like the native api of node.js, it allows you to send strings to the client
res.end("hello world");
res.sendFile
function
res.sendFile(path);
this function takes on parameter
parameter | type | description |
---|
path | String (required) | this parameter should specify the path of the file which you want to send |
this function uses promise to return a catch
function if the file isn't exist
Example
res.sendFile("/views/pages/home.html").catch((err) => {
console.log(err)
})
res.json
function
this function sends json data, it accepts one parameter this parameter should be an object and Grandjs stringify this object automatically
res.json({user: "tarek", email: "test@gmail.com", id: 1});
res.redirect
function
This function is used to make redirect to another link
It accepts one parameter
parameter | type | description |
---|
url | String (required) | this parameter should specify url that you want to redirect to |
res.redirect("/anotherurl");
res.status
function
this function sets the status of the response with http status code, it accepts one parameter which is the status code of the response
res.status(200).json({user: "tarek", email: "test@gmail.com", id: 1});
MiddleWares
middleWares is a group of functions used to run something before executing the final handler
the middWares property should be an array includes the functions
Every middleware should have three parameters
parameter | type | description |
---|
req | object | implies the coming request object contains all information about the request |
res | object | represents the response object |
next | function | is a function you can execute it to continue to the next middleWare or to the final handler of the router |
Example
function writeWithMiddleware(req, res, next) {
res.write("from middleware");
next();
}
homePage() {
return {
url: "/home",
middleWares: [],
method: "get",
handler: (req, res) => {
res.end("hello home page");
}
}
}
note that the middlWares array can include many middleWare functions and the middleWares are applied according to the indexing inside the array
Use Router class inside another router class
you can build a router class and append another router class to its parent, This is designed for special use case as the following:
suggest you have parent router class has basename /admin
and you want to group some of routes to manage products for example so the default way you can create another class with basename /admin/products
so we came up with the solution to use router classes inside another routing classes which give you the flexibility to use child routes inside parent route as the following:
class ProductRoutes extends Router{
constructor(options) {
super(options);
this.postRouters = [this.addProduct()]
}
addProduct() {
return {
url: "/product",
method: "POST",
handler: (req, res) => {
console.log(req.data);
}
}
}
}
class AdminRoutes extends Router{
constructor(options) {
super(options)
this.getRouters = [this.homePage()];
this.useRouter(ProductRoutes)
}
homePaage() {
return {
url: "/",
method: "GET",
handler: (req, res) => {
res.end("home page!");
}
}
}
}
Validation
Grandjs includes awesome validation system to validate inputs and remove strip tags and check the correct email
you can access on validation using on of two ways
1- Grandjs.helpers.validation
2- as a property inside request object inside handlers
validation.striphtmltags
function
this function removes weird characters from the string to insure that there is no harmful characters inside the string
let str = "h1hello worldh1"
Grandjs.helpers.validation.strip_html_tags(str)
validation.checkEmail
function
This function checks if the string is emain or not
this function takes two parameters
parameter | type | description |
---|
email | string (required) | this parameter is required and it should be a string that you want to test it as email or not |
cb | function (optional) | this is a callback function you can call it and includes one parameter either be true or false |
This function you can call it async with a callback function or sync without callback
1- with callback function
Grandjs.helpers.validation.checkEmail("test@gmail.com", (email) => {
if(email) {
console.log(email)
} else {
console.log(email)
}
});
2- without callback
let email = Grandjs.helpers.validation.checkEmail("test@gmail");
console.log(email)
validation.notEmpty
function
This function checks if the given string is empty or not
it accepts two parameters
parameter | type | description |
---|
string | string (required) | to test it is empty or not |
cb | function (optional) | this is a callback function you can call it and includes one parameter either be true or false |
This function you can call it async with a callback function or sync without callback
1- with callback function
Grandjs.helpers.validation.notEmpty("", (notEmpty) => {
if(notEmpty) {
console.log(notEmpty)
} else {
console.log(notEmpty)
}
});
2- without callback
let notEmpty = Grandjs.helpers.validation.notEmpty("");
console.log(email)
validation.checkContainsNumber
function
This function checks if the given string contains numbers or not
it accepts three parameters
parameter | type | description |
---|
string | string (required) | to test it contains numbers or not |
count | Number (required) | refers to the count of the number you want to test the string contains. if you specify it for example 5 so the function checks if the given string contains five numbers |
cb | function (optional) | this is a callback function you can call it and includes one parameter either be true or false |
This function you can call it async with a callback function or sync without callback
1- with callback function
Grandjs.helpers.validation.checkContainsNumber("Grandjs32test1", 3, (containsNumbers) => {
if(containsNumbers) {
console.log(containsNumbers)
} else {
console.log(containsNumbers)
}
});
2- without callback
let containsNumbers =
Grandjs.helpers.validation.checkContainsNumber("Grandjs32test1", 3);
console.log(containsNumbers)
validation.checkIsNumber
function
This function checks if the given parameter is a number or not
you can use this function to authenticate phone number and stuff like that
it accepts two parameters
parameter | type | description |
---|
value | any (required) | to test it is number or not |
cb | function (optional) | this is a callback function you can call it and includes one parameter either be true or false |
This function you can call it async with a callback function or sync without callback
1- with callback function
let test = 1222;
Grandjs.helpers.validation.checkIsNumber(test, (number) => {
if(number) {
console.log(number)
} else {
console.log(number)
}
});
2- without callback
let test = 1222;
let number = Grandjs.helpers.validation.checkIsNumber(test);
console.log(number)
Cryption
Grandjs gives you functionalities to crypt important info and cipher them and decrypt them
encryption functions are inside helpers inside grandjs library
This helper uses the ENCRYPTION_KEY
that you specify in setConfig
function.
not that the length of ENCRYPTION_KEY
should be 32 character
enCrypt
Grandjs.helpers.enCrypt(text);
This function takes one parameter which refers to the string you wanna encrypt or cipher it
parameter | type | description |
---|
text | string (required) | implies the text that you wanna cipher it |
This function rreturns the string after cipher it
let encryptedPassword = Grandjs.helpers.enCrypt("passowrd");
deCrypt
Grandjs.helpers.deCrypt(text);
This function takes one parameter which refers to the string you wanna decrypt or decipher it
parameter | type | description |
---|
text | string (required) | implies the text that you wanna decipher it |
This function rreturns the string after decipher it
let decryptedPassword = Grandjs.helpers.deCrypt("passowrd");
Session
Grandjs represents to you a session module which you can use it in login processes and setting cookies for logged in users
- you need to enable session in Grandjs configuration inside
Grandjs.setConfig
function as the following:
Grandjs.setConfig({
session: true
})
2- now you need to set the max age of cookies for global sessions inside Grandjs.setConfig
function as the following:
Grandjs.setConfig({
session: true
setCookie: {
expires: {
days: 30,
minutes: 30
}
}
})
setCooki
is an object, you specify inside it an object called expires
this object has two properties
property | type | description |
---|
days | number (optional) | refers to the number of days that you want to set the cookie until it |
minutes | number | refers to the number of minutes that you want to set the cookie until it |
Not that session is a global variable you can access on it from anywhere inside the project
session.set
function
This is a function you call it when you want to set a session, this function takes three parameters
parameter | type | description |
---|
sessionName | string(required) | this parameter specified the name of the session for example if the session for login so you can set it as "userInfo" |
obj | object (required) | this is the info that you want to store in the session |
sessionOptions | object (optional) | represents specific cookie configuration for this session only |
Example on session inside handler function
- set it without a custom cookie
postRouter () {
return {
url: "/user",
method: "post",
handler: () => {
let userdata = this.req.data;
session.set("userdata", userdata);
}
}
}
- set it with a custom cookie
property | type | description |
---|
expires | object | the epires days and minutes of cookies |
path | string (default: /) | represents the path that you want to make the user logged in, it is set to / as default |
postRouter () {
return {
url: "/user",
method: "post",
handler: () => {
let userdata = this.req.data;
session.set("userdata", userdata, {
expires: {
days: 1
},
path: "/"
});
}
}
}
session.getSession
function
This function gets the session of the user based on the name of the session that you specified in session.set
function
if the session is exist this function returns an object contains data that you have stored, and if no session with that name it will return an undefined
let info = session.getSession("userdata");
console.log(info)
session.updateSession
function
This function is very similar to session.set
function and it takes the same parameters but this function you use it to updating an already exist session
session.set("userdata", {name: "tarek", password: "blablabla"});
session.breakSession
function
This function breaks the session and stops it, so you use it when the user logs out
This function takes one parameter, that parameter is the name of the session that you want to break
session.breakSession("userdata");
Auth module
This module is a built in module in Grandjs, it is used to make authentication inside the system to allow user login and logout securely.
This module depends on the session
- Auth module is a global module you can access on it from any file inside the project
- This module represents to some functionalities for logging in process
auth.login
function
This function is used to log in the user when he signs up or in.
- This function takes the same parameters of
session.set
function and auth.login
depends on it
This function takes three parameters
parameter | type | description |
---|
loginName | string(required) | this parameter specified the name of the session for example if the session for login so you can set it as "userInfo" |
obj | object (required) | this is the info that you want to store in the session |
sessionOptions | object (optional) | represents specific cookie configuration for this session only |
auth.login("userdata", {username: "tarek", email:"test@gmail.com"});
auth.checkAuth
function
This function is used to check if the user is logged in or not
- If the user is logged in returns:
true
- if the user isn't logged in returns:
false
let logged = auth.checkAuth();
auth.Auth
function
This function is optional to use, the of it is if you are doing a heavy checks before require auth.login
function so Grandjs represents to you this function which enables you to make some processing and returns a callback function after that has done
parameter, it's value is: true
or false
auth.Auth((done, cb) => {
let some = 1+2;
if(some === 3) {
done = true;
return cb(done);
} else {
done = false;
return cb(done);
}
}, (done) => {
console.log(done)
if(done == true) {
auth.login("userdata", {});
}
})
auth.logout
function
This function depends on session.breakSession
function, it logouts the user and destroys the session
auth.logout();
Flash Messages
Flash is an object inside request
object, it allows you to set messages to send them to the handlebars to show alert messages or success or any message you want to show from backend to frontend
flash is accessable inside any router handler
Example
router.addRoute({
url: "/login",
method: "post",
handler: () => {
req.flash.errorMessage = "user not found"
}
})
router.addRoute({
url: "/login",
method: "get",
handler: () => {
this.res.render({
layouts: "./views/layouts/main.hbs",
body: "login.hbs",
data: {
errorMessage: this.req.flash.errorMessage
}
})
}
})
Add MimeTypes
if you want to serve more static files with another mimetype not exist in our built in mimetypes, so you can use the following function
Grandjs.addMimeTypes(extention, mimeType);
This function takes two parameters
Parameter | type | description |
---|
extention | string (required) | represents the extention of the file that you want to check |
mimeType | string (required) | The mime type that you want to set in the response header for the specific file |
Exmple
Granjs.addMimeTypes(".pdf", "application/pdf");
Built With
Authors
- Tarek Salem - Initial work - github
License
This project is licensed under the MIT License - see the LICENSE.md file for details
Acknowledgments
- url-pattern
- Handlebars template engine
- Hat tip to anyone whose code was used
- Inspiration