Security News
RubyGems.org Adds New Maintainer Role
RubyGems.org has added a new "maintainer" role that allows for publishing new versions of gems. This new permission type is aimed at improving security for gem owners and the service overall.
A complete framework for creating microservices and large-scale server-side applications for businesses
The framework adheres to the concept of "just install and use". Everything you need to run a server application:
Everything of this is provided out of the hood. All that is needed for some of the above things to work is to specify the basic settings in the configuration file.
The framework provides a very flexible architecture, making it easy to extend and customize all of these things, up to and including completely changing the basic behavior by specifying class loaders and references in the same configuration file.
The framework is independent, thus it doesn't rely on other frameworks like Express.
it downloads the current framework version's application with full usage examples.
git clone https://github.com/nodart-labs/nodart-app.git
it creates a base application structure from cli.
npx nodart create-app
it creates a microservice application file. Use flag "--js" when you are not using typescript in project.
npx nodart microapp --js[optional]
You can launch microservice or monolithic apps, or you can combine them, depending on the requirements. In this article, you can read more about application architecture.
BASIC INITIALISATION:
import { App } from "nodart";
const config = require("./config");
/**
Be aware that this fundamental initialization
automatically creates the necessary app files and folders.
(database folder, controllers folder, models folder,
services folder, views folder and etc.)
*/
new App({ ...config }).init().then(async (app) => {
const server = await app.serve(3000, "http", "127.0.0.1");
// do anything on startup, for example:
// configure server:
server.timeout = 30000; // setting max timeout on requests
// or create payload for all HTTP requests:
app.service.setRequestPayload((req, res) => {});
// or launch HTTP service:
const http = app.service.http;
http.get("url/path/with/:params", (scope) => {});
});
import { App } from "nodart";
const config = require("./config");
new App({ ...config }).start(3000).then(({ app, http, server }) => {
// base HTTP processing:
http.get("/path/:first/:second/:+optional_id?", ({ route, http }) => {
const { first, second, optional_id } = route.params;
const { httpQueryStringParam } = http.query;
// sending template from views folder:
http.respond.view("path/to/template/from/views/folder", { queryParam });
// sending file:
http.sendFile("absolute/path/to/file.png", "image/png");
// sending response JSON:
http.respond.data({ first, second, optional_id }, 200);
// or
http.send({ first, second, optional_id }, 200, "application/json");
// or just:
return { first, second, optional_id };
// throwing HttpException. The message will be sent to user.
http.throw(500, "some error occurred.");
// throwing RuntimeException. The message will not be sent to user,
// but shown in server logs.
http.exit(500, "some error occurred.", { someData });
});
// fetching data from POST request:
http.post("/", ({ http }) => {
const { someData } = http.data;
});
// fetching data from POST miltipart/form-data:
http.post("/", async ({ http }) => {
const { fields, files } = await http.form.fetchFormData();
const stat = http.form.stat("field_name");
});
// dealing with services and models:
import { SampleService } from "./services/sample";
import { SampleModel } from "./models/subfolder/sample";
http.get("/", async ({ service, model }) => {
const sampleService = service().sample as SampleService;
const sampleModel = model().subfolder.sample as SampleModel;
const users = await sampleModel.query.select().table("users");
sampleService.scope.http.send({ users });
});
});
// "./services/sample.ts"
import { Service } from "nodart";
export class SampleService extends Service {
get orm() {
return this.scope.app.service.db.orm; // or this.scope.app.get('orm').call()
}
}
You can change the basic behavior of class loaders by specifying in the base project configuration
import { App, ControllerLoader, nodart } from "nodart";
class ControllerLoaderOverride extends ControllerLoader {
protected _pathSuffix = "Controller";
}
const config = <nodart.app.AppConfigInterface>{
loaders: {
controller: ControllerLoaderOverride,
},
};
new App(config);
An abstract class called
RelationModel
enhances the functionality of the standard query builder. The system by default use the well-likedKnex
query builder: https://knexjs.org/guide/
Using pre-written database queries,
RelationModel
enables you to create query blanks and chain them together into a series of inquiries. In this manner, retrieving a list or a single object from the database does not require you to always develop new methods in the data model.
@/models/Book.ts
:import { RelationModel } from "nodart";
export interface IBook {
id: number;
author_id: number;
name: string;
meta_keywords: string;
meta_description: string;
}
export class Book extends RelationModel {
declare model: IBook;
get table() {
return "books";
}
static get table() {
return "books";
}
statements = (query: this["query"]) => ({
author: <T extends string>(author_id: number) =>
query.where("author_id", author_id) as T extends "list"
? Promise<IBook[]>
: Promise<IBook>,
});
}
@/controllers/BooksController.ts
:import { Controller, nodart } from "nodart";
import { Book, IBook } from "@/models/Book";
const statuses = nodart.http.HTTP_STATUS;
export class BooksController extends Controller {
model = {
Book: {} as Book,
};
declare book: Book;
init() {
this.book = this.model.Book;
}
/**
@returns: {Object<IBook>}
*/
async get(id: number) {
// Be aware to use the "result" property
// at the end of the call chain.
return await this.book.use.get({ id }).result;
}
/**
@returns: {Array<IBook>}
*/
async list(author_id: number) {
// The "result" property is not required
// after "on" property.
return await this.book.use.list("id", "name").on.author(author_id);
}
async patch(id: number) {
const { name } = this.http.data;
const result = await this.book.use.set({ name }).at({ id }).result;
return result ? { ok: true } : this.http.throw(statuses.NOT_FOUND);
}
async post() {
const data = this.http.data as IBook;
const result = await this.book.use.add(data).result; // Array<number>;
return { ok: !!result?.[0] };
}
async delete(id: number) {
const result = await this.book.use.delete({ id }).result;
return { ok: !!result };
}
}
nodart.mutable.MutableInterface
is an interface that provides a property with methods to cast the source data to the desired data model.
@/models/Book.ts
:import { RelationModel, nodart } from "nodart";
export interface IBook {
id: number;
author_id: number;
name: string;
meta_keywords: string;
meta_description: string;
}
export interface IBookAPI
extends Omit<IBook, "meta_keywords" | "meta_description"> {
meta: {
description: string;
keywords: string;
};
}
export interface IBookMutable extends nodart.mutable.DataMutable {
book(data: IBook): [Partial<IBookAPI>, (keyof IBook)[]];
}
/**
RelationModel implements nodart.mutable.MutableInterface
*/
export class Book extends RelationModel {
declare model: IBook;
get table() {
return "books";
}
static get table() {
return "books";
}
readonly mutable: IBookMutable = {
book: (data) => [
// This data will be assigned to the source data:
{
meta: {
description: data.meta_description,
keywords: data.meta_keywords,
},
},
// This optional part specifies which properties
// will be excluded from the source data:
["meta_description", "meta_keywords"],
],
};
statements = (query: this["query"]) => ({
author: <T extends string>(author_id: number) =>
query.where("author_id", author_id) as T extends "list"
? Promise<IBook[]>
: Promise<IBook>,
});
}
@/controllers/BooksController.ts
:import { Controller } from "nodart";
import { Book } from "@/models/Book";
export class BooksController extends Controller {
model = {
Book: {} as Book,
};
declare book: Book;
init() {
this.book = this.model.Book;
}
/**
@returns: {Object<IBookAPI>} -> {
id: number;
author_id: number;
name: string;
meta: {
description: string;
keywords: string;
}
}
*/
async get(id: number) {
const data = await this.book.use.get({ id }).result;
return this.book.mutate.get.book(data);
}
/**
@returns: {Array<IBookAPI>}
*/
async list(author_id: number) {
const data = await this.book.use.list().on.author<"list">(author_id);
return this.book.mutate.list.book(data);
}
}
new App({ ...config }).init().then(async (app) => {
const server = await app.serve(3000, "https", "127.0.0.1", () => {
const fs = require("fs");
const ssl = {
cert: fs.readFileSync("./localhost.crt"),
key: fs.readFileSync("./localhost.key"),
};
return require("https").createServer(ssl);
});
});
new App({ ...config })
.start(3000, "http", "127.0.0.1", (app) => {
return require("http").createServer((req, res) => {
app.resolveHttpRequest(req, res);
});
})
.then(({ app, http, server }) => {
//...
});
npm run dev
npm run start
The framework is built on the premise that performance and functionality should be perfectly balanced. The performance of some well-known server-side frameworks is compared here.
Spoiler message: As you can see, the NodArt framework is not far behind the fastest Fastify, and in some aspects surpasses it and all other frameworks.
Environment:
autocannon -R 10000 http://localhost:3000
(10000 requests per second; Total connections/users: 10; Total time: 10 seconds)
http.get("/", () => {
return { hello: "world" };
});
Framework | Bytes/sec | Requests/sec |
---|---|---|
Fastify v4.0.0 | 1.91 MB | 10183 |
NodArt v4.2.0 | 2.02 MB | 10143 |
Express v4.18.2 | 1.94 MB | 7683 |
Nest.js v9.0.0 | 1.61 MB | 6395 |
http.get("/test/:param1/:param2/:param3/:param4", ({ route }) => {
const { param1, param2, param3, param4 } = route.params;
return { param1, param2, param3, param4 };
});
Framework | Bytes/sec | Requests/sec |
---|---|---|
Fastify v4.0.0 | 2.27 MB | 10143 |
NodArt v4.2.0 | 2.45 MB | 10127 |
Express v4.18.2 | 2.17 MB | 7531 |
Nest.js v9.0.0 | 1.8 MB | 6239 |
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Title</title>
</head>
<body>
<h1>Hello world!</h1>
</body>
</html>
Framework | Bytes/sec | Requests/sec |
---|---|---|
NodArt v4.2.0 | 4.4 MB | 7955 |
Fastify v4.0.0 | 2.32 MB | 5295 |
Express v4.18.2 | 2 MB | 4343 |
Nest.js v9.0.0 | 1.87 MB | 4057 |
npx nodart [command name] [command action optional] --[argument name optional] [argument value]
node cmd [command name] [command action optional] --[argument name optional] [argument value]
Before making migrations you should insert database configuration options into "cmd/index.js" file
npx nodart migrate make --name migration-name
npx nodart migrate up | down
npx nodart migrate make-source --name source-name --migrations[optional] migration1 migration2 ...
npx nodart migrate source-up | source-down --name source-name --migrations[optional] migration1 migration2 ...
npx nodart migrate all-source-up | all-source-down --exclude[optional] excluded-migration-sourcename
npx nodart migrate rollback --all[optional]
npx nodart migrate latest
npx nodart migrate version
npx nodart migrate list
npx nodart migrate unlock
If the seed directory config is an array of paths, the seed file will be generated in the latest specified.
npx nodart seed make --name seed-name
npx nodart seed run
npx nodart seed make-source --name source-name --seeds[optional] seed1 seed2 ...
npx nodart seed source-run --name source-name --seeds[optional] seed1 seed2 ...
npx nodart seed all-source-run --exclude[optional] excluded-seed-sourcename
FAQs
A complete framework for creating microservices and large-scale server-side applications for businesses
The npm package nodart receives a total of 2 weekly downloads. As such, nodart popularity was classified as not popular.
We found that nodart demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer 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
RubyGems.org has added a new "maintainer" role that allows for publishing new versions of gems. This new permission type is aimed at improving security for gem owners and the service overall.
Security News
Node.js will be enforcing stricter semver-major PR policies a month before major releases to enhance stability and ensure reliable release candidates.
Security News
Research
Socket's threat research team has detected five malicious npm packages targeting Roblox developers, deploying malware to steal credentials and personal data.