Express, node-фреймворк

Алан-э-Дейл       11.03.2024 г.

What is Node.js? – An Overview

Node.js is a server-side, Javascript-based runtime environment that’s a massive open-source tool. It attributes its success as a high-performance, scalable framework to the single-threaded process used for web loads and async programming. What’s more, you can use Node.js-based frameworks such as Express, Socket.io, and Meteor.js within it to enhance the backend capability of a project. Designed with real-time and push-based architectures, the technology is known to build single-page applications, websites, and backend API services.

Here are some market usage stats of Node.js as a backend technology:

  • 66.8% of developers rated Node.js as the most loved tool in Stackoverflow Developer Survey Report 2020.
  • 85% of Node.js developers use it for web app development, and the other 43% use it for enterprise-grade applications.
  • 51.4% of developers regarded Node.js as the most-used tool earlier in 2020.
  • StackOverflow Trends has noted an increase in the discussion around Node.js over the last decade covering almost 3.5% of questions.

Use cases of Node.js

  • Streaming web applications
  • Real-time software & Streaming apps (Collaboration tools used for video/audio conferencing, document editing, Chat applications, etc. )
  • Complex single-page apps
  • Microservices
  • IoT-based applications
  • Backends and servers
  • Developing APIs
  • Scripting and automation

Express GET request example

The function routes an HTTP GET request to the
specified path with the specified callback function.

app-simple.js

const express = require('express');

const app = express();

app.get('/', (req, res) => res.send('Hello there!'));

app.listen(3000, () => console.log('Application started on port 3000'));

The application processes a GET request and sends a short message to the client.

const express = require('express');

We include the package.

const app = express();

The Express application is created.

app.get('/', (req, res) => res.send('Hello there!'));

With the function, we map the path
to the anonymous function, which sends a string back to the client.

app.listen(3000, () => console.log('Application started on port 3000'));

When started, the application listens on port 3000.

$ node app-simple.js
Application started on port 3000

We start the application on localhost.

$ curl localhost:3000
Hello there!

We create a request with the command.

What is Express? – An Overview

Express.js or Express is an open-source server-side framework built for Node.js. Written in Javascript, it is part of MEAN, MEVN, and MERN stacks. Express provides plugins, template code, middleware packages, and routing functionality for faster and efficient web development. Additionally, the framework allows third-party integrations of libraries and features to make customization easier.

Here are some market usage stats of Express as a backend technology:-

A study by BuiltWith shows that Express is the 10th most popular framework in the Top 10k sites.SimilarTech reports around 193,346 websites are built with Express worldwide, out of which 38,847 are made exclusively in the US.
According to a study by Statistics & Data, Express ranks 4th among the top 5 backend frameworks for 2021.

Use Cases of Express:-

  • Single-page apps
  • Reusable apps
  • Middleware Applications
  • RESTful APIs
  • Serve and access static files using a browser
  • Enterprise web applications
  • eCommerce web applications

Application overview

We will build Rest Apis for creating, retrieving, updating, deleting and searching Tutorials.

First, we start with an Express web server. Next, we add configuration for MySQL database, create model, write the controller. Then we define routes for handling all CRUD operations:

The following table shows overview of the Rest APIs that will be exported:

Methods Urls Actions
GET api/tutorials get all Tutorials
GET api/tutorials/:id get Tutorial by
POST api/tutorials add new Tutorial
PUT api/tutorials/:id update Tutorial by
DELETE api/tutorials/:id remove Tutorial by
DELETE api/tutorials remove all Tutorials
GET api/tutorials/published find all published Tutorials
GET api/tutorials?title= find all Tutorials which title contains

Finally, we’re gonna test the Rest Apis using Postman.

This Node.js Rest API works with Client in one of these posts:

  • Simple HTTP Client using Axios
  • Simple HTTP Client using Fetch API
  • Angular 8 / Angular 10 / Angular 11 / Angular 12 / Angular 13
  • Vue 2 / Vue 3
  • React / React Redux

Our project structure will be like:

Development Dependencies

