Is it possible to specify an integer property as int64 within a Joi model that is utilized by Hapi Swagger?

I'm currently working with a Joi model that looks like this:

const SimpleModel = Joi.object({
    id: Joi.number().integer().required().description('ID')
}).label('SimpleModel');

This model is being utilized in the following route within @hapi/hapi:

{
    method: 'GET',
    path: `/api/v1.0/data`,
    handler: () => { id: 1 }, // a mock handler
    config: {
        plugins: {
            'hapi-swagger': {
                responses: {
                    200: {
                        description: 'OK',
                        schema: SimpleModel
                    }
                }
            }
        }
    }
}

The swagger definition for the above model is generated as shown below:

swagger: '2.0'
host: localhost:8080
basePath: /api
info: 
  title: 'Swagger for SimpleModel'
  version: '1.0'
schemes:
  - http
  - https
paths:
  /v1.0/data:
    get:
      summary: Returns an object
      responses:
        '200':
          schema:
            $ref: '#/definitions/SimpleModel'
          description: OK
definitions:
  SimpleModel:
    type: object
    properties:
      id:
        type: integer
        description: ID
    required:
      - id

My goal is to introduce an additional field to the id, specifically format: int64. For example:

definitions:
  SimpleModel:
    type: object
    properties:
      id:
        type: integer
        format: int64 # <-- new field here!
        description: ID
    required:
      - id

Although this can be done in swagger, I am struggling to define it in my Joi model so that it reflects in the generated swagger by hapi-swagger.

I've been conducting online research for days, but unfortunately, haven't come across any useful resources or examples. Is there a way to include format: int64 in the swagger output of SimpleModel?

Answer №1

package.json

{
  "name": "71422008",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "dependencies": {
    "@hapi/hapi": "^20.2.1",
    "@hapi/inert": "^6.0.5",
    "@hapi/vision": "^6.1.0",
    "blipp": "^4.0.2",
    "hapi-swagger": "^14.2.5",
    "joi": "^17.6.0",
    "qs": "^6.10.3"
  }
}

index.js

const Hapi = require('@hapi/hapi');
const Joi = require('joi');
const Blipp = require('blipp');
const Inert = require('@hapi/inert');
const Vision = require('@hapi/vision');
const HapiSwagger = require('hapi-swagger');
const HapiSwaggerProperties = require('hapi-swagger/lib/properties');

const _parseNumber = HapiSwaggerProperties.prototype.parseNumber;
HapiSwaggerProperties.prototype.parseNumber = function(property, joiObj) {
  let _property = _parseNumber.apply(this, [property, joiObj]);
  _property.format = 'int64';
  return _property;
}
const SimpleModel = Joi.object({
    id: Joi.number().integer().required().description('ID')
}).label('SimpleModel');

const ser = async () => {
  const swaggerOptions = {
    info: {
      title: 'Test API Documentation',
      description: 'This is a sample example of API documentation.'
    }
  };

  const server = Hapi.Server({
    host: 'localhost',
    port: 3000
  });

  await server.register([
    Inert,
    Vision,
    Blipp,
    {
      plugin: HapiSwagger,
      options: swaggerOptions
    }
  ]);

  server.route({
    method: 'GET',
    path: `/api/v1.0/data`,
    options: {
      handler: () => { id: 1 }, // a mock handler
      tags: ['api'],
      plugins: {
        'hapi-swagger': {
          responses: {
            200: {
              description: 'OK',
              schema: SimpleModel
            }
          }
        }
      }
    },
  });

  await server.start();
  return server;
};

ser()
  .then((server) => {
    console.log(`Server listening on ${server.info.uri}`);
  })
  .catch((err) => {
    console.error(err);
    process.exit(1);
  });

Output:

...
    "definitions": {
        "SimpleModel": {
            "type": "object",
            "properties": {
                "id": {
                    "type": "integer",
                    "description": "ID",
                    "format": "int64"
                }
            },
            "required": [
                "id"
            ]
        }
    }
