lifttl v0.0.738
LiftTL
Strongly typed JavaScript / Typescript framework to build your rest api applications in Node.js.
Similar api like graphQL but with better performance boost.
Nicely done playground included!
This library is still in beta stages. So don't use it in production!
Install LiftTL from npm
npm i lifttl
or
yarn add lifttl
Import library
es6:
import { Server, createResolver, model } from 'lifttl' // and many more
module:
const { Server, createResolver, model } = require('lifttl') // and many more...
Use
Basic example
basic example how to use resolvers, add it to server etc...
import { Server, createResolver, model, makeRequired } from 'lifttl'
const UserModel = model({
user_id: { type: 'string' },
})
const userResolver = createResolver({
name: 'user',
io: UserModel,
params: makeRequired(UserModel, ['user_id']), // use userModel but change user_id prop to required so parameter must be there
resolve: ({ params, io, opt, Error }) => {
return new UserModel({ user_id: params.user_id }) // You can return new UserModel or null or undefined as well
},
})
new Server({
resolvers: [userResolver],
playground: true,
})
For check this example run it & open playground url
Multiple resolvers example
Same as basic example but with multiple resolver
import { Server, createResolver, model, makeRequired } from 'lifttl'
const UserModel = model({
user_id: { type: 'string' },
book_id: { type: 'string' },
})
const userResolver = createResolver({
name: 'user',
io: UserModel,
params: makeRequired(UserModel, ['user_id']),
resolve: ({ params, io, opt, Error }) => {
return new UserModel({ user_id: params.user_id })
},
})
/**
* Right now we create bookModel
*/
const BookModel = model({
book_id: { type: 'string' },
name: { type: 'string' },
})
/**
* Book resolver
*/
const bookResolver = createResolver({
name: 'books',
io: BookModel,
isArray: true, // if you use isArray prop, you should return BookModel[] or null or undefined
params: makeRequired(BookModel, ['book_id']),
resolve: ({ params, io, opt, Error }) => {
return [new BookModel({ name: 'Harry Potter' }), new BookModel({ name: 'Pulp Fiction' })]
},
})
new Server({
resolvers: [userResolver],
playground: true,
})
For check this example run it & open playground url
Multiple nested resolvers example
Adding nested bookResolver
into userResolver.book
.
You can use as nested resolvers as you want, as well as you can use nested resolvers with normal resolvers...doesn't matters
import { Server, createResolver, model, makeRequired } from 'lifttl'
const UserModel = model({
user_id: { type: 'string' },
book_id: { type: 'string' },
})
const userResolver = createResolver({
name: 'user',
io: UserModel,
params: makeRequired(UserModel, ['user_id']),
resolve: ({ params, io, opt, Error }) => {
return new UserModel({ user_id: params.user_id })
},
})
/**
* Right now we create bookModel
*/
const BookModel = model({
book_id: { type: 'string' },
name: { type: 'string' },
})
/**
* Book resolver
*/
const bookResolver = createResolver({
name: 'books', // this will generate for you books prop inside userResolvermodel
io: BookModel,
isArray: true, // if you use isArray prop, you should return BookModel[] or null or undefined
params: makeRequired(BookModel, ['book_id']),
resolve: ({ params, io, opt, Error }) => {
return [new BookModel({ name: 'Harry Potter' }), new BookModel({ name: 'Pulp Fiction' })]
},
})
/**
* Adding bookResolver to userResolver
*/
userResolver.addConnector({
resolver: bookResolver,
relation: { book_id: 'book_id' }, // means that book_id return from userResolver === book_id to bookResolver param. Relation parent-return === children-params
paramRelation: {}, // Same but for relation parent-params === children-params
})
new Server({
resolvers: [userResolver],
playground: true,
})
For check this example run it & open playground url
Setters Example
For settings data we are using setters. Of course you can set data inside resolver. But resolvers meant to be more like getter
as setter
.
import { Server, createResolver, model, makeRequired } from 'lifttl'
const UserModel = model({
user_id: { type: 'string' },
book_id: { type: 'string' },
})
const userResolver = createResolver({
name: 'user',
io: UserModel,
params: makeRequired(UserModel, ['user_id']),
resolve: ({ params, io, opt, Error }) => {
return new UserModel({ user_id: params.user_id })
},
})
/**
* Setter dont have return option. So you dont need to return something from setter.
*/
userResolver.addSetter(
'register_user',
makeRequired(UserModel, ['user_id', 'book_id']),
({ params, busboy }) => {
// We provide as well busboy instance if upload server options is provide.. so upload files will be easy as it is. We provide busboy instance here https://github.com/mscdex/busboy
// params passed here
console.log(params)
/**
* File uplopad examples:
*
*/
// if (busboy) {
// busboy.on('file', (fieldname, file, filename, encoding, mimetype) => {
// console.log(filename, 'this is your file')
// file.resume() // If you dont make any changes with the file use file.resume for less memoery consumptions as files buffers stays in memory...
// })
// }
}
)
new Server({
resolvers: [userResolver],
upload: true, // If you want to upload file via setter
playground: true,
})
For check this example run it & open playground url
Custom Error handling
- for create custom error handling we provide
Error
function to each resolve, setters & middlewares callbacks. Error
handler can be used as well forsuccess
statuses. For examplestatus:201 | user_created
use
const userResolver = createResolver({
name: 'user',
io: UserModel,
params: makeRequired(UserModel, ['user_id']),
resolve: ({ params, io, opt, Error }) => {
if (!params.user_id) {
return Error({ msg: 'You dont provide user_id', code: 400 }) // check if user_id params is provided. If not we return error messagge to the user
} else {
return new UserModel({ user_id: params.user_id })
}
},
})
Error options
Name | Type | Description | Required | Default |
---|---|---|---|---|
msg | string | string of error messagge | false | '' |
code | number | return response code | false | 400 |
success | boolean | If you dont provide code you can just provide success | false | false |
Methods
Server
options
Name | Type | Description | Required | Default |
---|---|---|---|---|
resolvers | array | Array of resolvers | true | |
port | number | port of the server | false | 4000 |
clusterMode | boolean | Cluster mode enables multiCpu instances for better performance | false | false |
numCPUs | number | Number of cpu's when clusterMode is enabled | false | require('os').cpus().length - 1 |
http2 | boolean | Enable http2 -> for that you have to provide cert & key options | false | |
https | boolean | Enable https -> for that you have to provide cert & key options | false | |
key | Buffer | Key file -> fs.readFileSync('path/to/file') | false | |
cert | Buffer | Cert file -> -> fs.readFileSync('path/to/file') | false | |
playground | boolean | Enable or disable playground / Default port:3005 | false | false |
playgroundPort | number | Playground port | false | 3005 |
allowedOrigins | '*' or string[] | Allowed origins | false | |
upload | boolean | If enable file upload. | false | false |
beforeEnd | event | Event trigger before end of response send to the client | false |
Resolver
options
- Resolvers return as well another object which can be modified.
Name | Type | Description | Required | Default |
---|---|---|---|---|
name | string | Name of resolver | true | |
io | ModelReturn | model for input / output handling | true | |
params | ModelReturn | model for resolver params | true | |
isArray | boolean | If resolver return array of models or not | false | false |
middlewares | arraycallback | Array of middleware functions which can be used for authorization or another purposes | false | [] |
middlewaresOnlyModel | boolean | If true middelwares will be used only for return of model & not setters | false | false |
resolve | async callback | Resolve callback with http req, http res, opt, Error, io, params | true |
resolve
callback options
Name | Type | Description | Required | Default |
---|---|---|---|---|
opt | object | Object with info & as well can be used as store per request | ||
io | ModelReturn | model for input / output handling | ||
params | ModelReturn | model for resolver params | true | |
Error | function | function properties are: success:boolean, msg:string, code:number | ||
req | http.IncomingMessage | http request | ||
res | http.ServerResponse | http response |
'Resolver' returned methods
use
const userResolver = createResolver({
...
})
userResolver.addSetter('registerUser', UserModel, async({params,busboy, Error}) => {
})
userResolver.addConnector({
resolver: bookResolver,
relation: { user_id: 'user_type' },
paramRelation: {},
})
Name | Type | Description | Required | Default |
---|---|---|---|---|
addSetter | function | Add setter function to the resolver | ||
addConnector | function | add connector or relation beetween another resolver (child / parent relation) |
addSetter options
addSetter allows you to add setter to resolvers. Setters means to set something. So inside this setter you can for example create user or add book to database....
Name Type Description Required Default name string Name of setter. Name will be add to the final model true params function add connector or relation beetween another resolver (child / parent relation) true resolve async callback Resolve callback with http req, http res, opt, Error, io, params , Busboy true middlewares arraycallback Array of middleware functions which can be used for authorization or another purposes [] limits busboyLimits obj Busboy limits for file uploading
addConnector options
addConnetors allows you to add relation beetween current resolver & another resolver as relation
parent / child
Name Type Description Required Default resolver resolver Child resolver true relation object prop is taken from parent io model & will be transfered to custom child param model true {} paramRelation object prop is taken from parent param model & will be transfered to custom child param model true {}
Example
relation
can be =>{ user_id: 'user_type' }
whereuser_id
from parentIO model
will be transfered touser_type
child resolverparam Model
.paramRelation
can be =>{ user_id: 'user_type' }
whereuser_id
from parentparam model
will be transfered touser_type
child resolverparam Model
.
Middelwares
- middleware is callback function which have to return
boolean | ErrorReturn | Promise<boolean | ErrorReturn>
. If returns true proccess can continue,otherwise process will stop.
For example you will use middleware if you want to check if client / user is authorized.
use
const someMiddleware1 = ({ params, opt, Error }) => {
return true
}
const someMiddleware2 = ({ params, opt, Error }) => {
return Error({ ms })
return false
}
const userResolver = createResolver({
name: 'user',
io: UserModel,
middlewares: [someMiddleware, someMiddleware2],
params: makeRequired(UserModel, ['user_id']),
resolve: ({ params, io, opt, Error }) => {
return new UserModel()
},
})
Middleware Callback options
Name | Type | Description | Required | Default |
---|---|---|---|---|
opt | object | Object with info & as well can be used as store per request | ||
io | ModelReturn | model for input / output handling | ||
Error | function | function properties are: success:boolean, msg:string, code:number |
Another scripts
- library comes with cli command
create
so if you install library you can usenpx lifttl create
command to build you app faster!
Benchmark
- compared apollo graphql, graphql.js & express -for testing I used bombardier tool.
LiftTL clusterMode & num of cpu-6v example from "Multiple nested resolvers example"
Statistics | Avg | Stdev | Max |
---|---|---|---|
Reqs/sec | 73209.02 | 20940.07 | 103736.40 |
Latency | 1.70ms | 7.18ms | 257.07ms |
HTTP codes: 1xx - 0, 2xx - 1000000, 3xx - 0, 4xx - 0, 5xx - 0 others - 0 Throughput: 52.09MB/s
liftTL normalMode example from "Multiple nested resolvers example"
Statistics | Avg | Stdev | Max |
---|---|---|---|
Reqs/sec | 12375.37 | 3024.95 | 29486.88 |
Latency | 4.92ms | 657.69us | 52.50ms |
HTTP codes: 1xx - 0, 2xx - 1000000, 3xx - 0, 4xx - 0, 5xx - 0 others - 0 Throughput: 18.05MB/s
graphQL.js example from https://graphql.org/graphql-js/ & node http server
Statistics | Avg | Stdev | Max |
---|---|---|---|
Reqs/sec | 13684.79 | 1056.40 | 14957.20 |
Latency | 9.13ms | 0.99ms | 72.42ms |
HTTP codes: 1xx - 0, 2xx - 1000000, 3xx - 0, 4xx - 0, 5xx - 0 others - 0 Throughput: 3.52MB/s
apollo GraphQL example from https://www.apollographql.com/docs/apollo-server/getting-started/
Statistics | Avg | Stdev | Max |
---|---|---|---|
Reqs/sec | 5252.22 | 724.85 | 6368.73 |
Latency | 23.83ms | 2.23ms | 145.26ms |
HTTP codes: 1xx - 0, 2xx - 1000000, 3xx - 0, 4xx - 0, 5xx - 0 others - 0 Throughput: 3.09MB/s
express.js example from https://expressjs.com/en/starter/hello-world.html
Statistics | Avg | Stdev | Max |
---|---|---|---|
Reqs/sec | 17433.23 | 2404.06 | 21519.79 |
Latency | 7.17ms | 0.86ms | 56.33ms |
HTTP codes: 1xx - 0, 2xx - 1000000, 3xx - 0, 4xx - 0, 5xx - 0 others - 0 Throughput: 7.28MB/s
Scripts used:
for liftTL
bombardier -c 125 -n 1000000 --header='content-type: application/json;odata=verbose' --method=POST --body='{"user_resolver":{"user_id":true,"book":{"name":true,"author":true,"book_id":true},"name":true,"samko":true,"$params":{"get_user_by_name":"John doe"}}}' http://localhost:4000
for apollo-server
bombardier -c 125 -n 1000000 --header='content-type: application/json;odata=verbose' --method=POST --body='{"operationName":null,"variables":{},"query":"{\n books {\n title\n author\n }\n}\n"}' http://localhost:4000
for grapqhl.js with simple http server
bombardier -c 125 -n 1000000 --header='content-type: application/json;odata=verbose' --method=POST --body='{ hello }' http://localhost:4000
*Todo - finish readme.
Models api:
Javascript / Typescript type definitions by generic functions.
Most easy way how to create interfaces / enums / models for another purposes.
Import all library
es6:
import { model, enums, required, modelInclude, modelExclude, isArray } from 'lifttl'
module:
const { model, enums, required, modelInclude, modelExclude, isArray } = require('lifttl')
Use
Creating of basic models
import { model } from 'typelift'
const userModel = model({
user_id: { type: 'uuid', required },
comment_id: { type: 'uuid', required },
email: { type: 'string', format: 'email' },
})
const newUser = new userModel({ user_id: '' }) // Intellisense will help you with creating of new model. So each time you will know about types inside model. No matters if you are using js or ts.
console.log(newUser.scheme) // output JSONSCHEMA7 of model
Creating of nested models
import { model, required } from 'typelift'
const bookModel = model({ book_id: { type: 'string' } })
const userModel = model({
user_id: { type: 'uuid', required },
comment_id: { type: 'uuid', required },
email: { type: 'string', format: 'email' },
book: { type: bookModel }, //you can use nested models as well.
})
const newUser = new userModel({ book: { book_id: '' } })
Creating of enums
import { model, enums } from 'typelift'
const USER_TYPE = enums('ADMIN', 'NORMAL')
const userModel = model({
user_id: { type: 'uuid' },
comment_id: { type: 'uuid' },
email: { type: 'string', format: 'email' },
user_type: { type: USER_TYPE },
})
const newUser = new userModel({ user_type: 'ADMIN' })
Merging models
import { modelMerge } from 'typelift'
const userModel = model({
user_id: { type: 'uuid' },
})
const paginationModel = model({
page: { type: 'number' },
limit: { type: 'number' },
})
const userParamsModel = modelMerge(userModel, paginationModel) // merge two models together
Make required fields after model creation
import { makeRequired } from 'typelift'
const userModel = model({
user_id: { type: 'uuid' },
name: { type: 'string' },
})
const userModelWithRequiredProps = makeRequired(userModel, ['user_id']) // make user_id prop required
Available types
Date boolean number string uuid timeUuid
Each type can be as well undefined until you dont provide required
prop into the model like this:
import { model, required } from 'typelift'
const userModel = model({
user_id: { type: 'uuid', required }, // Required means that for creating new userModel instance the new value will be filled as well.
})
const newUser = new userModel() // newUser will automatically generate new uuid for user_id prop becouse you provide required parameter = true
Available Formats
'date' | 'time' | 'date-time' | 'uri' | 'uri-reference' | 'uri-template' | 'email' | 'hostname' | 'ipv4' | 'uuid'
for type string you can provide as well format property like this
import { model } from 'typelift'
const userModel = model({
email: { type: 'string', format: 'email' }, // email is type string with format of email
})
Patterns
As well you can use custom regexes like this:
import { model } from 'typelift'
const userModel = model({
email: { type: 'string', pattern: '[abc]+' }, // validation use new RegExp("[abc]+")
})
Working nicely with javascript coded with love in typescript.
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
1 year ago
1 year ago
1 year ago
2 years ago
1 year ago
1 year ago
1 year ago
1 year ago
2 years ago
1 year ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
3 years ago
3 years ago
3 years ago
2 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago