Express is still one of the most prominent server side frameworks for node. This little guide will show you how you can build a simple API and connect it with your frontend framework of choice.
If you’re trying to build a site that features a list of items, stores, products or similar on your front page, read on. Examples of this could be sites like producthunt.com or the Google Play Store.
This microproject will be about the following things:
- display data, transmitted through an API
- enable the user to filter and select categories or tags
First we’ll write the backend server, the frontend will follow in the next post.
Requirements
This post doesn’t assume much knowledge of anything really and is aimed at beginners with the slightest of command line skills. The full code will be provided at the bottom of the post.
For the best development experience, I recommend you install nodemon (or similar) with:
npm install -g nodemon
Setting up the Express API Backend
Next, we’ll need to setup our project folder/directory:
cd projects
mkdir store-locator
cd store-locator
mkdir server
cd server
npm init -y
npm install express --save
touch server.js
Alright, now we have a basic folder structer and if we enter the following into server.js
:
const express = require('express');
const app = express();
app.get('/', function(req, res){
res.send('Hello World!')
});
app.listen(3000, function(){
console.log('Example app listening on port 3000!')
});
To test if our server works out alright, we can now start the server:
nodemon server.js
Output:
[nodemon] 1.11.0
[nodemon] to restart at any time, enter `rs`
[nodemon] watching: *.*
[nodemon] starting `node server.js`
Example app listening on port 3000!
and visit http://localhost:3000 in your web browser of choice! Hint: Firefox has become awesome again π
Adding Mock Data to your API
Alright, we got the basics working, but the hello world text will not get our startup far. You need to actually provide valuable content to your users if you want to become the next yellow pages or facebook or store locator for supernatural goods!
In order to satisfy our incoming users without to much of a hassle, let’s pack a couple of example data into a file located in our data folder. In the real world the data obviously would come from your database:
mkdir data
touch data/stores.js
This is the set of example-data I’m going to use for this project:
const stores = [
{
id: 1,
name: 'Hollows End Grim Potions',
imageURL: 'https://images.unsplash.com/photo-1465433360938-e02f97448763?auto=format&fit=crop&w=1267&q=60&ixid=dW5zcGxhc2guY29tOzs7Ozs%3D',
location: 'Hollows End',
nsfw: true
},
{
id: 2,
name: 'Lembas Food Truck',
imageURL: 'https://images.unsplash.com/reserve/DHHQbqc0RrWVf0uDNe5E_The%20Litte%20Cafe.jpg?auto=format&fit=crop&w=1489&q=60&ixid=dW5zcGxhc2guY29tOzs7Ozs%3D',
location: 'Lothlorien',
nsfw: false
},
{
id: 3,
name: 'Galadriels Mirror of Brutal Truth',
imageURL: 'https://images.unsplash.com/photo-1497519098947-a305f214d3bc?auto=format&fit=crop&w=1350&q=60&ixid=dW5zcGxhc2guY29tOzs7Ozs%3D',
location: 'Lothlorien',
nsfw: true
},
{
id: 4,
name: 'Jabbas Light Sabers',
imageURL: 'https://images.unsplash.com/photo-1481241857164-e8483bce922d?auto=format&fit=crop&w=1353&q=60&ixid=dW5zcGxhc2guY29tOzs7Ozs%3D',
location: 'Mos Eisley',
nsfw: true
}
];
module.exports = stores;
Alright, that will do for example data. Only really important about this are:
- id
- location
- nsfw (or other flag)
The directory for the project should now look like this:
.
βββ data
β βββ stores.js
βββ node_modules
βββ package.json
βββ package-lock.json
βββ README.md
βββ server.js
Now these are a bunch of imaginary shops, but how do we actually get them out to our users?
On top of the file, add:
const stores = require('./data/stores.js');
and below the /
route:
app.get('/api/stores', function(req, res){
res.json(stores);
});
Now when you access http://localhost:3000/api/stores you should be able to see the following:
Hint: Yes, the new Firefox does pretty JSON by default.
Now we can provide all of our example data to our users, but how can we enable them to filter it?
Filtering API queries with Express
We will need to parse the queries that our users throw at us, for example with GET
parameters or query parameters. Adjusting the following will do the trick if we want to filter by nsfw
or not nsfw
:
app.get('/api/stores', function(req, res){
var response = [];
if( typeof req.query.nsfw != 'undefined' ){
response = stores.filter(function(store){
if(store.nsfw === true){
return store;
}
});
} else {
response = stores;
}
res.json(response);
});
We can test this by accessing these URLs:
That’s great, but maybe we also want a drop-down for the location, so if we would implement it just like the other if
conditional we would not be able to filter for both, because we overwrite the array response
. We’ll need to implement it a bit differently and also take care of de-duplication. Note that usually your database server would take care of that and we have to write extra lines because we’re using a simple array.
Let’s make use of lodash for the de-duplication to ensure unique IDs on the API response:
npm install --save lodash
and then add on top of the file near the other requires:
const _ = require('lodash');
So that we can adjust our /api/stores
route like this:
app.get('/api/stores', function(req, res){
var response = [];
console.log(req.query)
// this would usually adjust your database query
if(typeof req.query.nsfw != 'undefined'){
stores.filter(function(store){
if(store.nsfw.toString() == req.query.nsfw){
response.push(store);
}
});
}
// this would usually adjust your database query
if(typeof req.query.location != 'undefined'){
stores.filter(function(store){
if(store.location === req.query.location){
response.push(store);
}
});
}
// de-duplication:
response = _.uniqBy(response, 'id');
// in case no filtering has been applied, respond with all stores
if(Object.keys(req.query).length === 0){
response = stores;
}
res.json(response);
});
Note that we’re not taking care of responding with the full list of stores in the bottom if no keys are set on the req.query
object.
Now we can also mix the search or filter by adding multiple query parameters, try accessing http://localhost:3000/api/stores?nsfw=true&location=Lothlorien, which will include all results that either are nsfw
or at the location Lothlorien
. I know it’s actually LothlΓ³rien btw, but that’s beyond the point of this post.
Summary
Now you’ve created a basic API that accepts query parameters to filter items by query parameters with express.js. In the next post we’re going to have a look at how to build a frontend to actually display the stores from different fantasy universes properly.
If you’re looking for a good place to host your backend, check out: Best Cheap VPS Hosting Comparison.
If you have any questions or additions, please throw me a tweet or leave a comment below!
Here is the code that should be semi copy-paste ready:
const _ = require('lodash');
const express = require('express');
const app = express();
const stores = require('./data/stores.js');
// if you have some query parameters that are not vital to the display, just list them here and check for them later
const possibleQueryVars = [
'nsfw',
'location'
];
app.get('/', function(req, res){
res.send('Hello World!')
});
app.get('/api/stores', function(req, res){
var response = [];
console.log(req.query)
// this would usually adjust your database query
if(typeof req.query.nsfw != 'undefined'){
stores.filter(function(store){
if(store.nsfw.toString() == req.query.nsfw){
response.push(store);
}
});
}
// this would usually adjust your database query
if(typeof req.query.location != 'undefined'){
stores.filter(function(store){
if(store.location === req.query.location){
response.push(store);
}
});
}
// de-duplication:
response = _.uniqBy(response, 'id');
// in case no filtering has been applied, respond with all stores
if(Object.keys(req.query).length === 0){
response = stores;
}
res.json(response);
});
app.listen(3000, function(){
console.log('Example app listening on port 3000!')
});
Thank you for reading! If you have any comments, additions or questions, please leave them in the form below! You can also tweet them at me
If you want to read more like this, follow me on feedly or other rss readers
Hi, This is a great tutorial, however when I try to use 2 query strings I get unexpected results.
Please see this stack overflow question. I’m not sure if others are experiencing the same.
https://stackoverflow.com/questions/48248469/multiple-query-values-in-get-request-to-json-based-express-api/48249377#48249377
I was messing around with this a bit and with some StackOverflow help, I think I got it to provide the expected response. Try it out…
=================================
const stores = require(‘./data/stores’);
const boolParser = require(‘express-query-boolean’);
const express = require(‘express’);
const app = express();
// convert query string true / false to boolean
// npm i express-query-boolean
app.use(boolParser());
app.get(‘/’, function (req, res) {
res.send(‘Hello World’)
});
app.get(‘/api/stores’, function (req, res) {
let response = [];
const q = req.query;
if (!Object.keys(q).length) {
response = stores;
} else {
// NO arrow functions here, we are using “THIS”
response = stores.filter(function (store) {
return Object.keys(this).every((key) => store[key] === this[key] )
}, q);
}
res.json(response);
});
app.listen(3000, function () {
console.log(‘Example app listening on port 3000!’)
});
Hey it’s great to learn a bit about these things. I never made an api before and maybe it’s because of this but don’t get the same results as you.
For example after the step of filtering the results with: http://localhost:3000?nsfw=true I only get “Hello World” on my site. I tried your finished project as in the last picture but still it’s not working because then it just shows all data in stores
Hi Daniel,
I just tested this and the path is supposed to be http://localhost:3000/api/stores?nsfw=true
Thanks mate, very helpful
Just one question, I think the URL after adding the nsfw filter should be:
http://localhost:3000/api/stores?nsfw=true
Am I right?
Also, you’ve defined the logic as filter on nsfw whenever nsfw != undefined, so from what I can tell
http://localhost:3000?nsfw=false
will actually return records where nsfw=true
Hi Aaron,
I just tested this and it works on my end, with “true” it shows 4 records, otherwise only 3.
No prob! Happy to help!
Are there any free ways to host the backend? I have a page on GitHub pages which I would like to be able to host this kind of searchable api with query parameters in the url.
Hi Jonathan
I copy pasted the same code but getting error
TypeError: stores.filter is not a function
Did you make sure to create data/stores.js and that it exports an array?
Yes I have data/stores.js in place as you mentioned
Also i am very new into Nodejs, here i am trying to build a Node.js API which helps for text search and wanted to use cosmos DB at backend..
do you have similar type of article which i can refer , any help is appreciated.