...

Example 2 with custom types:

const Hapi = require('@hapi/hapi');
const Joi = require('joi');
const Blipp = require('blipp');
const Inert = require('@hapi/inert');
const Vision = require('@hapi/vision');
const HapiSwagger = require('hapi-swagger');
const HapiSwaggerProperties = require('hapi-swagger/lib/properties');

const custom = Joi.extend(joi => ({
  type: 'number',
  base: Joi.number().$_setFlag('type', 'integer').$_setFlag('format', 'int64'),
  rules: {
    i32: {
      method(first, second) {
        return this.$_setFlag('type', 'integer').$_setFlag('format', 'int32');
      }
    }
  }
}));

const _parseNumber = HapiSwaggerProperties.prototype.parseNumber;
HapiSwaggerProperties.prototype.parseNumber = function(property, joiObj) {
  let _property = _parseNumber.apply(this, [property, joiObj]);

  const describe = joiObj.describe();
  if (describe.flags) {
    if (describe.flags.format) {
      _property.format = describe.flags.format;
    }

    if (describe.flags.type) {
      _property.type = describe.flags.type;
    }
  }
  return _property;
}

const SimpleModel = Joi.object({
    id: Joi.number().integer().required().description('ID'),
    seconds: custom.number().i32(),
    epochSeconds: custom.number(),
}).label('SimpleModel');

const ser = async () => {
  const swaggerOptions = {
    info: {
      title: 'Test API Documentation',
      description: 'This is a sample example of API documentation.'
    }
  };

  const server = Hapi.Server({
    host: 'localhost',
    port: 3000
  });

  await server.register([
    Inert,
    Vision,
    Blipp,
    {
      plugin: HapiSwagger,
      options: swaggerOptions
    }
  ]);

  server.route({
    method: 'GET',
    path: `/api/v1.0/data`,
    options: {
      handler: () => { id: 1 }, // a mock handler
      tags: ['api'],
      plugins: {
        'hapi-swagger': {
          responses: {
            200: {
              description: 'OK',
              schema: SimpleModel
            }
          }
        }
      }
    },
  });

  await server.start();
  return server;
};

ser()
  .then((server) => {
    console.log(`Server listening on ${server.info.uri}`);
  })
  .catch((err) => {
    console.error(err);
    process.exit(1);
  });

Output:

...
    "definitions": {
        "SimpleModel": {
            "type": "object",
            "properties": {
                "id": {
                    "type": "integer",
                    "description": "ID"
                },
                "seconds": {
                    "type": "integer",
                    "format": "int32"
                },
                "epochSeconds": {
                    "type": "integer",
                    "format": "int64"
                }
            },
            "required": [
                "id"
            ]
        }
    }
...

Example 3 with custom types and meta properties:

const Hapi = require('@hapi/hapi');
const Hoek = require('@hapi/hoek');
const Joi = require('joi');
const Blipp = require('blipp');
const Inert = require('@hapi/inert');
const Vision = require('@hapi/vision');
const HapiSwagger = require('hapi-swagger');
const HapiSwaggerProperties = require('hapi-swagger/lib/properties');

const custom = Joi.extend(joi => ({
  type: 'int',
  base: Joi.number().meta({
    'type': 'integer',
    'format': 'int32',
  }),
  rules: {
    i64: {
      method(first, second) {
        return this.meta({
          'type': 'integer',
          'format': 'int64',
        });
      }
    }
  }
}));

const _parsePropertyMetadata = HapiSwaggerProperties.prototype.parsePropertyMetadata;
HapiSwaggerProperties.prototype.parsePropertyMetadata = function(property, name, parent, joiObj) {
  let _property = _parsePropertyMetadata.apply(this, [property, name, parent, joiObj]);

  const _type = Hoek.reach(joiObj, 'type');
  if (!this.propertyMap[_type]) {
    delete _property['x-meta']
  }

  let xMeta = Hoek.reach(joiObj, '$_terms.metas');
  xMeta = xMeta.length > 0 ? xMeta[xMeta.length - 1] : undefined;
  if (xMeta) {
    if (xMeta.type) {
      _property.type = xMeta.type;
    }

    if (xMeta.format) {
      _property.format = xMeta.format;
    }
  }

  return _property;
}