In development, we’ll be writing Typescript. Therefore, we need to install . We’ll also want to install the types for both express and node. We use the flag to let know that these are dev dependencies.

Great! But we’re not quite done. Sure, we could stop here, but the problem is that we would need to compile our code every time we wanted to see changes in development. That’s no fun! So we’ll add a couple additional dependences:

  • —this package will let us run Typescript without having to compile it! Crucial for local development.
  • —this package automagically watches for changes in your application code and will restart your dev server. Coupled with , will enable us to see changes reflected in our app instantaneously!

Again, these are development dependencies because they only help us with development and won’t be used after our code is compiled for production.

File Upload

The following HTML code creates a file uploader form. This form has method attribute set to POST and enctype attribute is set to multipart/form-data

<html>
   <head>
      <title>File Uploading Form</title>
   </head>

   <body>
      <h3>File Upload:</h3>
      Select a file to upload: <br />
      
      <form action = "http://127.0.0.1:8081/file_upload" method = "POST" 
         enctype = "multipart/form-data">
         <input type="file" name="file" size="50" />
         <br />
         <input type = "submit" value = "Upload File" />
      </form>
      
   </body>
</html>

Let’s save above code in index.htm and modify server.js to handle home page requests as well as file upload.

var express = require('express');
var app = express();
var fs = require("fs");

var bodyParser = require('body-parser');
var multer  = require('multer');

app.use(express.static('public'));
app.use(bodyParser.urlencoded({ extended: false }));
app.use(multer({ dest: '/tmp/'}));

app.get('/index.htm', function (req, res) {
   res.sendFile( __dirname + "/" + "index.htm" );
})

app.post('/file_upload', function (req, res) {
   console.log(req.files.file.name);
   console.log(req.files.file.path);
   console.log(req.files.file.type);
   var file = __dirname + "/" + req.files.file.name;
   
   fs.readFile( req.files.file.path, function (err, data) {
      fs.writeFile(file, data, function (err) {
         if( err ) {
            console.log( err );
            } else {
               response = {
                  message:'File uploaded successfully',
                  filename:req.files.file.name
               };
            }
         
         console.log( response );
         res.end( JSON.stringify( response ) );
      });
   });
})

var server = app.listen(8081, function () {
   var host = server.address().address
   var port = server.address().port
   
   console.log("Example app listening at http://%s:%s", host, port)
})

Accessing the HTML document using http://127.0.0.1:8081/index.htm will generate the following form −

File Upload:
Select a file to upload: 
NOTE: This is just dummy form and would not work, but it must work at your server.

Wrapping Up

It took me the better part of four years to start using proper tooling and adhering to best practices. In the end, I just want to point out the most important part of your application is to be available and performant. Otherwise, you won’t see any users stick around. If they can’t use your application, what’s the point?

The idea behind this article was to cover best practices you should stick to, but also the bad practices to stay away from.

You’ve learned many new things in this Express.js tutorial. From optimizing Express.js itself, creating an intuitive project structure and optimizing for performance to learning about JavaScript best practices and test-driven development. You’ve also learned about error handling, logging and monitoring.

After all this, you can say with certainty that you’ve had an introduction to DevOps culture. What does that mean? Well, making sure to write reliable and performant software with test coverage, while maintaining the best possible developer productivity. That’s how we as engineers continue loving our job. Otherwise, it’s all mayhem.

Hope you all enjoyed reading this as much as I enjoyed writing it. If you liked it, feel free to hit the share button so more people will see this tutorial. Until next time, be curious and have fun.

Define the Sequelize Model

In models folder, create tutorial.model.js file like this:

This Sequelize Model represents tutorials table in MySQL database. These columns will be generated automatically: id, title, description, published, createdAt, updatedAt.

After initializing Sequelize, we don’t need to write CRUD functions, Sequelize supports all of them:

  • create a new Tutorial:
  • find a Tutorial by id:
  • get all Tutorials:
  • update a Tutorial by id:
  • remove a Tutorial:
  • remove all Tutorials:
  • find all Tutorials by title:

