
Security News
AGENTS.md Gains Traction as an Open Format for AI Coding Agents
AGENTS.md is a fast-growing open format giving AI coding agents a shared, predictable way to understand project setup, style, and workflows.
just-another-http-api
Advanced tools
A framework built on top of fastify aimed at removing the need for any network or server configuration.
This module provides a comprehensive but extremely simple solution for creating HTTP servers with support for features like caching, authentication, file uploads, and more. It leverages Fastify for high performance and includes integrations with Redis for caching and AWS S3 for file uploads. This lightweight HTTP API is used by businesses receiving over 100,000 requests every minute but can also been seen as a quick solution for those devs who want to get something up and running quickly! Unique for its simplicity and efficiency, it's designed to streamline the process of setting up an HTTP server.
To use this module, you need to have Node v18+ and npm installed. You can then include it in your project by cloning the repository or copying the module file.
domain.com/endpoint
./routes/endpoint/index.js
exports.get
in ./routes/endpoint/index.js
would handle HTTP GET requests to domain.com/endpoint
exports.config
const server = await justAnotherHttpApi ( globalConfig )
npm init
npm i just-another-http-api
index.js
const justAnotherHttpApi = require ( 'just-another-http-api' );
const globalConfig = {
name: 'My Server',
port: 4500
};
( async () => {
const server = await justAnotherHttpApi( globalConfig );
console.log ( 'Server Ready' );
} ) ();
/routes/myendpoint/index.js
/routes/myendpoint/index.js
const response = require ( 'just-another-http-api/utils/response' );
exports.get = async req => {
return response ( { json: { hello: 'world' } } );
};
node index.js
http://localhost:4500/myendpoint
and see your response.The HTTP API module requires a configuration object to initialize. Here's a breakdown of the configuration options:
name
: Name of the server.cache
: Caching configuration.
defaultExpiry
: Default cache expiry time in seconds.enabled
: Enable or disable caching.addCacheHeaders
: Add cache-related headers to the response.redisClient
: Redis client instance.redisPrefix
: Prefix for Redis cache keys.auth
: Authentication configuration.
requiresAuth
: Global flag to require authentication.type
: Type of authentication (currently only JWT).jwtSecret
: Secret key for JWT.jwtLoginHandle
: Function to handle login logic.jwtExpiresIn
: JWT expiry time in seconds.jwtEnabledRefreshTokens
: Enable or disable refresh tokens.jwtStoreRefreshToken
: Function to store refresh tokens.jwtRetrieveRefreshToken
: Function to retrieve refresh tokens.jwtRefreshExpiresIn
: Expiry time for refresh tokens.docRoot
: Directory containing route definitions.port
: Port number for the server.logs
: Enable or disable logging. Accepts a function or false.uploads
: File upload configuration.
enabled
: Enable or disable file uploads.storageType
: Type of storage ('s3', 'memory', or 'filesystem').localUploadDirectory
: Directory for local file uploads.s3Client
: AWS S3 client instance.s3UploadDirectory
: S3 directory for file uploads.s3UploadBucket
: S3 bucket for file uploads.cors
: CORS configuration.middleware
: Array of additional middleware functions.Here's an example of how to set up the global configuration:
const getConfig = async () => {
// Initialize Redis and S3 clients, and define authentication functions
...
return {
name: 'Server Name',
cache: {...}, // See Caching section for details
auth: {...}, // See Authentication section for details
docRoot: './routes', // This is where you store your endpoint handlers
port: 4500,
logs: false, // Accepts function or false
uploads: {...}, // See Uploads section for details
cors: {
allowedHeaders: [
'accept',
'accept-version',
'content-type',
'request-id',
'origin',
],
exposedHeaders: [
'accept',
'accept-version',
'content-type',
'request-id',
'origin',
'x-cache',
'x-cache-age',
'x-cache-expires',
],
origin: '*',
methods: 'GET,PUT,POST,DELETE,OPTIONS',
optionsSuccessStatus: 204
},
middleware: [] // Currently not implemented
};
};
Here's an example of how to set up the endpoint handler configuration:
// ./routes/endpoint/index.js
exports.config = {
get: {
cache: true,
expires: 50, //seconds
requiresAuth: false
},
post: {
upload: {
enabled: true,
storageType: 'filesystem', // memory, filesystem, s3 (overrides the global config)
requestFileKey: 'data', // defaults to "files"
maxFileSize: 1000 * 1000 * 1000 * 1000 * 2, // 2GB
maxFiles: 1,
s3ACL: 'public-read',
subDirectory: 'test'
},
cache: false
}
};
// endpoint method handlers
exports.get = async ( req ) => { ... }
exports.post = async ( req ) => { ... }
// ...etc
The upload functionality in the API allows for file uploads with various storage options including in-memory, filesystem, and Amazon S3. The configuration is flexible and can be set globally or overridden on a per-endpoint basis.
Uploads are configured through the exports.config
object. Here's an example of how you can configure uploads for a particular endpoint handler:
exports.config = {
post: {
upload: {
enabled: true,
storageType: 's3', // Options: 'memory', 'filesystem', 's3'
requestFileKey: 'data', // Field name in the multipart form, defaults to 'files'
maxFileSize: 1000 * 1000 * 1000 * 1000 * 2, // Maximum file size, here set to 2GB
maxFiles: 5, // Maximum number of files
s3ACL: 'public-read', // S3 Access Control List setting
subDirectory: 'test' // Optional subdirectory for file storage in S3
},
cache: false
}
};
localUploadDirectory
in the global configuration.The upload handler middleware is automatically invoked for endpoints configured with upload functionality. It handles the file upload process based on the specified configuration, including the management of storage and any necessary cleanup in case of errors.
The upload middleware provides comprehensive error handling to cover various scenarios such as file size limits, unsupported file types, and storage issues. Users will receive clear error messages guiding them to resolve any issues that may arise during the file upload process.
After configuring the upload settings, you can access the uploaded files in your request handler. The uploaded file data will be available in the request (req
) object. Depending on the storage type and the maxFiles
setting, the structure of the uploaded file data may vary.
When using S3 storage, the uploaded files are available in the req.files
array. Each file in the array is an object containing metadata and the path of the uploaded file in the S3 bucket. For example:
[
{
"fieldname": "<input field name>",
"originalname": "<original file name>",
"encoding": "7bit",
"mimetype": "<mime type>",
"path": "<S3 bucket path>"
},
...
]
If maxFiles
is set to 1, the file information will be available as a single object in req.file
.
When using memory storage, the file's content is stored in memory. The file data can be accessed through req.file
or req.files
depending on maxFiles
. An example structure is:
{
"fieldname": "<input field name>",
"originalname": "<original file name>",
"encoding": "7bit",
"mimetype": "<mime type>",
"buffer": "<file data buffer>",
"size": <file size in bytes>
}
For filesystem storage, the file is saved to the specified directory on the server. The file information, including the path to the saved file, can be accessed in a similar way:
{
"fieldname": "<input field name>",
"originalname": "<original file name>",
"encoding": "7bit",
"mimetype": "<mime type>",
"destination": "<file save directory>",
"filename": "<generated file name>",
"path": "<full file path>",
"size": <file size in bytes>
}
The API module provides a robust caching system to enhance performance and reduce load on the server. This system caches responses based on the request URL and query parameters.
Caching can be configured in the handlerConfig for each endpoint. Here is an example configuration:
exports.config = {
get: {
cache: true,
expires: 50, // seconds
},
post: {
cache: false
}
};
In this configuration:
When a request is made, the cache middleware checks if a valid, non-expired cache entry exists. If a valid cache exists, the response is served from the cache, bypassing the handler. If no valid cache is found, the request proceeds to the handler, and the response is cached for future use.
The provided example below assumes your docRoot is ./routes
. The following file would be ./routes/example/index.js
Below would provide you with the following endpoints:
http://localhost:4500/example
(cached)http://localhost:4500/example
(not cached)const response = require ( 'just-another-http-api/utils/response' );
exports.config = {
get: {
cache: true,
expires: 50, //seconds
},
post: {
cache: false
}
};
exports.get = async req => {
return response ( { html: '<p>hello world</p>' } );
};
exports.post = async req => {
return req.body ;
};
The caching system handles the caching and retrieval of responses automatically, based on the provided configuration. Responses that are cached do not execute any code in your endpoint method handler.
The API module utilizes specific headers to convey caching information to clients. These headers are included in responses to indicate whether the data was served from the cache or generated afresh, as well as to provide information about the age and expiry of the cache data.
Headers for Cache Miss (Data not served from cache) When the response data is not served from the cache (Cache Miss), the following headers are added:
When the response data is served from the cache (Cache Hit), the following headers are included:
X-Cache-Age
reaches this number is will no longer be cached.These headers provide valuable insights into the caching status of the response, helping clients to understand the freshness and validity of the data they receive.
The API module supports JWT (JSON Web Token) based authentication. This provides a secure way to handle user authentication and authorization throughout the API.
Authentication is configurable through the auth
object in the global configuration. Here are the key options and their descriptions:
false
./auth/login
(Default)/auth/refresh
(Default)false
on failure.3600
for 1 hour.604800
for 1 week.The authentication logic is integrated with the Fastify server instance. The auth
plugin is responsible for setting up the necessary routes and middleware for handling JWT-based authentication.
Coniguration for JWT auth:
const config = {
auth: {
requiresAuth: true,
tokenEndpoint: '/auth/login',
refreshTokenEndpoint: '/auth/refresh',
type: 'jwt', //only support for JWT currently
jwtSecret: 'secretkey', // can be any string
jwtLoginHandle: authenticateNewUser, // promise - see example below
jwtExpiresIn: 3600, // 1 hour
jwtEnabledRefreshTokens: true,
jwtStoreRefreshToken: storeRefreshToken, // promise - see example below
jwtRetrieveRefreshToken: retrieveRefreshToken, // promise - see example below
jwtRefreshExpiresIn: 604800, // 1 week
}
}
Example for authenticating a new user:
// Add this as the `jwtLoginHandle` in the config
const authenticateNewUser = async ( requestBody ) => {
const { username, password } = requestBody;
// Do your own authentication here, this is just an example
if ( username === 'admin' && password === 'admin' ) {
return 'username'; // A unique identifier that can be used to identify the user
}
else return false; // Login failed
};
Example for storing a refresh token:
// Add this as the `jwtStoreRefreshToken` in the config
const storeRefreshToken = async ( username, refreshToken ) => {
await redis.set ( `refresh-token:${ username }`, refreshToken, 60 * 60 * 24 * 30 ); // Set expiry here
return;
};
Example for retrieving a refresh token:
// Add this as the `jwtRetrieveRefreshToken` in the config
const retrieveRefreshToken = async ( username, refreshToken ) => {
const storedRefreshToken = await redis.get ( `refresh-token:${ username }` );
return storedRefreshToken === refreshToken;
};
To use JWT authentication, the requiresAuth
flag must be set to true
in the global configuration or at the endpoint level. The API will then require a valid JWT token for accessing protected routes.
Upon successful login (as determined by the jwtLoginHandle
function), the API generates a JWT token based on the provided jwtSecret
and jwtExpiresIn
configuration.
If jwtEnabledRefreshTokens
is set to true
, the API also generates a refresh token, allowing users to obtain a new access token without re-authenticating. This token is managed by the jwtStoreRefreshToken
and jwtRetrieveRefreshToken
functions.
For each request to a protected route, the API validates the provided JWT token. If the token is invalid or expired, the request is denied with an appropriate error message.
jwtSecret
is kept secure and confidential.jwtSecret
to maintain security.jwtLoginHandle
function to prevent unauthorized access.By integrating JWT authentication, the API ensures secure and efficient user authentication and authorization.
Contributions to improve the module or add new features are welcome. Please follow the standard GitHub pull request process.
Specify the license under which this module is released.
MIT License
Copyright (c) 2024 Oliver Edgington
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Oliver Edgington oliver@edgington.com
FAQs
A framework built on top of fastify aimed at removing the need for any network or server configuration.
The npm package just-another-http-api receives a total of 400 weekly downloads. As such, just-another-http-api popularity was classified as not popular.
We found that just-another-http-api demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 0 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
AGENTS.md is a fast-growing open format giving AI coding agents a shared, predictable way to understand project setup, style, and workflows.
Security News
/Research
Malicious npm package impersonates Nodemailer and drains wallets by hijacking crypto transactions across multiple blockchains.
Security News
This episode explores the hard problem of reachability analysis, from static analysis limits to handling dynamic languages and massive dependency trees.