I'm building a REST api server in Node js. Let's say I have an Api - http://localhost:3000/api/employee/:employeeId. And I want to fetch the employeeId parameter without using any framework such as Express js. If possible what would be a better approach ?
**If you Need the Employee Id from Url So You can use this - **
const express = require('express');
const router = express.Router();
router.get('http://localhost:3000/api/employee/:employeeId',getUserById);
getUserById(req, res) {
console.log('---EmployeeId ----',req.params.employeeId);
}
Here's a vanilla js function to get the url segments
function UrlSegments() {
// Change this to the correct url if its not
// the url that you want to process
// You can also do window.location.pathname to process
// the current path.
let urlSegments: string[] = /api/employee/:employeeId
// Remove the empty string before the first '/'
if (urlSegments.shift() === "")
urlSegments.slice(1, -1);
// Remove the empty string after the last '/'
if (urlSegments.slice(-1)[0] === "")
urlSegments.splice(-1, 1);
return urlSegments;
}
string employeeId = UrlSegments()[2];
I was wondering how you would use node.js to parse a string parameter from a request url akin to express.
I know this is possible with express, but I would like to know how it can be done with node.js without express.
Express example:
var app = require('express')();
app.get('sample/request/url/:id', function(req, res) {
var parameter = req.params.id;
});
If your are using connect (or just http module) you can use RegExp:
With http:
var http = require('http');
http.createServer(function (req, res) { // Note there's no next here
var match = req.url.match(/^sample\/request\/url\/(.+)$/);
var id = match ? match[1] : null;
}).listen(3000);
...
With connect:
var connect = require('connect');
connect.createServer(funcion(req, res, next) {
var match = req.url.match(/^sample\/request\/url\/(.+)$/);
var id = match ? match[1] : null;
}).listen(3000);
...
This is the simple case. If you want to have your own routing middleware you should start with an array of RegExps (that can be generated dinamically from a String that you add) and loop through them until you find a match.
Each route element should have its RegExp and also its parameters, so that once you find a match you can extract and append the parameters to the req object with an appropriate name that you choose.
EDIT:
As robertklep pointed out in his comment, you can check paramify. Its code is very clear and does some of the things I said in the last part of the answer. For example, you can see it has a function regify to dinamically contruct the RegExps and a loop to extract the parameters of a match:
var params = []
for (var i = 1; i < matches.length; i++) {
var key = reg.keys[i - 1]
if (key) {
params[key.name] = matches[i]
} else {
params.push(matches[i])
}
}
You can get the url property from req and parse as you want:
var server = require('http').createServer(function (req, res) {
console.log(req.url);
// would log "/sample/request/url/123"
});
The parse part can be done using RegEx.
I have a Node.js / Express app working, that receives routes like so:
app.get('/resource/:res', someFunction);
app.get('/foo/bar/:id', someOtherFunction);
This is great and works fine.
I am also using Socket.IO, and want to have some server calls use websockets instead of traditional RESTful calls. However, I want to make it very clean and almost use the same syntax, preferrably:
app.sio.get('/resource/:res', someFunction);
This will give a synthetic 'REST' interface to Socket.IO, where, from the programmer's perspective, he isn't doing anything different. Just flagging websockets: true from the client.
I can deal with all the details, such as a custom way to pass in the request verbs and parse them and so and so, I don't have a problem with this. The only thing I am looking for is some function that can parse routes like express does, and route them properly. For example,
// I don't know how to read the ':bar',
'foo/:bar'
// Or handle all complex routings, such as
'foo/:bar/and/:so/on'
I could dig in real deep and try to code this myself, or try to read through all of express' source code and find where they do it, but I am sure it exists by itself. Just don't know where to find it.
UPDATE
robertklep provided a great answer which totally solved this for me. I adapted it into a full solution, which I posted in an answer below.
You can use the Express router class to do the heavy lifting:
var io = require('socket.io').listen(...);
var express = require('express');
var sioRouter = new express.Router();
sioRouter.get('/foo/:bar', function(socket, params) {
socket.emit('response', 'hello from /foo/' + params.bar);
});
io.sockets.on('connection', function(socket) {
socket.on('GET', function(url) {
// see if sioRouter has a route for this url:
var route = sioRouter.match('GET', url);
// if so, call its (first) callback (the route handler):
if (route && route.callbacks.length) {
route.callbacks[0](socket, route.params);
}
});
});
// client-side
var socket = io.connect();
socket.emit('GET', '/foo/helloworld');
You can obviously pass in extra data with the request and pass that to your route handlers as well (as an extra parameter for example).
robertklep provided a great answer which totally solved this for me. I adapted it into a full solution, which is below in case others want to do something similar:
Node (server side):
// Extend Express' Router to a simple name
app.sio = new express.Router();
app.sio.socketio = require('socket.io').listen(server, { log: false });
// Map all sockets requests to HTTP verbs, which parse
// the request and pass it into a simple callback.
app.sio.socketio.sockets.on('connection', function (socket) {
var verbs = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'];
for (var i = 0; i < verbs.length; ++i) {
var go = function(verb) {
socket.on(verb, function (url, data) {
var route = app.sio.match(verb, url);
if (route && route.callbacks.length) {
var req = {url: url, params: route.params, data: data, socket:socket}
route.callbacks[0](req);
}
});
}(verbs[i]);
}
});
// Simplify Socket.IO's 'emit' function and liken
// it traditional Express routing.
app.sio.end = function(req, res) {
req.socket.emit('response', req.url, res);
}
// Here's an example of a simplified request now, which
// looks nearly just like a traditional Express request.
app.sio.get('/foo/:bar', function(req) {
app.sio.end(req, 'You said schnazzy was ' + req.data.schnazzy);
});
Client side:
// Instantiate Socket.IO
var socket = io.connect('http://xxxxxx');
socket.callbacks = {};
// Similar to the server side, map functions
// for each 'HTTP verb' request and handle the details.
var verbs = ['get', 'post', 'put', 'path', 'delete'];
for (var i = 0; i < verbs.length; ++i) {
var go = function(verb) {
socket[verb] = function(url, data, cb) {
socket.emit(String(verb).toUpperCase(), url, data);
if (cb !== undefined) {
socket.callbacks[url] = cb;
}
}
}(verbs[i]);
}
// All server responses funnel to this function,
// which properly routes the data to the correct
// callback function declared in the original request.
socket.on('response', function (url, data) {
if (socket.callbacks[url] != undefined) {
socket.callbacks[url](data);
}
});
// Implementation example, params are:
// 1. 'REST' URL,
// 2. Data passed along,
// 3. Callback function that will trigger
// every time this particular request URL
// gets a response.
socket.get('/foo/bar', { schnazzy: true }, function(data){
console.log(data); // -> 'You said schnazzy was true'
});
Thanks for your help, robertklep!
I'm trying to use VersionOne JS SDK in Node.js (https://github.com/versionone/VersionOne.SDK.JavaScript). I'm simply downloading whole library, placing it alongside with my js file:
var v1 = require('./v1sdk/v1sdk.js');
var V1Server = v1.V1Server;
console.log(v1);
console.log(V1Server);
Unfortunately something seems wrong, the output I get after calling
node app.js
is:
{}
undefined
Can somebody point me what I'm doing wrong or check whether the sdk is valid.
Thanks!
You can see in the source where V1Server is defined, that it's a class with a constructor. So you need to use the new keyword and pass the arguments for your environment.
https://github.com/versionone/VersionOne.SDK.JavaScript/blob/master/client.coffee#L37
var server = new V1Server('cloud'); //and more if you need
Can you try the sample.js script that I just updated from here:
https://github.com/versionone/VersionOne.SDK.JavaScript/blob/master/sample.js
It pulls in the two modules like this:
var V1Meta = require('./v1meta').V1Meta;
var V1Server = require('./client').V1Server;
var hostname = "www14.v1host.com";
var instance = "v1sdktesting";
var username = "api";
var password = "api";
var port = "443";
var protocol = "https";
var server = new V1Server(hostname, instance, username, password, port, protocol);
var v1 = new V1Meta(server);
v1.query({
from: "Member",
where: {
IsSelf: 'true'
},
select: ['Email', 'Username', 'ID'],
success: function(result) {
console.log(result.Email);
console.log(result.Username);
console.log(result.ID);
},
error: function(err) { // NOTE: this is not working correctly yet, not called...
console.log(err);
}
});
You might have to get the latest and build the JS from CoffeeScript.
I think I was trying out "browserify" last year and that's how the "v1sdk.js" file got generated. But I'm not sure if that's the best approach if you're using node. It's probably better just to do it the way the sample.js file is doing it.
However, I did also check in a change to v1sdk.coffee which property exports the two other modules, just as a convenience. With that, you can look at sample2.js. The only different part there is this, which is more like you were trying to do with your example:
var v1sdk = require('./v1sdk');
var hostname = "www14.v1host.com";
var instance = "v1sdktesting";
var username = "api";
var password = "api";
var port = "443";
var protocol = "https";
var server = new v1sdk.V1Server(hostname, instance, username, password, port, protocol);
var v1 = new v1sdk.V1Meta(server);
I want to be able to set a single cookie, and read that single cookie with each request made to the nodejs server instance. Can it be done in a few lines of code, without the need to pull in a third party lib?
var http = require('http');
http.createServer(function (request, response) {
response.writeHead(200, {'Content-Type': 'text/plain'});
response.end('Hello World\n');
}).listen(8124);
console.log('Server running at http://127.0.0.1:8124/');
Just trying to take the above code directly from nodejs.org, and work a cookie into it.
There is no quick function access to getting/setting cookies, so I came up with the following hack:
const http = require('http');
function parseCookies (request) {
const list = {};
const cookieHeader = request.headers?.cookie;
if (!cookieHeader) return list;
cookieHeader.split(`;`).forEach(function(cookie) {
let [ name, ...rest] = cookie.split(`=`);
name = name?.trim();
if (!name) return;
const value = rest.join(`=`).trim();
if (!value) return;
list[name] = decodeURIComponent(value);
});
return list;
}
const server = http.createServer(function (request, response) {
// To Read a Cookie
const cookies = parseCookies(request);
// To Write a Cookie
response.writeHead(200, {
"Set-Cookie": `mycookie=test`,
"Content-Type": `text/plain`
});
response.end(`Hello World\n`);
}).listen(8124);
const {address, port} = server.address();
console.log(`Server running at http://${address}:${port}`);
This will store all cookies into the cookies object, and you need to set cookies when you write the head.
If you're using the express library, as many node.js developers do, there is an easier way. Check the Express.js documentation page for more information.
The parsing example above works but express gives you a nice function to take care of that:
app.use(express.cookieParser());
To set a cookie:
res.cookie('cookiename', 'cookievalue', { maxAge: 900000, httpOnly: true });
To clear the cookie:
res.clearCookie('cookiename');
RevNoah had the best answer with the suggestion of using Express's cookie parser. But, that answer is now 3 years old and is out of date.
Using Express, you can read a cookie as follows
var express = require('express');
var cookieParser = require('cookie-parser');
var app = express();
app.use(cookieParser());
app.get('/myapi', function(req, resp) {
console.log(req.cookies['Your-Cookie-Name-Here']);
})
And update your package.json with the following, substituting the appropriate relatively latest versions.
"dependencies": {
"express": "4.12.3",
"cookie-parser": "1.4.0"
},
More operations like setting and parsing cookies are described here
and here
As an enhancement to #Corey Hart's answer, I've rewritten the parseCookies() using:
RegExp.prototype.exec - use regex to parse "name=value" strings
Here's the working example:
let http = require('http');
function parseCookies(str) {
let rx = /([^;=\s]*)=([^;]*)/g;
let obj = { };
for ( let m ; m = rx.exec(str) ; )
obj[ m[1] ] = decodeURIComponent( m[2] );
return obj;
}
function stringifyCookies(cookies) {
return Object.entries( cookies )
.map( ([k,v]) => k + '=' + encodeURIComponent(v) )
.join( '; ');
}
http.createServer(function ( request, response ) {
let cookies = parseCookies( request.headers.cookie );
console.log( 'Input cookies: ', cookies );
cookies.search = 'google';
if ( cookies.counter )
cookies.counter++;
else
cookies.counter = 1;
console.log( 'Output cookies: ', cookies );
response.writeHead( 200, {
'Set-Cookie': stringifyCookies(cookies),
'Content-Type': 'text/plain'
} );
response.end('Hello World\n');
} ).listen(1234);
I also note that the OP uses the http module.
If the OP was using restify, he can make use of restify-cookies:
var CookieParser = require('restify-cookies');
var Restify = require('restify');
var server = Restify.createServer();
server.use(CookieParser.parse);
server.get('/', function(req, res, next){
var cookies = req.cookies; // Gets read-only cookies from the request
res.setCookie('my-new-cookie', 'Hi There'); // Adds a new cookie to the response
res.send(JSON.stringify(cookies));
});
server.listen(8080);
Let me repeat this part of question that answers here are ignoring:
Can it be done in a few lines of code, without the need to pull in a third party lib?
Reading Cookies
Cookies are read from requests with the Cookie header. They only include a name and value. Because of the way paths work, multiple cookies of the same name can be sent. In NodeJS, all Cookies in as one string as they are sent in the Cookie header. You split them with ;. Once you have a cookie, everything to the left of the equals (if present) is the name, and everything after is the value. Some browsers will accept a cookie with no equal sign and presume the name blank. Whitespaces do not count as part of the cookie. Values can also be wrapped in double quotes ("). Values can also contain =. For example, formula=5+3=8 is a valid cookie.
/**
* #param {string} [cookieString='']
* #return {[string,string][]} String Tuple
*/
function getEntriesFromCookie(cookieString = '') {
return cookieString.split(';').map((pair) => {
const indexOfEquals = pair.indexOf('=');
let name;
let value;
if (indexOfEquals === -1) {
name = '';
value = pair.trim();
} else {
name = pair.substr(0, indexOfEquals).trim();
value = pair.substr(indexOfEquals + 1).trim();
}
const firstQuote = value.indexOf('"');
const lastQuote = value.lastIndexOf('"');
if (firstQuote !== -1 && lastQuote !== -1) {
value = value.substring(firstQuote + 1, lastQuote);
}
return [name, value];
});
}
const cookieEntries = getEntriesFromCookie(request.headers.Cookie);
const object = Object.fromEntries(cookieEntries.slice().reverse());
If you're not expecting duplicated names, then you can convert to an object which makes things easier. Then you can access like object.myCookieName to get the value. If you are expecting duplicates, then you want to do iterate through cookieEntries. Browsers feed cookies in descending priority, so reversing ensures the highest priority cookie appears in the object. (The .slice() is to avoid mutation of the array.)
Settings Cookies
"Writing" cookies is done by using the Set-Cookie header in your response. The response.headers['Set-Cookie'] object is actually an array, so you'll be pushing to it. It accepts a string but has more values than just name and value. The hardest part is writing the string, but this can be done in one line.
/**
* #param {Object} options
* #param {string} [options.name='']
* #param {string} [options.value='']
* #param {Date} [options.expires]
* #param {number} [options.maxAge]
* #param {string} [options.domain]
* #param {string} [options.path]
* #param {boolean} [options.secure]
* #param {boolean} [options.httpOnly]
* #param {'Strict'|'Lax'|'None'} [options.sameSite]
* #return {string}
*/
function createSetCookie(options) {
return (`${options.name || ''}=${options.value || ''}`)
+ (options.expires != null ? `; Expires=${options.expires.toUTCString()}` : '')
+ (options.maxAge != null ? `; Max-Age=${options.maxAge}` : '')
+ (options.domain != null ? `; Domain=${options.domain}` : '')
+ (options.path != null ? `; Path=${options.path}` : '')
+ (options.secure ? '; Secure' : '')
+ (options.httpOnly ? '; HttpOnly' : '')
+ (options.sameSite != null ? `; SameSite=${options.sameSite}` : '');
}
const newCookie = createSetCookie({
name: 'cookieName',
value: 'cookieValue',
path:'/',
});
response.headers['Set-Cookie'].push(newCookie);
Remember you can set multiple cookies, because you can actually set multiple Set-Cookie headers in your request. That's why it's an array.
Note on external libraries:
If you decide to use the express, cookie-parser, or cookie, note they have defaults that are non-standard. Cookies parsed are always URI Decoded (percent-decoded). That means if you use a name or value that has any of the following characters: !#$%&'()*+/:<=>?#[]^`{|} they will be handled differently with those libraries. If you're setting cookies, they are encoded with %{HEX}. And if you're reading a cookie you have to decode them.
For example, while email=name#domain.com is a valid cookie, these libraries will encode it as email=name%40domain.com. Decoding can exhibit issues if you are using the % in your cookie. It'll get mangled. For example, your cookie that was: secretagentlevel=50%007and50%006 becomes secretagentlevel=507and506. That's an edge case, but something to note if switching libraries.
Also, on these libraries, cookies are set with a default path=/ which means they are sent on every url request to the host.
If you want to encode or decode these values yourself, you can use encodeURIComponent or decodeURIComponent, respectively.
References:
Cookie Syntax
Set-Cookie Syntax
Additional information:
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cookie
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie
You can use the "cookies" npm module, which has a comprehensive set of features.
Documentation and examples at:
https://github.com/jed/cookies
To get a cookie splitter to work with cookies that have '=' in the cookie values:
var get_cookies = function(request) {
var cookies = {};
request.headers && request.headers.cookie.split(';').forEach(function(cookie) {
var parts = cookie.match(/(.*?)=(.*)$/)
cookies[ parts[1].trim() ] = (parts[2] || '').trim();
});
return cookies;
};
then to get an individual cookie:
get_cookies(request)['my_cookie']
Cookies are transfered through HTTP-Headers
You'll only have to parse the request-headers and put response-headers.
Here's a neat copy-n-paste patch for managing cookies in node. I'll do this in CoffeeScript, for the beauty.
http = require 'http'
http.IncomingMessage::getCookie = (name) ->
cookies = {}
this.headers.cookie && this.headers.cookie.split(';').forEach (cookie) ->
parts = cookie.split '='
cookies[parts[0].trim()] = (parts[1] || '').trim()
return
return cookies[name] || null
http.IncomingMessage::getCookies = ->
cookies = {}
this.headers.cookie && this.headers.cookie.split(';').forEach (cookie) ->
parts = cookie.split '='
cookies[parts[0].trim()] = (parts[1] || '').trim()
return
return cookies
http.OutgoingMessage::setCookie = (name, value, exdays, domain, path) ->
cookies = this.getHeader 'Set-Cookie'
if typeof cookies isnt 'object'
cookies = []
exdate = new Date()
exdate.setDate(exdate.getDate() + exdays);
cookieText = name+'='+value+';expires='+exdate.toUTCString()+';'
if domain
cookieText += 'domain='+domain+';'
if path
cookieText += 'path='+path+';'
cookies.push cookieText
this.setHeader 'Set-Cookie', cookies
return
Now you'll be able to handle cookies just as you'd expect:
server = http.createServer (request, response) ->
#get individually
cookieValue = request.getCookie 'testCookie'
console.log 'testCookie\'s value is '+cookieValue
#get altogether
allCookies = request.getCookies()
console.log allCookies
#set
response.setCookie 'newCookie', 'cookieValue', 30
response.end 'I luvs da cookies';
return
server.listen 8080
Using Some ES5/6 Sorcery & RegEx Magic
Here is an option to read the cookies and turn them into an object of Key, Value pairs for client side, could also use it server side.
Note: If there is a = in the value, no worries. If there is an = in the key, trouble in paradise.
More Notes: Some may argue readability so break it down as you like.
I Like Notes: Adding an error handler (try catch) wouldn't hurt.
const iLikeCookies = () => {
return Object.fromEntries(document.cookie.split('; ').map(v => v.split(/=(.+)/)));
}
const main = () => {
// Add Test Cookies
document.cookie = `name=Cookie Monster;expires=false;domain=localhost`
document.cookie = `likesCookies=yes=withARandomEquals;expires=false;domain=localhost`;
// Show the Objects
console.log(document.cookie)
console.log('The Object:', iLikeCookies())
// Get a value from key
console.log(`Username: ${iLikeCookies().name}`)
console.log(`Enjoys Cookies: ${iLikeCookies().likesCookies}`)
}
What is going on?
iLikeCookies() will split the cookies by ; (space after ;):
["name=Cookie Monster", "likesCookies=yes=withARandomEquals"]
Then we map that array and split by first occurrence of = using regex capturing parens:
[["name", "Cookie Monster"], ["likesCookies", "yes=withARandomEquals"]]
Then use our friend `Object.fromEntries to make this an object of key, val pairs.
Nooice.
If you don't care what's in the cookie and you just want to use it, try this clean approach using request (a popular node module):
var request = require('request');
var j = request.jar();
var request = request.defaults({jar:j});
request('http://www.google.com', function () {
request('http://images.google.com', function (error, response, body){
// this request will will have the cookie which first request received
// do stuff
});
});
var cookie = 'your_cookie';
var cookie_value;
var i = request.headers.indexOf(cookie+'=');
if (i != -1) {
var eq = i+cookie.length+1;
var end = request.headers.indexOf(';', eq);
cookie_value = request.headers.substring(eq, end == -1 ? undefined : end);
}
I wrote this simple function just pass
req.headers.cookie and cookie name
const getCookieByName =(cookies,name)=>{
const arrOfCookies = cookies.split(' ')
let yourCookie = null
arrOfCookies.forEach(element => {
if(element.includes(name)){
yourCookie = element.replace(name+'=','')
}
});
return yourCookie
}
I know that there are many answer to this question already, but here's a function made in native JS.
function parseCookies(cookieHeader) {
var cookies = {};
cookieHeader
.split(";")
.map(str => str.replace("=", "\u0000")
.split("\u0000"))
.forEach(x => cookies[x[0]] = x[1]);
return cookies;
}
It starts by taking in the document.cookie string. Every key-value pair is separated by a semicolon (;). Therefore the first step is to divide the string up each key-value pair.
After that, the function replaces the first instance of "=" with a random character that isn't in the rest of the string, for this function I decided to use the NULL character (\u0000). The key-value pair can now be split into just two pieces. The two pieces can now be combined into JSON.
You can use cookie lib to parse incoming multiple cookies, so that you won't have to worry about exceptions cases:
var cookies = cookie.parse('foo=bar; equation=E%3Dmc%5E2');
// { foo: 'bar', equation: 'E=mc^2' }
To write a cookie you can do like this:
response.writeHead(200, {
"Set-Cookie": `mycookie=cookie`,
"Content-Type": `text/plain`
});
First one needs to create cookie (I have wrapped token inside cookie as an example) and then set it in response.To use the cookie in following way install cookieParser
app.use(cookieParser());
The browser will have it saved in its 'Resource' tab and will be used for every request thereafter taking the initial URL as base
var token = student.generateToken('authentication');
res.cookie('token', token, {
expires: new Date(Date.now() + 9999999),
httpOnly: false
}).status(200).send();
To get cookie from a request on the server side is easy too.You have to extract the cookie from request by calling 'cookie' property of the request object.
var token = req.cookies.token; // Retrieving Token stored in cookies