Skip to content
Skitsanos edited this page Jan 22, 2023 · 2 revisions

A Foxx Microservice endpoint is a specific URL or path that a client can make a request to in order to access a specific functionality or resource provided by a Foxx Microservice. This endpoint is defined in the route handler of the Foxx Microservice and is associated with a specific HTTP method (e.g. GET, POST, etc.). When a client makes a request to a Foxx Microservice endpoint, the associated route handler is executed and the response is returned to the client. This allows for creating custom, modular functionality that can be easily added to an ArangoDB server by creating and registering new Foxx Microservices.

Foxx Builder uses a similar approach to routing and mapping as popular frameworks like Umijs and Next.js. Like in those frameworks, the user puts their files into a 'routes' folder in Foxx Builder. The parsePath method in Foxx Builder is responsible for mapping these files to their corresponding endpoint handlers. It recursively scans the 'routes' folder, and for each file it finds, it uses the file name to determine the HTTP method (e.g. 'get.js' is mapped to the 'get' method) and the folder structure to determine the URL or path (e.g. '/routes/users/get.js' is mapped to the '/users' endpoint). This allows developers familiar with frameworks like Umijs and Next.js to easily understand and work with the routing and mapping in Foxx Builder. So, if you are coming from Umijs or Next.js, you can think of the 'routes' folder as the equivalent of the 'pages' folder in those frameworks.

Example of the router

This is an example of a Foxx microservice route responsible for user authentication. The route exports an object with several properties that define the behavior of the route. The contentType property specifies that the route will return JSON data. The name property gives the route a name for reference.

The body property defines a validation schema for the request body using the joi library. In this example, the schema requires a JSON object with two properties: username and password, both of which are required strings. This ensures that the request body has the correct format and data types.

The handler property is a function that will be executed when a request is made to this route. The function takes in the request and response objects as arguments. The code inside the function first destructures the request body to get the username and password values. Then, it uses ArangoDB's AQL query language to check if a user is in the 'users' collection with the provided username and password. If the query returns no results, the function throws a 403 error, indicating that the request is not authorized. If a user is found, the function updates the user's last login time and creates a session token using the auth.encode method. The token is included in the response, along with the user's information.

This example demonstrates how Foxx microservices can handle authentication securely and efficiently by validating the request body, and querying the database. The use of the crypto module to hash the password and create the gravatar link is added security measure.

const joi = require('joi');
const {query} = require('@arangodb');
const crypto = require('@arangodb/crypto');

module.exports = {
    contentType: 'application/json',
    name: 'Login',

    body: {
        model: joi.object({
            username: joi.string().required(),
            password: joi.string().required()
        }).required()
    },

    handler: (req, res) =>
    {
        const {utils, update, auth} = module.context;

        const {username, password} = req.body;

        const [queryResult] = query`
            for doc in users
            filter 
                doc.username == ${username}
                &&
                doc.password == ${crypto.sha384(password)}
            RETURN unset(doc, "_id", "_rev", "password")`
        .toArray();

        if (!Boolean(queryResult))
        {
            res.throw(403, 'Not Authorized');
        }

        //update lastLogin
        update('users', queryResult._key, {lastLogin: new Date().getTime()});

        const doc = {
            user: {
                ...queryResult
            },
            session: {
                token: auth.encode({
                    userId: queryResult._key
                })
            }
        };

        if (utils.isEmail(username))
        {
            doc.user.gravatar = `https://www.gravatar.com/avatar/${crypto.md5(username)}?d=robohash&s=150`;
        }

        res.send({result: doc});
    }
};
Clone this wiki locally