NodeJS #3

Basic practice

$ whoami

Inna Ivashchuk

Senior Software Engineer

JS developer, music fan, movie-dependent and Star Wars fan 🤓

May the Force be with you!

4+ years with GlobalLogic

6+ years in web-development

        GitHub page

Agenda

  • HTTP methods, headers, and status codes

  • REST API

  • http module

  • Command-line arguments. Environment variables

  • Let’s create a REST API

HTTP methods, headers and status codes

HTTP

     Hypertext Transfer Protocol (HTTP) is an application-layer protocol for transmitting hypermedia documents, such as HTML. It was designed for communication between web browsers and web servers, but it can also be used for other purposes. HTTP follows a classical client-server model, with a client opening a connection to make a request, then waiting until it receives a response. HTTP is a stateless protocol, meaning that the server does not keep any data (state) between two requests.

How HTTP works

How HTTP works

      Designed in the early 1990s, HTTP is an extensible protocol that has evolved. It is an application layer protocol that is sent over TCP, or over a TLS-encrypted TCP connection, though any reliable transport protocol could theoretically be used.

HTTP methods

GET

POST

OPTIONS

PATCH

DELETE

PUT

TRACE

HEAD

CONNECT

HTTP methods

GET Get a representation of the target resource’s state.
GET Requests a representation of the specified resource. Requests using GET should only retrieve data.
HEAD Asks for a response identical to that of a GET request, but without the response body.
POST Used to submit an entity to the specified resource, often causing a change in state or side effects on the server.
PUT Replaces all current representations of the target resource with the request payload.
DELETE Deletes the specified resource.
CONNECT Establishes a tunnel to the server identified by the target resource.
OPTIONS Used to describe the communication options for the target resource.
TRACE Performs a message loop-back test along the path to the target resource (for proxy-debugging purposes)
PATCH Used to apply partial modifications to a resource.

Description

HTTP methods

HTTP header

     HTTP headers let the client and the server pass additional information with an HTTP request or response. An HTTP header consists of its case-insensitive name followed by a colon (:), then by its value. Whitespace before the value is ignored.

HTTP headers

Authentication

Cookies

CORS

Caching

Proxies

Security

Redirects

HTTP header types 

     Headers can be grouped according to their contexts:

  • Request headers contain more information about the resource to be fetched, or about the client requesting the resource.
  • Response headers hold additional information about the response, like its location or about the server providing it.
  • Representation headers contain information about the body of the resource, like its MIME type, or encoding/compression applied.
  • Payload headers contain representation-independent information about payload data, including content length and the encoding used for transport.

HTTP header types 

    Headers can also be grouped according to how proxies handle them:

The most common response codes

408

Request Timeout

200

OK

301

Moved Permanently

401

Unauthorized

403

Forbidden

404

Not Found

429

Too Many Requests

409

Conflict

500

Internal Server Error

Headers: Content-Type

The Content-Type representation header is used to indicate the original media type of the resource (prior to any content encoding applied for sending).

In responses, a Content-Type header tells the client what the content type of the returned content actually is. Browsers will do MIME sniffing in some cases and will not necessarily follow the value of this header; to prevent this behavior, the header X-Content-Type-Options can be set to nosniff.

In requests, (such as POST or PUT), the client tells the server what type of data is actually sent.

Headers: Content-Type

MIME types:

  • application (application/zip , application/javascript an etc)
  • audio (audio/mpeg, audio/vorbis)
  • font (font/woff, font/ttf, and font/otf)
  • image (image/jpeg, image/png, and image/svg+xml)
  • text (text/plain, text/csv, and text/html)
  • video (video/mp4)

REST API

App 2

API

App 3

App 1

App 4

Standard situation

What is REST API?

    A REST API (also known as RESTful API) is an application programming interface (API or web API) that conforms to the constraints of REST architectural style and allows for interaction with RESTful web services. REST stands for representational state transfer and was created by computer scientist Roy Fielding.

Client

Database

GET, POST, PUT, PATCH, DELETE

REST API Model

JSON data

REST API

Architectural constraints

In order for an API to be considered RESTful, it has to conform to six criteria:

  • A client-server architecture
  • Stateless client-server communication,
  • Cacheable data that streamlines client-server interactions.
  • A uniform interface between components so that information is transferred in a standard form. 
  • A layered system that organizes each type of server (those responsible for security, load-balancing, etc.) involved the retrieval of requested information into hierarchies, invisible to the client.
  • Code-on-demand (optional): the ability to send executable code from the server to the client when requested, extending client functionality. 

