Catalog npm
PACKAGE EXPLORERView in registry

4hands-api

npm

4Hands API is an backend framework to build applications.

Versions collected47
Direct dependencies32From release 0.8.5
Last observed20 Oct, 20:09 UTC

README

Current package documentation

4Hands API (v0.8.4 BETA)

This is a API framework to create a backend for your applications.

New Features

  1. Redis service (Manage Redis database)
  2. File upload support via multipart/form-data (AJAX.upload and AJAX.authUpload)
  3. Custom route support for Auth.login() and Auth.register() on the client
  4. bodyValidation middleware now exported from the middleware index

Bugs fixed

  1. Fixed the memory heap issue on larger apps
  2. Fixed session not persisting after login, register, and email confirmation
  3. Fixed signout recreating a new session after destroying the old one
  4. Fixed ghost sessions accumulating in the session store
  5. Fixed document subscription ID not resolving on nested snapshots
  6. Fixed cookieStore unavailable error in environments that do not support the Cookie Store API
  7. Smaller bugs

Instalation

Proceed with the following steps to install the framework.

Requirements

  • Node Version: 20.13.1
  • Database: MongoDB Community (Install MongoDB)
  • Redis: The Redis is used to store sessions and other needs you have on your project. Please follow the link to install the Redis, it's required to create a Server API. (Install Redis)

Installation Steps

With the database running, follow the next steps:

  1. Run the command npm install 4hands-api

Getting Started

Create Server

To create a server you will need to instantiate a ServerAPI class into you project main file, or whenever you need it. Check the example below:

const { ServerAPI } = require('4hands-api');

global.API = new ServerAPI({
    projectName: 'project-name',
    API_SECRET: process.env.API_SECRET,
    redisURL: 'redis://192.168.15.102:6379', // Your Redis host address. Default is 'redis://localhost:6379'
    databaseConfig: {
        HOST: 'mongodb://192.168.15.102:27017/', // Your MongoDB host address. Default is 'mongodb://0.0.0.0:27017/'
        dbName: 'project-name', // Your MongoDB database name.
        collections: [
            require('./src/collections/users'),
            require('./src/collections/master_accounts'),
            require('./src/collections/slot_accounts')
        ]
    },
    // Configuring your server e-mail
    emailConfig: {
        type: 'gmail', // Type of email service ('smtp' or 'gmail'). It's recommended to use environment variables .env for this;
        emailUser: 'testing@gmail.com', // User email for authentication. It's recommended to use environment variables .env for this;
        emailPassword: 'Af09f09sddssd0fssd09fs0df', // Password for authentication. It's recommended to use environment variables .env for this;
    },
    listenCallback: () => {
        // Your callback after the server is connected with success
    }
});

// Examples of endpoints being declared
API.createEndpoint(require('./src/controllers/master-account/create'));
API.createEndpoint(require('./src/controllers/master-account/delete'));

API.createEndpoint(require('./src/controllers/slot-account/create'));
API.createEndpoint(require('./src/controllers/slot-account/delete'));

API.createEndpoint(require('./src/controllers/transfer/depositWithdraw'));

Create Collection

To create new collection on the database, you'll need to create a main collection file, a class file, a event file and a query file. Which follows the same standard of mongoose. If you want to have a better understanding take a look at mongoose documentation.

Main Collection File

Take a look on the example below to see how a Main Collection File should look.

const {Collection} = require('4hands-api');
const Schema = require('4hands-api/src/models/SchemaDB');
const {ObjectId} = Schema.mongoSchema.Types;

module.exports = new Collection({
    displayName: 'My Collection',
    name: 'my_collection',
    pluralLabel: 'My Collections',
    singularLabel: 'My Collection',
    symbol: 'MC',
    fieldsSet: [
        {
            fieldName: 'user',
            type: ObjectId,
            required: true,
            ref: 'users',
            refConfig: {
                relatedField: 'masterAccounts',
                type: 'array-oid'
            }
        },
        {
            fieldName: 'tasks',
            type: [ObjectId],
            required: true,
            ref: 'master_accounts',
            refConfig: {
                relatedField: 'master',
                type: 'ObjectId'
            }
        },
        {
            fieldName: 'status',
            type: String,
            default: 'activated',
            enum: ['running', 'activated', 'disabled', 'paused', 'error', 'closing-position']
        },
        {
            fieldName: 'count',
            type: Number,
            default: 0
        }
    ]
});

Class File

Take a look on the example below to see how a Class File should look.

const MyCollectionModel = require('../../models/MyCollectionModel');

class MyCollectionClass {
    // It's highly recommended that you provide a model for you collection.
    // To standardize and make more stable your application.
    static BSModel = MyCollectionModel;
    
