tspace-0spear
tspace-0spear is a lightweight API framework for Node.js that is fast and highly focused on providing the best developer experience.
It aims to be performant HTTP & Websocket Server using uWebSockets written in C++.
Install
Install with npm:
npm install tspace-0spear --save
Basic Usage
StartServer
import ZeroSpear from "tspace-0spear";
new ZeroSpear()
.get('/' , () => 'Hello world!')
.get('/json' , () => {
return {
message : 'Hello world!'
}
})
.listen(3000 , ({ server, port }) =>
console.log(`server listening on : http://localhost:${port}`)
)
CRUD
import Spear from "tspace-0spear";
const spears = [
{
id : 1,
damage : 100
},
{
id : 2,
damage : 75
},
{
id : 3,
damage : 50
}
]
new ZeroSpear()
.useBodyParser()
.get('/' , () => spears)
.get('/:id' , ({ params }) => spears.find(spear => spear.id === Number(params.id ?? 0)))
.post('/' , ({ body }) => {
const damage = Number(body.damage ?? (Math.random() * 100).toFixed(0))
const id = spears.reduce((max, spear) => spear.id > max ? spear.id : max, 0) + 1
spears.push({ id , damage })
return spears.find(spear => spear.id === id)
})
.patch('/:id' , ({ params , body , res }) => {
const damage = Number(body.damage ?? (Math.random() * 100).toFixed(0))
const id = Number(params.id)
const spear = spears.find(spear => spear.id === id)
if (spear == null) return res.status(404).json({ message : 'Spear not found'})
spear.damage = damage;
return spears.find(spear => spear.id === id)
})
.delete('/:id', ({ params , res }) => {
const id = Number(params.id)
const spear = spears.find(spear => spear.id === id)
if (spear == null) return res.status(404).json({ message : 'Spear not found'})
spears.splice(spears.findIndex(spear => spear.id === Number(params.id ?? 0)), 1)
return res.status(204).json()
})
.listen(3000 , ({ server, port }) =>
console.log(`server listening on : http://localhost:${port}`)
)
Cluster
import Spear from "tspace-0spear";
new ZeroSpear({
cluster : {
use : true,
maxWorkers : 3
}
})
.get('/' , () => 'Hello world!')
.get('/json' , () => {
return {
message : 'Hello world!'
}
})
.listen(3000 , ({ server, port }) =>
console.log(`server listening on : http://localhost:${port}`)
)
Middleware
export default (ctx : TContext, next: TNextFunction) =>{
console.log('cat middleware globals');
return next();
}
import Spear { Router, TContext, TNextFunction } from "tspace-0spear";
import CatMiddleware from './cat-middleware.ts'
(async () => {
const port = Number(process.env.PORT ?? 3000)
const app = new ZeroSpear({
middlewares: [ CatMiddleware ]
})
app.use((ctx : TContext , next : TNextFunction) => {
console.log('global middlewares')
return next()
})
app.get('/' , ({ res } : TContext) => {
return res.json({
message : 'hello world!'
});
})
app.listen(port , () => console.log(`Server is now listening http://localhost:${port}`))
})()
Controller
import {
Controller ,
Middleware ,
Get ,
Post,
Patch,
Put,
Delete,
WriteHeader,
Query,
Body,
Params,
Cookies,
Files,
StatusCode,
TCookies,
TParams,
TRequest,
TResponse ,
TQuery,
TFiles,
TContext,
TNextFunction
} from 'tspace-0spear';
import CatMiddleware from './cat-middleware.ts'
@Controller('/cats')
class CatController {
@Get('/')
@Middleware(CatMiddleware)
@Query('test','id')
@Cookies('name')
public async index({ query , cookies } : {
query : TQuery<{ id : string }>
cookies : TCookies<{ name : string}>
}) {
return {
query,
cookies
}
}
@Get('/:id')
@Middleware(CatMiddleware)
@Params('id')
public async show({ params } : TContext) {
return {
params
}
}
@Post('/')
@Middleware(CatMiddleware)
public async store({ body } : TContext) {
return {
body
}
}
@Put('/:id')
@Middleware(CatMiddleware)
public async update({ files } : TContext) {
return {
files
}
}
@Post('/upload')
@Middleware(CatMiddleware)
public async upload({ files } : TContext) {
return {
files
}
}
@Delete('/:id')
@Middleware(CatMiddleware)
public async destroy({ params } : TContext) {
return {
params
}
}
}
import Spear , { Router, TContext, TNextFunction } from "tspace-0spear";
import CatController from './cat-controller.ts'
(async () => {
const app = new ZeroSpear({
controllers: [ CatController ]
})
app.useBodyParser()
app.useCookiesParser()
app.useFileUpload()
app.get('/' , ( { res } : TContext) => {
return res.json({
message : 'hello world!'
});
})
const port = 3000
app.listen(port , () => console.log(`Server is now listening http://localhost:${port}`))
})()
Router
import Spear , { Router, TContext, TNextFunction } from "tspace-0spear";
const app = new ZeroSpear()
const router = new Router()
router.groups('/my',(r) => {
r.get('/cats' , ({ req , res }) => {
return res.json({
message : 'Hello, World!'
})
})
return r
})
router.get('/cats' , ({ req , res }) => {
return res.json({
message : 'Hello, World!'
})
})
app.useRouter(router)
app.get('/' , ({ res } : TContext) => {
return res.json({
message : 'hello world!'
});
})
let port = 3000
app.listen(port , () => console.log(`Server is now listening http://localhost:${port}`))
Swagger
import {
TContext,
Controller,
Get ,
Post,
Put,
Patch,
Delete,
Swagger
} from 'tspace-0spear';
@Controller('/cats')
class CatController {
@Get('/')
@Swagger({
query : {
id : {
type : 'integer'
},
name : {
type : 'string'
}
}
})
public async index({ query } : TContext) {
return {
query
}
}
@Get('/:id')
@Swagger({
description : '- message',
query : {
id : {
type : 'integer'
}
},
responses : [
{ status : 200 , description : "OK" , example : { id : 'catz' }},
{ status : 400 , description : "Bad request" , example : { id : 'catz' }}
]
})
public async show({ params } : TContext) {
return {
params
}
}
@Post('/')
@Swagger({
bearerToken : true,
body : {
description : 'The description !',
required : true,
properties : {
id : {
type : 'integer',
example : 1
},
name : {
type : 'string',
example : "xxxxx"
}
}
}
})
public async store({ body } : TContext) {
return {
body
}
}
@Put('/:uuid')
@Swagger({
bearerToken : true,
body : {
description : 'The description !',
required : true,
properties : {
id : {
type : 'integer',
example : 1
},
name : {
type : 'string',
example : "xxxxx"
}
}
}
})
public async updated({ body } : TContext) {
return {
body
}
}
@Patch('/:uuid')
@Swagger({
bearerToken : true,
body : {
description : 'The description !',
required : true,
properties : {
id : {
type : 'integer',
example : 1
},
name : {
type : 'string',
example : "xxxxx"
}
}
}
})
public async update({ body } : TContext) {
return {
body
}
}
@Delete('/:uuid')
@Swagger({
bearerToken : true
})
public async delete({ params } : TContext) {
return {
params
}
}
@Post('/upload')
@Swagger({
bearerToken : true,
files : {
required : true,
properties : {
file : {
type : 'array',
items: {
type:"file",
format:"binary"
}
},
name : {
type : 'string'
}
}
}
})
public async upload({ body , files } : TContext) {
return {
body,
files
}
}
}
(async () => {
await new ZeroSpear({
controllers: [ CatController ]
})
.get('/' , ({ res } : TContext) => {
return res.json({
message : 'hello world!'
});
})
.useSwagger({
path : "/docs",
servers : [
{ url : "http://localhost:3000" , description : "development"},
{ url : "http://localhost:8000" , description : "production"}
],
info : {
"title" : "Welcome to the the documentation",
"description" : "This is the documentation"
}
})
.listen(3000 , () => console.log(`Server is now listening http://localhost:3000`))
})()
Others
const app = new ZeroSpear({
logger : true,
globalPrefix : '/api'
})
app.useLogger({
methods : ['GET','POST'],
exceptPath : ['/']
})
app.enableCors({
origins: [
/^http:\/\/localhost:\d+$/
],
credentials: true
})
app.useFileUpload({
limit : 1000 * 1000,
tempFileDir : 'tmp',
removeTempFile : {
remove : true,
ms : 1000 * 60
}
})
app.get('/' , ({ res } : TContext) => {
return res.json({
message : 'hello world!'
});
})
app.get('/bad-request' , ({ res } : TContext) => {
return res.status(400).json({
message : 'hello but bad request'
});
})
app.get('/not-found' , ({ res } : TContext) => {
return res.status(404).json({
message : 'hello but not found the world!'
});
})
app.get('/errors', () => {
throw new Error('testing Error handler')
})
app.response((results : unknown , statusCode : number) => {
if(typeof results === 'string') return results
return {
success : statusCode < 400,
...results,
statusCode
}
})
app.notfound(({ res } : TContext) => {
return res.notFound();
})
app.catch((err : Error , { res } : TContext) => {
return res
.status(500)
.json({
success : false,
message : err?.message,
statusCode : 500
});
})
const port = 3000
app.listen(port , () => console.log(`Server is now listening http://localhost:${port}`))