These functions will be used in our Controller.

We can improve the example by adding Comments for each Tutorial. It is the One-to-Many Relationship and I write a tutorial for this at:Sequelize Associations: One-to-Many example – Node.js, MySQL

Or you can add Tags for each Tutorial and add Tutorials to Tag (Many-to-Many Relationship):Sequelize Many-to-Many Association example with Node.js & MySQL

The Middleware in Express.js

Express.js uses the powerful concept of middleware, which allows us to build a modular application.

Remember, the goal of our application is to listen to HTTP requests and provide responses to them. As such, we can consider each interaction to have one request, and one subsequent response. Obviously, each interaction starts with a request and an empty response, and as we process it we build the response.

So, imagine your Express.js app as a set of pipes, chained one after the other. The request is water running inside the pipes. Each pipe, in reality, is a middleware. And, each of them gets a chance to read the request, modify the response, but also read the response coming from the previous middlewares.

Express.js middleware work pretty much like pipes, they process a request and output a response on the other side.

In this way, you can build all the code to address a request as a set of smaller components (the middlewares), that you will engage in sequence, one after the other. Even if this seems trivial, it is an amazing capability in terms of write clear code.

Express send file

The function transfers the file at the given path.
The image is displayed in the browser. The function
transfers the image; the image is offered as an attachment by browsers.

app-send-file.js

const express = require('express');
const path = require('path');

const app = express();


app.get('/file', (req, res) => {

    res.set({ 'Content-Type': 'image/jpeg' });

    let file = path.join(__dirname, 'img/book.jpg');

    // res.sendFile(file);
    res.download(file, 'book-image.jpg');
});


app.listen(3000, () => {

    console.log('Application started on port 3000');
})

The example sends an image to the client. Note that since
the browsers are doing caching, we might no see a difference between
the two methods. In such a case, we can open a private window.

res.set({ 'Content-Type': 'image/jpeg' });

We set the appropriate content type.

let file = path.join(__dirname, 'img/book.jpg');

We specify the path to the image.

2: Создание сервера Express

Теперь, когда фреймворк Express установлен, создайте новый файл server.js, откройте его с помощью редактора кода и добавьте следующие строки кода:

const express = require('express');


const app = express();

Первая строка – это запрос основного модуля Express из установленного нами пакета. Этот модуль представляет собой функцию, которую мы затем запускаем во второй строке, чтобы создать переменную app. Таким образом вы можете создать несколько приложений, каждое со своими запросами и ответами.

const express = require('express');


const app = express();


app.get('/', (req, res) => {

  res.send('Successful response.');

});

Следующие три строки кода сообщают серверу Express, как обрабатывать GET-запрос. Express включает аналогичные функции для POST, PUT и других подобных запросов (через app.post (…), app.put (…) и так далее).

Эти функции принимают два основных параметра. Первый – это URL-адрес, на который распространяется эта функция. В этом случае мы установим ‘/’ в качестве цели (то есть функция будет предназначена для корневого URL-адреса нашего веб-сайта, в данном случае это localhost:3000).

Второй параметр – это функция с двумя аргументами: req и res. Объект req представляет запрос, который был отправлен на сервер; мы можем использовать его, чтобы прочитать данные о том, что просит сделать клиент. Объект res представляет собой ответ, который мы отправим обратно клиенту.

Здесь мы вызываем функцию в res, чтобы отправить ответ: ‘Successful response.’

Добавьте в файл server.js следующие строки:

const express = require('express');


const app = express();


app.get('/', (req, res) => {

  res.send('Successful response.');

});

app.listen(3000, () => console.log('Example app is listening on port 3000.'));

Итак, мы настроили запросы, пора запустить наш сервер! Передайте 3000 в функцию listen, которая сообщает приложению, какой порт прослушивать. Функция, переданная в качестве второго параметра, является опциональной и запускается при запуске сервера – она дает нам в консоли обратную связь, чтобы мы могли понять, что приложение запущено.

Вернитесь в окно терминала и запустите приложение:

Затем посетите localhost:3000 в своем веб-браузере. В окне браузера должно отобразиться сообщение: ‘Successful response’. В терминале вы увидите: ‘Example app is listening on port 3000.’

Значит, наш веб-сервер работает. Однако мы, конечно, хотим отправить клиенту больше, чем одну строку текста. Давайте кратко рассмотрим, что такое промежуточное ПО и как настроить наш готовый сервер как статический файловый сервер.

CRUD — CREATE

Browsers can only perform a CREATE operation if they send POST request to the server. This request can be triggered through JavaScript or through a element.

Let’s figure out how to use a element to create new entries for this Star Wars quote application for now. We’ll examine how to send requests via JavaScript later.

To send a POST request through a , you need to add the element to your file.

You need three things on this form element:

  1. An attribute
  2. A attribute
  3. attributes on each elements within the form

The tells browsers what kind of request to send. In this case, we use because we’re sending a request.

The attribute tells the browser where to send the request. In this case, we’re send the request to .

We can handle this request with a method in . The path should be the value you placed in the attribute.

Restart your server (hopefully you’ve set up Nodemon so it restarts automatically) and refresh your browser. Then, enter something into the element and submit the form. Next, look at your command line. You should see in your command line.

 

Great, we know that Express is handling the form for us right now. The next question is, how do we get the input values with Express?

Turns out, Express doesn’t handle reading data from the element on it’s own. We have to add another package called body-parser to gain this functionality.

Body-parser is a middleware. They help to tidy up the object before we use them. Express lets us use middleware with the method.

The method within body-parser tells body-parser to extract data from the element and add them to the property in the object.

You should be able to see values from the element inside now. Try doing a and see what it is!

You should see an object similar to the following:

Hmmm.

Master Yoda has spoken! Let’s make sure we remember Yoda’s words. It’s important. We want to be able to retrieve it the next time we load our index page.

Enter the database, MongoDB.

Cache headers intro

The http header Cache-control allows to define how your proxy server (e.g. Nginx), your CDN and client browsers will cache content and serve it instead of forwarding requests to the app.

You can find the full specification of Cache-control at MDN. The following are the key parameters that we will use in our example configuration:

  • public — indicates that the response may be cached by any cache. That means that every layer that your response passes through is allowed to cache your content and serve it. This is the setting you want for most of your content representing articles, blogs, ‘static’ pages, product pages etc…

  • max-age — in seconds specifies how long the content is supposed to be cached. You want to set this as long as possible without compromising the ability to refresh your content where it is changing. Anything from 5 minutes to a day (or even a few days) is probably a good choice for most ‘publishers’ depending on the frequency they publish and refresh content. So for example, if you publish a lot of new content, your home page could be cached for only 5 minutes but pages with articles or blog posts could be cached for a day.

Here is how the header looks like to enable public 5 minut cache:

npm install (with options)

In addition to arguments, npm install can be run with different options. Here are some of the more important ones to be aware of…

npm install (with —global)

When run with —global or -g, npm install installs the package globally. This means the package is installed in two places. The first is at the root directory where package.json is defined. The second is the global node_modules folder on the user system.

As a general rule, you want to install packages globally when they will be reused across projects or when they provide executable commands from the CLI.

npm install (with —save)

When run with —save, npm install modifies the package.json file to include the specified package as a dependency. In this case, the express package will be added as a dependency to package.json.

The —save option is important whenever you want future installs to include the specified package.

npm install (with —save-dev)

The —save-dev flag specifies that the package should be added to the devDependencies section of the package.json rather than the dependencies section.

npm dependencies vs devDependencies

So what’s the difference? Packages included as devDependencies won’t get installed when the optional —production flag is used. This makes it possible to exclude packages you only need for development.

For example, linters are popular for enforcing clean code but aren’t necessary in production. You would include a linter package as a devDependency so you can run linters against your code locally without including it in a production build.

Test the APIs

Run our Node.js application with command: . The console shows:

Using Postman, we’re gonna test all the Apis above.

  1. Create a new Tutorial using Api

After creating some new Tutorials, we can check MySQL table:

Retrieve all Tutorials using Api

Retrieve a single Tutorial by id using Api

Update a Tutorial using Api

Check table after some rows are updated:

Find Tutorials by title using Api

Find all published Tutorials

Delete a Tutorial using Api

Tutorial with id=4 was removed from table:

Delete all Tutorials using Api

Now there are no rows in table:

You can also test this Node.js Rest API with Client in one of these posts:

  • Simple HTTP Client using Axios
  • Simple HTTP Client using Fetch API
  • Angular 8 CRUD Application example with Web API
  • Angular 10 CRUD Application example with Web API
  • Angular 11 CRUD Application example with Web API
  • Angular 12 CRUD Application example with Web API
  • Angular 13 CRUD Application example with Web API
  • Vue 2 CRUD Application with Vue Router & Axios
  • Vue 3 CRUD Application with Axios & Vue Router
  • React CRUD example to consume Web API
  • React Redux CRUD example with API calls

Showing quotes to users (READ operation)

We need to do two things to show quotes from MongoDB Atlas to our users.

  1. Get quotes from MongoDB Atlas.
  2. Rendering the quotes in HTML with a template engine

Let’s go one step at a time.

Getting quotes from MongoDB

We can get quotes we stored in MongoDB with the method. This method from mLab by using the method that’s available in the method.

The method returns a which won’t make sense if you tried logging it.

 

But this object contains all quotes from our database! It has a bunch of method that lets us get our data. For example, we can use to convert the data into an array.

 

Great! We see the quotes we added! (You see so many of the same quotes because I added them all when writing this tutorial ).

Next we want to generate a HTML that contains all our quotes.

Rendering the HTML

We cannot serve up the file and expect quotes to magically appear because there’s no way to add dynamic content to a HTML file.

What we can do, instead, is to use a template engine to generate the HTML. Popular template engines include Pug, Embedded JavaScript, and Nunjucks.

I’ve wrote extensively about the how and why of template engines in a separate post. You might want to check it out if you have no idea what template engines are.

I use Nunjucks as my template engine of choice. Feel free to check out the post to find out why.

For this tutorial, we will use Embedded JavaScript (EJS) as our template engine because it’s the easiest to start with. You’ll find it familiar from the get-go since you’ll be writing HTML and JavaScript.

Using EJS

First, we need to install EJS.

Next, we need to set to . This tells Express we’re using EJS as the template engine. You can need to place it before any , or methods.

We can now generate HTML that contains the quotes. This process is called rendering the HTML.

We will use the method built into Express’s . It needs to follow the following syntax:

  • is the name of the file we’re rendering. This file must be placed inside a folder.
  • is the data passed into the file.

Let’s create a view. We’ll make an file inside the views folder.

We’ll copy/paste everything from into .

Next, we’ll use to render this file.

If you refresh the page, you should still see the same thing. Nothing should change, nothing should break.

Let’s put the quotes into . To do this, we need to pass the quotes into the method.

In , we can use place variables between and tags. Let’s try putting into the HTML:

You should see this:

We see lots of because each quote inside is a JavaScript object. cannot convert that object into HTML automatically.

We need to loop through the quotes. We can do this with a loop. In EJS, we write a for loop like how we write a JavaScript loop. The only difference is we need to put the loop statements between and .

Conclusion & Further Reading

With this short tutorial, we covered all the tools you need to get started with Express.js. Yet, the is more road to cover to become a professional. Here I want to share with you a few resources that you absolutely need to read to master Express.js and writing large apps without shooting yourself in the foot along the way (aka building something that becomes too cumbersome to maintain that you eventually abandon it).

Start with the Bulletproof node.js project architecture. This is THE guide on how to structure your project. In this complete article, Sam Quinn (@SantyPK4) will explain how to scaffold your project, how to arrange your files, and why it is important. Following his suggestions, you will create a project that you are able to maintain for years. Personally, I have been using this approach for a while and it just couldn’t be better. It also comes with an example on GitHub.com, so you can look at all the details there.

