So, we got to know what is meant by Restful Api
concept, now let's start implementing it in our project.
Workflow
Before we start implementing the Restful Api
concept, we need to know the workflow of the project, so we can understand how to implement it.
Our first step is to create a route.resource
method, this method will be used to generate all the routes for a specific resource, so we need to pass the resource name to it.
This method is supposed to generate the following routes:
-
GET /resource
- Get all resources -
GET /resource/:id
- Get a specific resource -
POST /resource
- Create a new resource -
PUT /resource/:id
- Update a specific resource -
DELETE /resource/:id
- Delete a specific resource -
PATCH /resource/:id
- Update a specific resource
The second argument is resource object, that object should have certain methods, each method represents a handler for a specific route, so we need to pass the handler for each route to the resource object.
For example:
const resource = {
list: listUsers,
get: getSingleUser,
create: createUser,
update: updateUser,
delete: deleteUser,
patch: patchUser,
};
So based on what the resource contains, we'll add the routes accordingly.
The other good feature is that we can also pass route options to be used with these routes, as we do with single route call.
We'll also use route name feature to name the routes, so we can use them in the future.
Enough talking now let's move to the code.
Implementation
First off, let's define our Resource Type
.
// src/core/router/types.ts
/** Route resource */
export type RouteResource = {
/**
* list route
*/
list?: RouteHandler;
/**
* Single resource route
*/
get?: RouteHandler;
/**
* Create resource route
*/
create?: RouteHandler;
/**
* Update resource route
*/
update?: RouteHandler;
/**
* Patch resource route
*/
patch?: RouteHandler;
/**
* Delete resource route
*/
delete?: RouteHandler;
};
So we just defined a type that is used to define the resource object, it contains all the methods that will be used to handle the routes, the handler is the same as the single route handler, so nothing new here.
Resource Method
Now let's define our resource
method, this method will receive three arguments, the first one is the resource path, the second one is the resource object, and the third one is the route options.
We need also to set the resource name if it is not passed in the options, let's see in action.
// src/core/router/index.ts
import { ltrim, merge, toCamelCase } from "@mongez/reinforcements";
// ...
/**
* Add full restful resource routes
* This method will generate the following routes:
* 1. GET /path: list all resources
* 2. GET /path/:id: get a single resource
* 3. POST /path: create a new resource
* 4. PUT /path/:id: update a resource
* 5. DELETE /path/:id: delete a resource
* 6. PATCH /path/:id: update a resource partially
*/
public resource(
path: string,
resource: RouteResource,
options: RouteOptions = {},
) {
// get base resource name
const baseResourceName = options.name || toCamelCase(ltrim(path, "/"));
if (resource.list) {
const resourceName = baseResourceName + ".list";
this.get(path, resource.list, { ...options, name: resourceName });
}
if (resource.get) {
const resourceName = baseResourceName + ".get";
this.get(path + "/:id", resource.get, { ...options, name: resourceName });
}
if (resource.create) {
const resourceName = baseResourceName + ".create";
this.post(path, resource.create, { ...options, name: resourceName });
}
if (resource.update) {
const resourceName = baseResourceName + ".update";
this.put(path + "/:id", resource.update, {
...options,
name: resourceName,
});
}
if (resource.patch) {
const resourceName = baseResourceName + ".patch";
this.patch(path + "/:id", resource.patch, {
...options,
name: resourceName,
});
}
if (resource.delete) {
const resourceName = baseResourceName + ".delete";
this.delete(path + "/:id", resource.delete, {
...options,
name: resourceName,
});
}
return this;
}
Nothing hard here, firstly i got the baseResourceName
, you can pass it in the options list, otherwise it will be taken from the path.
const baseResourceName = options.name || toCamelCase(ltrim(path, "/"));
toCamelCase is used to transform the path to camel case, for example users-groups
will be transformed to usersGroups
.
And i used ltrim to remove the /
from the path, so if the path is /users
it will be users
.
Then we checked for each route type, we started with list
, if it exists we'll add the route, we used the baseResourceName
and added .list
to it, so the route name will be users.list
for example, then we added it as a route to our router system.
And the same applies for the rest of the routes.
Usage
Now let's see how we can use it.
// src/app/users/routes.ts
import router from "core/router";
import login from "./controllers/auth/login";
import listUsers from './controllers/users/list';
import getUser from './controllers/users/get';
import createUser from './controllers/users/create';
import updateUser from './controllers/users/update';
import patchUser from './controllers/users/patch';
const restfulUser = {
list: listUsers,
get: getUser,
create: createUser,
update: updateUser,
patch: patchUser,
};
router.resource("/users", restfulUser);
router.post("/login", login);
And that's it!, now you can use the resource routes in your application.
🎨 Conclusion
In this article, we learned how to create a resource route, and how to use it in our application.
In our next article, we'll see how to create our first Restful Class, that manages everything within a one place!, stay tuned.
☕♨️ Buy me a Coffee ♨️☕
If you enjoy my articles and see it useful to you, you may buy me a coffee, it will help me to keep going and keep creating more content.
🚀 Project Repository
You can find the latest updates of this project on Github
😍 Join our community
Join our community on Discord to get help and support (Node Js 2023 Channel).
🎞️ Video Course (Arabic Voice)
If you want to learn this course in video format, you can find it on Youtube, the course is in Arabic language.
📚 Bonus Content 📚
You may have a look at these articles, it will definitely boost your knowledge and productivity.
General Topics
- Event Driven Architecture: A Practical Guide in Javascript
- Best Practices For Case Styles: Camel, Pascal, Snake, and Kebab Case In Node And Javascript
- After 6 years of practicing MongoDB, Here are my thoughts on MongoDB vs MySQL
Packages & Libraries
- Collections: Your ultimate Javascript Arrays Manager
- Supportive Is: an elegant utility to check types of values in JavaScript
- Localization: An agnostic i18n package to manage localization in your project
React Js Packages
Courses (Articles)
Top comments (0)