Hey guys, hopefully you are doing well, today is an introduction to get started with Grandjs the promising nodejs framework!
Grandjs is a javascript server side framework for building scalable web applications with solid architecture in nodejs
As anyone live in this planet hears every day about a new javascript framework and library and don't know why this happens or what is the difference between these new frameworks and the current frameworks!!
Expressjs is amazing and great framework, it have been maintaining nodejs development applications for many and many years, it gives you the ability to create, build anything you want because it's simplicity!
One concern on express, it's doesn't developed for javascript ES6+ features, it's a great choice for any type of applications but it doesn't have a new vision based on the current javascript state
Grandjs is build on typescript to get the benefits of typescript types, decorators and ES6+ features such as classes and so on
However Grandjs is built upon typescript, it's available for usage also in javascript without any need for compilation or code transpiling.
Grandjs gives you the ability to create solid routing architecture based on javascript classes, it gives you the ability to write one router which can be extended for different purposes and uses, it also gives you the ability to specify middle wares in an organized way for each route, router or apply global middle wares over the whole app.
These routing features gives you a full control over your routes to customize and consume your routes in different ways for different purposes
When it comes to rendering dynamic pages, you may find yourself puzzled to chose one good template engine contains everything you need, you may choose handlebars, ejs, pug or any other template engine, but have you asked yourself why a template engine in a language has template literals and JSX like javascript?
Javascript is a great language in itself, it contains most of things you need to build a full web application just in javascript, one of these amazing features is jsx syntax which allows you writing HTML like markup inside javascript code, which gives you the ability to do any operation on this markup, render data you want, easy to access data and many many other operations!
Grandjs decided to use JSX syntax to make server side rendering, so instead of using normal template engine, we need to get the power of javascript inside our HTML markups, so using JSX is the best choice!
Grandjs also doesn't just includes it's own architecture and methodology, it has an API similar to express js like render method which is renders the components, request object, response object and middle ware injecting.
Also Grandjs accepts many expressjs packages to be used inside it, it accepts using body-parser for parsing request body, cookie-parser for parsing cookies, sessions, morgan, express-fileupload and many other packages that can be used by injecting them inside the app by using this function
app.use()
However using these middlewares, Grandjs includes also a built in methods such as native request body parsing and fileupload without a nead to middlewares!
One of the amazing parts in grandjs, it gives you another helpers such as working with directories and files based on fs module, data validation, and ciphering methods to hash and secure data
Getting started with grandjs is so easy, you just need to be installed nodejs previously!
Create a new Directory, navigate to it and hit the following command in your cmd
npm init
Now you can install grandjs by the following command
npm i grandjs
Create a file called index.js as your entry point and require grandjs as the following:
const {Server} = require("grandjs/lib")
As you can see above I am requiring server object from grandjs/lib
Then we will set some configurations for this server as the following:
const {Server} = require("grandjs/lib");
const path = require("path")
// set config
Server.setConfig({
port: 3000,
staticFolder: {
url: "/public",
path: path.join(process.env.cwd(), "/uploads")
}
})
We have added some configurations to the server such as specify the port that the application will work on and static folder which contains our assets such as client side js files, css files or images
The static folder is an object contains two properties:
Now we will create A directory call views which will be the folder that contains our jsx components, this folder will contain the following components:
In each component we have to require View which is an object contains some methods to recognize the written component and parse them
const {View} = require("grandjs/lib")
The component should be a stateless functional component as the following
const {View} = require("grandjs/lib");
const Header = ({pageTitle}) => {
return (
<head>
<meta charset="utf-8"/>
<title>{pageTitle}</title>
</head>
)
}
module.exports = Header;
If you are familiar with react, you will find the code is too similar to react components, we are rendering some markup language to render the header of the page
Note that to export the component you have to use commonjs export syntax which is as the following:
module.exports = Header
Header.jsx component finally should be something similar to the following:
const {View} = require("grandjs/lib");
const Header = ({pageTitle}) => {
return (
<head>
<meta charset="utf-8"/>
<title>{pageTitle}</title>
</head>
)
}
module.exports = Header;
Body.jsx: This file will contain the html body markup and any data you want to insert at the middle of the rendered html page
const {View} = require("grandjs/lib");
const Body = (props) => {
return (
<body>
<h1>this is body from Body.jsx file & {props.pageT}</h1>
{props.children}
</body>
)
}
module.exports = Body;
As you can see above, we can pass child items to be rendered inside the component to render dynamic childs, as you can do with react
BasePage.jsx: This page page is considered as the page that we will extend or reuse for any other page we want to render, this basepage simply includes the header and body components
const {View} = require("grandjs/lib");
const Header = View.importJsx("./Header.jsx");
const BasePage = (props) => {
return (
<html lang="en" dir="ltr">
<Header pageTitle={props.pageTitle}/>
{props.children}
</html>
)
}
module.exports = BasePage;
In Granjs to import a component from another file you cannot import this component normally with require function, instead of that grandjs View provides you a function you can use to import jsx files, this function called View.importJsx. This method takes one argument which is the path of the jsx component.
const {View} = require("grandjs/lib");
const Header = View.importJsx("./Header.jsx");
Home.jsx: This page will be rendered as the home page, it has a simple html markup as the following:
const {View} = require("grandjs/lib");
const BasePage = View.importJsx("./BasePage.jsx");
const Body = View.importJsx("./Body.jsx");
const Home = (props) => {
return (
<BasePage pageTitle={props.pageTitle}>
<Body>
<h1>{props.message}</h1>
</Body>
</BasePage>
)
}
module.exports = Home;
User.jsx: This page contains markup syntax similar to the home page:
const {View} = require("grandjs/lib");
const BasePage = View.importJsx("./BasePage.jsx");
const Body = View.importJsx("./Body.jsx");
const User = (props) => {
return (
<BasePage pageTitle={props.pageTitle}>
<Body>
<h1>{props.message}</h1>
</Body>
</BasePage>
)
}
module.exports = User;
As you can see in the two pages home and user, we are passing dynamic data like pageTitle and message, these data will be passed later when we render the components, don't worry just keep reading 😂
Now we will create a directory called routes which will include our routes, inside this directory we will create two files, one called index.js which will contain the main router and the second file will called user.js which will contain the user routes user.js.
First require Router class from grandjs
const {Router} = require("grandjs/lib");
This Router is a class can be extended or instantiated directly, in this example we will extend this router
const {Router} = require("grandjs/lib");
class UserRouter extends Router{
}
In this router we will add single route which will be with url / to render the user page component.
Adding routers in grandjs is something easy as the following:
class UserRouter extends Router{
constructor(props) {
super(props);
this.base = "/user"
this.getRouters.push(this.getUserPage());
}
getUserPage() {
return {
url: "/",
method: "GET",
handler: () => {
return this.res.status(200).render(User, {pageTitle: "user page", message: "Hello User"})
}
}
}
}
Inside router class we have an array called getRouters this array contains all routes inside this router with GET method, the same thing for other methods, each request method as the following:
The method inside the router class should return an object, this object contains the following properties
This Router has a property called /base which is the main url that all routes inside this router will be resolved to.
Now we can export this router
const {Router, View} = require("grandjs/lib");
const User = View.importJsx("../views/User.jsx");
class UserRouter extends Router{
constructor(props) {
super(props);
this.base = "/user"
this.getRouters.push(this.getUserPage());
}
getUserPage() {
return {
url: "/user",
method: "GET",
handler: (req, res) => {
return res.status(200).render(User, {pageTitle: "user page", message: "Hello User"})
}
}
}
}
module.exports = UserRouter;
As you can see, we have imported User Page which will be render when the route /user will be visited
return res.status(200).render(User, {pageTitle: "user page", message: "Hello User"})
This statement inside the route handler sets the status of response as 200 which means that everything is OK and uses a method called render which is method used to render a JSX component. This method takes two parameters, the first one is the component that we need to render, and the second parameter is the data object that we want to pass to the component to be rendered inside the HTML
In index.js file we will create a main router
const {Router, View} = require("grandjs/lib");
const Home = View.importJsx("../views/Home.jsx");
const UserRouter = require("./user");
class HomeRouter extends Router{
constructor(props) {
super(props);
this.base = "/"
this.getRouters.push(this.getHomePage());
this.useRouter(UserRouter);
}
getHomePage() {
return {
url: "/",
method: "GET",
handler: () => {
return this.res.status(200).render(Home, {pageTitle: "Home Page", message: "Hello Home Page"})
}
}
}
}
module.exports = HomeRouter;
We have imported the User Router, Home page component and created another router called HomeRouter which will be the main and base router
Inside this router we are using function called useRouter which is can be used to append a router inside another router!
This way is so good because it allows you to append the parent router global middlewares and other functionality to the child router. In our example we are adding UserRouter as a router inside the HomeRouter
In the project entry point which is index.js we will import our needed dependencies and routers as the following:
const {Server, Router, View} = require("grandjs/lib");
View.settings.set("views", "./views");
const HomeRouter = require("./routes/index");
As you can see in the part above I have imported Server, Router and view.
Note: before Importing any component or router include a component, you have to define something in the view which is the path of the directory that your views components will be exist in, for me the views folder path is ./views
View.settings.set("views", "./views");
Now we will instantiate the HomeRouter and build it as the following:
// instantiate home router
let router = new HomeRouter();
// build home router
router.build();
Instantiating the router means start parsing it's properties and routes
Build method should be called to complete the router bootstrapping process and make the router ready to be consumed on the fly!
Now we will set some configuration related to the folder, these configuration will be the port that the application will work on
Server.setConfig({port: 3000});
Then initialize the server
Server.initServer();
Then initServer method can take a callback function after listening to the port
Server.initServer(() => {
console.log("server is running")
});
As you can see, it's super simple and easy to build extendable routes, rendering components like react but for server side rendering without a need to a template engine!
Thanks guys for reading this article, if you have any question don't hesitate to write it, and here is the documentation to learn more about this framework, also if you liked this article tell me to make more tutorials about it, thank you.