const SimpleModel = Joi.object({
    id: Joi.number().integer().required().description('ID'),
    seconds: custom.int(),
    epochSeconds: custom.int().i64(),
}).label('SimpleModel');

const ser = async () => {
  const swaggerOptions = {
    info: {
      title: 'Test API Documentation',
      description: 'This is a sample example of API documentation.'
    }
  };

  const server = Hapi.Server({
    host: 'localhost',
    port: 3000
  });

  await server.register([
    Inert,
    Vision,
    Blipp,
    {
      plugin: HapiSwagger,
      options: swaggerOptions
    }
  ]);

  server.route({
    method: 'GET',
    path: `/api/v1.0/data`,
    options: {
      handler: () => { id: 1 }, // a mock handler
      tags: ['api'],
      plugins: {
        'hapi-swagger': {
          responses: {
            200: {
              description: 'OK',
              schema: SimpleModel
            }
          }
        }
      }
    },
  });

  await server.start();
  return server;
};

ser()
  .then((server) => {
    console.log(`Server listening on ${server.info.uri}`);
  })
  .catch((err) => {
    console.error(err);
    process.exit(1);
  });
...
    "definitions": {
        "SimpleModel": {
            "type": "object",
            "properties": {
                "id": {
                    "type": "integer",
                    "description": "ID"
                },
                "seconds": {
                    "type": "integer",
                    "format": "int32"
                },
                "epochSeconds": {
                    "type": "integer",
                    "format": "int64"
                }
            },
            "required": [
                "id"
            ]
        }
    }
...

Answer №2

After receiving valuable insights from comments and an informative answer, I successfully made a pull request to enhance the functionality of the hapi-swagger repository.

Starting from version v14.3.0, implementing the following Joi model definition will yield the desired outcome:

const SimpleModel = Joi.object({
    id: Joi.number().integer().required().description('ID').meta({ format: 'int64' })
}).label('SimpleModel');
definitions:
  SimpleModel:
    type: object
    properties:
      id:
        type: integer
        format: int64
        description: ID
    required:
      - id

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Numerous invocations of express middleware functions