The second reading I suggest is Building and using APIs. Read this after the Bulletproof node.js project architecture, which is more important. While the project architecture explains how to structure your files and folder, this guide shows you how to structure your path and interactions that you allow for the user. Express.js is the perfect framework to create APIs, and APIs are what we are creating indeed. Hence, before going wild with paths and resources, read that guide. You can read the same article on GitHub here. Oh, and it’s from The White House.

Templating in style

One area where simple and real programs differ is the creation of views. While a small example project usually uses hand-crafted strings, assembling large swaths of HTML with a string of connected things is highly annoying.

Template engines provide a neat workaround. They permit the creation of predefined schema files, which can be populated programmatically in execution. 

In the case of our example program, the views lay in .jade files. Opening index reveals the following structure:

Expressions enclosed in curly brackets act as template fields whose values are to be replaced at runtime. Index.js invokes render with a parameter object, leading to the rendering of the start page shown in the figure accompanying this step:

Most templating engines can also parse arrays when provided with an item template. In this case, every line of the array is displayed with one instance of the DOM model – similarities to the list display model found in Android are purely coincidental. Express.JS is not limited to the predefined templating engines. Should you feel like rolling out your own for some reason, simply follow the steps outlined  – in principle, you have to override all but one function.

Application architecture – Node.js vs. Express

When choosing a framework, it’s important to choose flexibility and avoid any strict enforcement of architecture and guidelines. As a matter of fact, it’s always recommended to treat a framework as a guide, not methods, and standards. That said, let’s juxtapose Node.js and Express with each other and check whether they are flexible in terms of architecture.

What kind of architecture does Node.js support?

Under the hood, Node. js leverages the Single-threaded Event Loop architecture that enables it to handle multiple concurrent requests with high performance. However, Node.js also lets you use MVC/ MVP architecture pattern, which eases isolating and onboarding issues in the application codebase. In addition to this, it creates multiple views for the same data and supports asynchronous communication between various components.

Here’s how you benefit from the async architecture of Node.js:

  • Processes multiple concurrent requests simultaneously and offers high performance.
  • Ensures faster and flexible development of modules.
  • Reduces time-to-market of applications.

What kind of architecture does Express support?

Express does not require a specific architecture, and developers have the liberty to determine a structure for their web apps. However, MVC (Model-View-Controller) is the preferred and most common architecture type for Express apps.

app.js

The file is special, because it sets up your Express application and glues all of the different parts together. Let’s walk through what it does. Here’s how the file starts:

These first six lines of the file are required. If you’re new to Node, be sure to read “Understanding module.exports and exports in Node.js”.

The next two lines of code the different route files that the Express generator sets up by default: and .

After that, we create a new app by calling . The app variable contains all of the settings and routes for your application. This object glues together your application.

Once the app instance is created, the templating engine is set up for rendering views. This is where you’d change the path to your view files if necessary.

After this, you’ll see Express being configured to use middleware. The generator installs several common pieces of middleware that you’re likely to use in a web application:

  • logger. When you run your app, you might notice that all the routes that are requested are logged to the console. If you want to disable this, you can just comment out this middleware.
  • express.json. You might notice that there are two lines for parsing the body of incoming HTTP requests. The first line handles when JSON is sent via a POST request and it puts this data in .
  • express.urlencoded. The second line parses query string data in the URL (e.g. ) and puts this in .
  • cookieParser. This takes all the cookies the client sends and puts them in . It also allows you to modify them before sending them back to the client, by changing .
  • express.static. This middleware serves static assets from your folder. If you wanted to rename or move the public folder, you can change the path here.

Next up is the routing:

Here, the example route files that were required are attached to our app. If you need to add additional routes, you’d do it here.

All the code after this is used for error handling. You usually won’t have to modify this code unless you want to change the way Express handles errors. By default, it’s set up to show the error that occurred in the route when you’re in development mode.

Гость форума
От: admin

Эта тема закрыта для публикации ответов.