Applied to web services

Web service APIs that adhere to the REST architectural constraints are called RESTful APIs. HTTP-based RESTful APIs are defined with the following aspects:

  • a base URI, such as http://api.example.com/;
  • standard HTTP methods (e.g., GET, POST, PUT, and DELETE);
  • a media type that defines state transition data elements (e.g., Atom, microformats, application/vnd.collection+json,:91–99 etc.).

The semantics of HTTP methods

     The following table shows how HTTP methods are intended to be used in HTTP APIs, including RESTful ones.

GET Get a representation of the target resource’s state.
GET Used to get a resource from a server. The server looks for the data you requested and sends it back to you. A GET request performs a READ operation
POST Used to create a new resource on a server. On a POST request, the server creates a new entry in the database and tells you whether the creation is successful.
POST request performs a CREATE operation.
PUT / PATCH These two requests are used to update a resource on a server. Using such requests the server updates an entry in the database and tells you whether the update is successful.
PUT or PATCH request performs an UPDATE operation.
DELETE Used to delete a resource from a server. On a DELETE request, the server deletes an entry in the database and tells you whether the deletion is successful.

Description

HTTP methods

The anatomy of a Request

It’s important to know that a request is made up of four things:

The endpoint (or route)

The methods

The headers

The data (or body)

Endpoints or routes

  • https://api.github.com/users (fetch all users)
  • https://api.github.com/users/1  (fetch one user by ID)
  • https://api.github.com/users?per_page=5 (fecth first 5 users)

GET

  • https://api.github.com/users  (create a user with provided data)

POST

{
  login: "mojombo",
  // id should be created
  node_id: "MDQ6VXNlcjE=",
  avatar_url: "https://avatars.githubusercontent.com/u/1?v=4",
  gravatar_id: "",
  url: "https://api.github.com/users/mojombo",
  ....
  type: "User",
  site_admin: false
}

Endpoints or routes

PATCH / PUT

  • https://api.github.com/users/1  (delete a user by ID)

DELETE

{
  login: "mojombo",
  id: 1,
  node_id: "MDQ6VXNlcjE=",
  avatar_url: "https://avatars.githubusercontent.com/u/1?v=4",
  gravatar_id: "",
  url: "https://api.github.com/users/mojombo",
  ....
  type: "User",
  site_admin: false
}
  • https://api.github.com/users/1  (update a user by ID  with provided data)

cURL

GET

curl -X GET \
	-H "Accept: application/json" \
	-H "Content-Type: application/json" \
	 https://jsonplaceholder.typicode.com/posts/1
    
  • https://api.github.com/users/1 

cURL is a computer software project providing a library and command-line tool for transferring data using various network protocols. The name stands for "Client URL", which was first released in 1997.

cURL

POST

curl -X POST \
  -H "Content-Type: application/json; charset=UTF-8" \
  -d '{"title": "The best actor", "body": "Keanu Reeves", "userId": 1}' \
  https://jsonplaceholder.typicode.com/posts
  • https://jsonplaceholder.typicode.com/posts

http module

http module

To use the HTTP server and client one must require('http').

The HTTP interfaces in Node.js are designed to support many features of the protocol which have been traditionally difficult to use. In particular, large, possibly chunk-encoded, messages. The interface is careful to never buffer entire requests or responses, so the user is able to stream data.

const http = require('http');

// request from the server to an API
http.get('http://localhost:3000/api/users', { agent }, (res) => {
  res.on('data', (data) => {
  // Do nothing or do something =)
  });
});

Create a server

const http = require('http');

const server = http.createServer((request, response) => {
  // magic happens here!
});

server.on('request', (request, response) => {
  // the same kind of magic happens here!
})

    Any node web server application will at some point have to create a web server object. This is done by using createServer.

    The function that's passed into createServer is called once for every HTTP request that's made against that server, so it's called the request handler.

    The Server object returned by createServer is an EventEmitter.

Method, URL and Headers

const { method, url, headers } = request;

const userAgent = headers['user-agent'];

if (url === '/login') {
  // redirect to login
}

The request object is an instance of IncomingMessage.

Request Body

let body = [];

request.on('data', (chunk) => {
  body.push(chunk);
}).on('end', () => {
  body = Buffer.concat(body).toString();
  // at this point, `body` has the entire
  // request body stored in it as a string
});