Currently, I am in the process of developing an app with express and have created a function to set a language cookie. [..] app.use(cookieParser()); app.use(function(req, res, next) { if(req.cookies.lang === undefined){ console.log(req.cookies); ...

Here is a way to prevent a null input value from being accepted in a JavaScript function

Hey there, I have a function that looks like this: class Fun { pem_Files_Checker_And_Adder_Server_Id_Adder(id , serverType , hostname) { //do something }; }; } In order for this function to work properly, I need to give it some values. For exam ...

Is it feasible to combine CoffeeScript, Node.js, MongoDB, and JasperReports?

Recently, I came across a JSF (web JavaScript application) that interacts with a MongoDB database and creates a report in PDF format using the JasperReports Library through a .java file. I am curious if it is feasible to achieve similar functionality with ...

Commands for Yarn, such as type checking, testing, and formatting with Prettier, are encountering

I have been encountering errors in my Jenkins builds for the past 3 weeks. Despite numerous attempts, I cannot seem to get these commands to work within the Jenkins pipeline code using our shared libraries. The same commands work fine locally. The errors ...

What are some potential problems that could arise when making a POST request for signing authentication in a MERN stack using JWT?

I'm currently in the process of developing a social media application using the MERN stack. To ensure the functionality of the backend API, I am utilizing POSTMAN. Here is an overview of the dependencies outlined in the package.json file: { "na ...

What is the code to retrieve all _id values within an array in MongoDB?

I currently possess the following assortment: { "_id" : ObjectId("5acdb95d5ea63a27c1facf92"), "venue" : ObjectId("5acdb95d5ea63a27c1facf8c"), "author" : ObjectId("5ac8ba3582c2345af70d4658"), } { "_id" : ObjectId("5acdb95d5ea63a27c1facf93") ...

Make sure npm installs identical dependencies across different machines

My current issue involves a packages.json file where I'm installing node modules using npm install from the same directory. However, the problem arises when different machines already have some dependencies installed globally. Although this situation ...

The expiration time of the JWT signed token remains unchanged in the browser application even after being updated in the code

Here is the code snippet: const token = jwt.sign({ _id: user._id }, process.env.JWT_SECRET, { expiresIn: '1d' }); res.cookie('token', token, { expiresIn: '1d' }); Initially, everything was functioning properly with t ...

utilizing PM2 on a Windows server with a traditional Azure pipeline configuration

I am currently working on setting up a release pipeline using classic Azure pipelines for my Next.js project deployment on a self-hosted Windows server. I have configured IIS as a redirection server to redirect traffic to the Next.js application. However, ...

What are the best methods for profiling a Node.js application at a specific point in its execution?

I am currently facing performance issues with my Node application, which listens to a websocket data feed and communicates with another API. The CPU usage is normally stable at around 2-5%, but occasionally (approximately 3 times within 24 hours) the incom ...

Encountering issues while trying to npm install or execute the electron-quick-start application

Currently, I am running Node version 7.4.0 and NPM version 4.0.5 (Windows) as recommended on Electron's website. The issue I'm facing arises when running the npm install command within the cloned electron-quick-start directory. I've experim ...

Execute a command post-deployment on AWS Beanstalk

Encountering an issue with executing a command after deployment. I have a node.js project with a script that relies on binaries from node_modules. When I write the command for the script in .ebextensions/.config file, it executes before npm install causi ...

Failure to install Playwright browsers in the continuous integration environment following the upgrade to a more recent version

, we are currently facing the following situation: For our e2e Playwright tests that rely on JS/TS, two crucial steps in our CI pipeline include: - Running the script: npm ci Displayed as 'npm ci' - Running the script: sudo npx playwright ins ...

What is causing the issue with fetching data from MongoDB in my basic web application?

Recently, I have been trying to integrate MongoDB into my Express Node.js web application. Being fairly new to Node.js, I decided to follow a tutorial video [link to video] for guidance. Unfortunately, I encountered some difficulties while setting up the M ...

Firebase is not updating the number

Having just started with Firebase, I have been following all the guidelines for web Firebase auth. I have successfully managed to login, log out, and update all data except for the phone number. Despite receiving a success message in the console, my phone ...

Can a single project update just one node?

Currently, I am facing a challenge with a large project that uses an outdated version of Node.js which is globally installed on my computer. I intend to kick off a new project using npx create-react-app, but this requires me to upgrade Node.js. I am aware ...

Retrieve data from a form on the server side using an Ajax request

I am currently working on submitting form data through an Ajax call. Here is the relevant form code: <form target="_blank" id="addCaseForm" class="form-horizontal col-md-12" action="" method="POST> <div class="form-group"> <labe ...

Deciphering an HTTP request in a Node.js script

Currently, I am endeavoring to concoct a script that navigates to a script on localhost and interprets it within a node.js script. Take a glance at my progress thus far: var http = require('http'); var options = { host:'127.0.0.1' ...

Real-time chat functionality using Laravel 5.2, socket.io, and private messaging with redis

TLDR; Seeking best solution for private instant messenger integration with Laravel. I currently have a live real-time chat feature on my Laravel website, but I am missing the ability for private messaging. The chat is located on the home page and every ti ...

Is Jade monitoring *.jade files?

Though I am not sure of the internal workings of Jade, my best guess is that it compiles each template file once and then employs a compiled and cached version for subsequent HTTP requests. One intriguing observation I have made while running my Express a ...