    // Static properties can be added to use on the database doc read. It will be appended to the document
    static testingStatic = 'This is static';

    // Getters also can be added to use on the database doc read. It will be appended to the document
    get testingGetter() {
        return 'This is a getter';
    }

    // To create a method to be appended to the doc.
    async myMethdod() {
        // Your code here
    }
}

module.exports = MyCollecitonClass;

Event File

Take a look on the example below to see how a Event File should look.

ℹ️ This file is not mandatory, at least you need to perform some action in one of the mongoose events.

async function preSave(next){
    // Perfrom actions before the document is saved. This event require the next param be called in order to continue ahead.
    next();
}

async function preFindOne(next) {
    // Perfrom actions after the document is found
    next();
}

async function preUpdate(next) {
    // Perfrom actions after the document is updated
    next();
}

async function postSave() {
    // Perform actions after the document was saved
}
async function postFindOne() {
    // Perfrom actions after the document was found
}

async function postUpdate() {
    // Perfrom actions after the document was updated
}

module.exports = {
    preSave,
    postUpdate,
    postSave
}

Query File

Take a look on the example below to see how a Query File should look. A good example is when you need to populate the document.

ℹ️ This file is not mandatory, at least you need to perform some action in one of the mongoose query.

function defaultPopulate() {
    return this.populate([
        {
            path: 'user',
            model: 'users'
        },
        {
            path: 'transfers',
            model: 'transfers'
        },
        {
            path: 'botAccounts',
            model: 'bot_accounts',
            populate: [
                {
                    path: 'bot',
                    model: 'bots'
                },
                {
                    path: 'trades',
                    model: 'positions',
                    match: {
                        status: 'opened'
                    }
                }
            ]
        }
    ]);
}

module.exports = {
    defaultPopulate
};

Create Endpoint

In order to create an endpoint for your API, you'll have to follow two steps: Create the controller file and loaded it into your server declaration.

Create the controller

In your project create your controller files under src > controllers, you can organized whenever you want, but it's recommanded that your folders structure follows the endpoint URL.

On the example below I created a new controller under src > controllers > transfer > deposit.js. Check the cde example below:

const Endpoint = require('4hands-api/src/models/settings/Endpoint');
const CRUD = require('4hands-api/src/services/database/crud');

module.exports = new Endpoint({
    method: 'PUT',                                    // Choose the method of the endpoint
    routePath: '/transfer/deposit',                   // Path for the endpoint
    isAuthRoute: true,                                // Protect the route for authenticated users
    bodySchema: {                                     // This is the schema for the params be validated
        master: { type: String, required: true },
        type: { type: String, required: true },
        value: { type: Number, required: true }
    },
    controller: async (req, res) => {                 // The controller to handle the endpoint
        try {
            const body = req.body;
            const transfered = await CRUD.create('transfers', {...body, user: req.session?.user?._id});
            const response = transfered.toObject();

            response.success = true;
            return res.status(201).send(response);
        } catch(err) {
            const error = logError(err);
            return res.status(500).send(error);
        }
    }
});

Load the controller into main server file

Got to the main server file on you api, the place where you declared the ServerAPI instance.

const { ServerAPI } = require('4hands-api');

global.API = new ServerAPI({
    // ServerAPI settings properties
});

// ############################################################################
// ADD THE NEW CONTROLLER HERE
API.createEndpoint(require('./src/controllers/transfer/deposit'));
// ############################################################################

User Authentication

The 4hands API has two default endpoints to be used on authentication that is loaded when a ServerAPI is instantiated: /auth/login and /auth/register.

[POST] /auth/register

Params
nametypedescription
emailstring(Required) The email param will be the username of the new user.
passwordstring(Required) Password of account
confirmPasswordstring(Required) Confirmation of password
firstNamestring(Required) The user's first name
lastNamestring(Required) The user's last name
phonestringThe user's phone
Success Response

Returns the new user document. One of the properties on the response is token, this token will be required on the request headers for every endpoint that is auth protected with isAuthRoute set as true, so store it into the cookies to use later.

Custom Route

If your project overrides the default register endpoint you can pass a custom route when calling Auth.register() on the client:

await instance.auth.register(data, '/my-custom/register');

[POST] /auth/login

Params
nametypedescription
emailstring(Required) The email param will be the username of the new user.
passwordstring(Required) Password of account
Success Response

Returns the new user document. One of the properties on the response is token, this token will be required on the request headers for every endpoint that is auth protected with isAuthRoute set as true, so store it into the cookies to use later.

Custom Route

If your project overrides the default login endpoint you can pass a custom route when calling Auth.login() on the client:

await instance.auth.login(email, password, '/my-custom/login');

Sign-up E-mail Confirmation