When receiving a POST or PUT request, the request body is important to your application. The request object that's passed into a handler implements the ReadableStream interface. 

The chunk emitted in each 'data' event is a Buffer.

HTTP Status Code

const http = require('http');

const server = http.createServer((request, response) => {
  // set correct status code
  response.statusCode = 404;
  // magic happens here! 
});

If you don't bother setting it, the HTTP status code on a response will always be 200.

HTTP Status Code

const http = require('http');

const server = http.createServer((request, response) => {
  // set correct status code
  response.statusCode = 404;
  // magic happens here! 
});

If you don't bother setting it, the HTTP status code on a response will always be 200.

Setting Response Headers

response.setHeader('Content-Type', 'application/json');
response.setHeader('X-Powered-By', 'bacon');

Headers are set through a convenient method called setHeader.

   We can explicitly write the headers to the response stream. To do this, there's a method called writeHead, which writes the status code and the headers to the stream.

response.writeHead(200, {
  'Content-Type': 'application/json',
  'X-Powered-By': 'bacon'
});

Sending Response Body

response.write('<html>');
response.write('<body>');
response.write('<h1>Hello, World!</h1>');
response.write('</body>');
response.write('</html>');
response.end();

Since the response object is a WritableStream, writing a response body out to the client is just a matter of using the usual stream methods.

 Or to send as the last bit of data on the stream:

response.end('<html><body><h1>Hello, World!</h1></body></html>');

A Quick Thing About Errors

request.on('error', (err) => {
  // This prints the error message and stack trace to `stderr`.
  console.error(err.stack);
});

Since the request object is a ReadableStream, it's also an EventEmitter and behaves like one when an error happens. 

Note: If we don't have a listener for that event, the error will be thrown, which could crash your Node.js program.

Command-line arguments. Environment variables

What is an environment variable? 

An environment variable is a dynamic-named value that can affect the way running processes will behave on a computer. They are part of the environment in which a process runs.

.env file

 .env file can be used to store needed environment variables. A . env file or dotenv file is a simple text configuration file for controlling your Applications's environment constants.

# .env file
APP_NAME="restfull-api"
NODE_ENV="development"
PORT="3000"
HOST="127.0.0.1"

How to read environment variables from Node.js

The process core module of Node.js provides the env property which hosts all the environment variables that were set at the moment the process was started.

 

The below code runs app.js and set NODE_ENV and APP_NAME.

NODE_ENV=development APP_NAME=foobar node app.js

How to read environment variables from Node.js

function() {
	const mode = process.env.NODE_ENV;
	const appName = process.env.APP_NAME;
    
	console.log({ mode, appName });
}

To access environment variable in JS file:

In the same way, we can access any custom env. variables.

How to read environment .env file

APP_NAME="restfull-api"
NODE_ENV="development"
PORT="3000"
HOST="127.0.0.1"

If you have multiple environment variables in your node project, you can also create an .env file in the root directory of your project, and then use the dotenv package to load them during runtime.

How to read environment .env file

const env = require('dotenv').config();

function app() {
    console.log({ env });

    console.log('parsed env PORT: ', env.parsed.PORT);
}

app();

As we are using dotenv package in our JS files and it will look like:

We can also run our js file with node -r dotenv/config app.js command if we don't want to import the package in your code.

Accept arguments from the command line

node app.js joe

Arguments can be standalone or have a key and a value.

For example:

 

or:

node app.js name=joe

Accept arguments from the command line

node app.js joe

  From the process object, built into Node.js, it exposes an argv property, which is an array that contains all the command line invocation arguments.

 

If we have one argument without an index name, like this:

// can iterate over all the arguments
process.argv.forEach((val, index) => {
  console.log(`${index}: ${val}`)
});
const args = process.argv.slice(2)
args[0]

we can access it using

Postman and Swagger

Swagger

Postman

Let’s create a REST API

What should be implemented

  1. RESTfull API

  2. Several different endpoints (routes) to interact with API

  3. The server should serve static files (index.html, main.js, and main.css) using a non-blocking IO approach - "fs/promises"

Q & A

Homework

  1. Create a new directory tutorial3

  2. Create API, that follows architecture constraints of REST API

  3. Create different endpoints to interact with API

  4. Create static files (index.html, main.js, and main.css) 

  5. Server static on '/' route

NodeJS Core #3

By Inna Ivashchuk

NodeJS Core #3

  • 425