Which is the optimal structure for a REST API? Do you really need to bother about HTTP methods? Do we really need PUT and DELETE?
First of all: do we really need PUT and DELETE?
Most REST API courses will tell you to match CRUD actions with HTTP methods: GET for getting data, POST for creating data, PUT or PATCH for editing data, DELETE for removing data.
The simple answer is no. There is no reason why you, as backend developer, should be limiting the capabilities of your API just to match HTTP methods.
There is no need to force a single HTTP method (such as POST) either. Why would you? Such a choice does not add value and it does not add security to your API.
The optimal solution is the easiest one: just support them all.
const Express = require('express'); const app = new Express(); // API for a library of books app.use('/books/add', (req, res) => res.status(200).json({ message: 'added' }) ); app.use('/books/edit', (req, res) => res.status(200).json({ message: 'edited' }) ); app.use('/books/remove', (req, res) => res.status(200).json({ message: 'removed' }) ); app.use('/books/search', (req, res) => res.status(200).json({ message: 'list results' }) );
It will be up to the frontend developer to choose whether to send a GET, POST, PUT, PATCH or DELETE request to each endpoint, causing less confusion and delivering more value with less code.
Naysayers will note that this breaks the RESTful pattern.
It does not. All actions relevant to books are still grouped under the “books” path and there is a specific request for each action. That’s the only thing that matters.
There are several ways to send data to an express backend:
Normally, you would have to use a different body parser for each one of these formats. For example body-parser
and multer
.
Formidable, on the other hand, is an all-in-one package! Just add it to your project to support all possible configurations:
const Express = require('express'); const formidable = require('express-formidable'); const app = new Express(); app.use(formidable());
Now, when you send data to the backend (whether FormData, urlencoded or JSON), you can access it from the req.fields
object:
const Express = require('express'); const formidable = require('express-formidable'); const app = new Express(); app.use(formidable()); app.use('/login', (req, res) => { const username = req.fields.username; const password = req.fields.password; });
Also, when you send files to the backend (with multipart/form-data), you can access them from the req.files
object:
const Express = require('express'); const formidable = require('express-formidable'); const app = new Express(); app.use(formidable()); app.use('/upload', (req, res) => { const file = req.files.file; // assuming that "file" is the FormData key const sameFileDifferentSyntax = req.files['file']; });
In my opinion, the best way to group routes in ExpressJS is to use index.js
as folder indexes. Let’s see this via example.
Let’s say that we have a books CRUD API and an authors CRUD API. We are going to have the following routes:
/api/v1/routes/books/add
/api/v1/routes/books/edit
/api/v1/routes/books/remove
/api/v1/routes/books/search
/api/v1/routes/authors/add
/api/v1/routes/authors/edit
/api/v1/routes/authors/remove
/api/v1/routes/authors/search
We are then going to organize our routes in the following way:
routes
- folder containing all routesbooks
- folder for book routesindex.js
- index file for this folderadd.js
- add a bookedit.js
- edit a bookremove.js
- remove a booksearch.js
- search through booksauthors
- folder for author routesindex.js
- index file for this folderadd.js
- add an authoredit.js
- edit an authorremove.js
- remove an authorsearch.js
- search through authorsThis is what an index file looks like:
const express = require('express'); const add = require('./add'); const edit = require('./edit'); const remove = require('./remove'); const search = require('./search'); const router = express.Router(); router.use('/add', add); router.use('/edit', edit); router.use('/remove', remove); router.use('/search', search); module.exports = router;
As you can see, the best API structure is one where objects have a clearly recognizable path (as folders on disk too), but
there are no constraints on HTTP methods and data payload format. express-formidable
comes to our rescue with parsing data formats.
You can check out the full example source code here: Github repo link (express-api-structure)
Quick Links
Legal Stuff