If you'd like to set the ServerAPI to send a confirmation e-mail and a new user sign-up, you need first to set the emailConfig property of ServerAPI instance. Check below: Check how to set the emailConfig

Validating a confirmation e-mail

To validate the confirmation e-mail link received, you'll have to create a page on your project front-end side with the following path: /dashboard/email-confirmation. This page will receive the confirmationtoken as a query string param on the page's URL. This token needs to be sent to the API endpoint in charge of validate the email and enable the user. Check below the endpoint which should receive the token to validate.

[POST] /auth/confirm-email

Params

nametypedescription
confirmationtokenstring(Required) It's the token received on the e-mail's URL as a search param

Success Response You'll receive back a { success: true } if the e-mail was successfully validated.

Password Recovery

To reset a user's password you'll have to first send a New Password E-mail using the endpoint /auth/reset-password/send-email as a POST request, and then when you received the e-mail with the link you'll send a ajax PUT request to /auth/reset-password/create-new.

[POST] /auth/reset-password/send-email

Params

nametypedescription
emailstring(Required) The user's email account to recover the password.

Success Response You'll receive back a { success: true } if the e-mail was successfully validated.

[PUT] /auth/reset-password/create-new

Params

nametypedescription
newPasswordstring(Required) The user's new password.
confirmPasswordstring(Required) The confirmation for the new password
resettokenstring(Required) The resettoken searchparam received on the e-mail link.
useremailstring(Required) The useremail searchparam received on the e-mail link.

Success Response You'll receive back a { success: true } if the e-mail was successfully validated.

File Upload

The client AJAX service supports uploading files via multipart/form-data. Two methods are available: upload for unauthenticated requests and authUpload for requests that require a valid auth token.

AJAX.upload(endpoint, file, fields, options)

paramtypedescription
endpointstringThe API endpoint path
fileFile | BlobThe file to upload
fieldsobjectAdditional form fields to include (e.g. { bucket: 'avatars' })
optionsobjectOptional request options (headers, onUploadProgress, etc.)
const result = await instance.ajax.upload('/files/upload', file, { bucket: 'avatars' }, {
    onUploadProgress: (event) => console.log(event.loaded, event.total)
});

AJAX.authUpload(endpoint, file, fields, options)

Same as upload but automatically attaches the auth token to the request headers.

const result = await instance.ajax.authUpload('/files/upload', file, { bucket: 'avatars' });

Send E-mails (MailService)

To use e-mails on your ServerAPI instance, you'll need to set the emailConfig property of ServerAPI. The MailService instance will be always available at ServerAPI properties after instantiated, to access e-mail features use the ServerAPI.mailService property. Check below a example of email configuring:

global.API = new ServerAPI({
    // ...
    emailConfig: {
        type: 'gmail', // (Required) Type of email service ('smtp' or 'gmail').
        host: 'smtp.mydomain.com', // Hostname for the SMTP server (for 'smtp' type). It's recommended to use environment variables .env for this;
        smtpPort: 465, // Port number for the SMTP server (for 'smtp' type). Default is 465.
        isSecure: true, // Indicates whether the connection is secure (for 'smtp' type). Default is false.
        emailUser: 'testing@gmail.com', // (Required) User email for authentication. It's recommended to use environment variables .env for this;
        emailPassword: 'Af09f09sddssd0fssd09fs0df' // (Required) Password for authentication. It's recommended to use environment variables .env for this;
    },
    // ...
});

Downloads & popularity

Awaiting observations

No metric observations yet

Download counts and popularity will appear here as the collector receives them.

Versions 47

Collected releases
VersionPublishedDirect dependenciesOpen version
0.8.5Registry latest7 Sept 202632
0.8.45 Aug 202632
0.8.217 Jul 202632
0.8.16 Jul 202632
0.8.016 Jun 202632
0.7.1528 Jun 202532
0.7.1428 Jun 202532
0.7.1327 Jun 202532
0.7.1227 Jun 202532
0.7.119 Feb 202532
0.7.104 Feb 202532
0.7.927 Jan 202532
0.7.811 Dec 202432
0.7.710 Dec 202431
0.7.611 Oct 202431
0.7.518 Sept 202431
0.7.430 Aug 202431
0.7.322 Aug 202431
0.7.221 Aug 202431
0.7.12 Aug 202431

Package advisories

0
No matching advisories collected

This describes our collected data; it is not a security assessment.

Repository

PENDING
feliperamosdev/4hands-api

Shared repository data · GitHub

License detected in repository
Not collected

Metadata history

Meaningful changes
Current collected state Current

Observed since 20 Oct, 20:09 UTC

No superseded metadata states collected. Unchanged observations do not create new revisions.

Record identity

package:6ac906a2cd2e856686ca79d83444970bb6a6543c4c72767d0e9ef226979c2d20