0.0.3-alpha • Published 5 years ago

basilic v0.0.3-alpha

Weekly downloads
1
License
MIT
Repository
-
Last release
5 years ago

Basilic (alpha)

A minimalist Node.js JSON API with database integration.

Table of contents

Introduction

Basilic is a minimalist Node.js framework, based on http package. Its aim is to provide the minimal things you need to build a functional API, from the routes to the database.

import basilic, {sqlite} from 'basilic'

const api = basilic
  .database(sqlite, ':memory:')
  .get('/check', ctx => 204)
  .get('/posts', ctx => {
    const posts = await sql`SELECT * FROM posts`
    return {posts}
  })
  .listen()

Installation

yarn add basilic

Note: basilic is written in TypeScript, so types are included - no need for @types.

Don't forget the driver, depending on your database:

yarn add sqlite3  # For SQLite3
yarn add pg       # For PostgreSQL
yarn add mysql    # For MySQL

Usage

Database

type database = (BasilicConnector, options?: string) => BasilicServer

basilic.database(connector, ':memory:')

Available connectors:

import {sqlite, postgres, mysql} from 'basilic'

You can also define your own driver (see existing ones):

import {BasilicConnector} from 'basilic'

const custom: BasilicConnector = {
  ...
}

basilic.database(custom, 'options')

Method

Routes any incomming request to the correct handler. A route matches when the method and the path match:

type get:     (Path, Handler) => BasilicServer
type post:    (Path, Handler) => BasilicServer
type patch:   (Path, Handler) => BasilicServer
type put:     (Path, Handler) => BasilicServer
type delete:  (Path, Handler) => BasilicServer

api
  .get('/route', handler)
  .post('/route', handler)
  .patch('/route', handler)
  .put('/route', handler)
  .delete('/route', handler)

Path

A path can be a string or a regular RegExp:

type Path = string | RegExp

api
  // Matches only '/route'
  .get('/route', handler)

  // Matches routes starting by '/route'
  .get(/^\/route/, handler)

  // You can also use named groups (https://github.com/tc39/proposal-regexp-named-groups)
  // Matches '/route/<number>'
  // Matched groups are available in ctx.params
  .get(/\/route\/(?<id>\d+)/, handler)

Handler

type Handler = BasilicContext => any | void

api
  // Returns a 204 without body
  .get('/route', ctx => null)

  // Returns a 401 without body
  .get('/route', ctx => 401)

  // Returns a 200 without a JSON body
  // Sets automatically Content-Type: application/json
  .get('/route', ctx => ({data: 'test'}))

  // Returns a 200 without a plain text body
  // Sets automatically Content-Type: text/plain
  .get('/route', ctx => 'data')

  // Returns a 401 with custom headers and data
  .get('/route', ctx => ({
    status: 401,
    headers: {'Content-Type': 'text/plain'},
    body: 'data',
  }))

Context

type BasilicContext = {
  method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'
  url: string
  params: any
  sql: BasilicConnector['sql']
}

basilic.get('/route', ctx => {
  // The matched method
  const {method} = ctx

  // The matched path
  const {url} = ctx

  // The matched groups from the path RegExp (if exist)
  const {params} = ctx

  // The SQL template string helper
  // It allows you to easily interact with your database
  // All variables inside the template string are escaped
  const {sql} = ctx
  const value = 'value'
  const res = await sql`
    SELECT *
    FROM table
    WHERE attr = ${value}
  `
})

Listen

Starts the server:

type listen = (port = 5000, hostname = 'localhost') => void

basilic.listen()

Testing

You can test your API with supertest:

// src/api.ts

import basilic, {sqlite} from 'basilic'

const api = basilic
  .database(sqlite, ':memory:')
  .get('/route', ctx => 204)

export default api
// src/index.ts

import api from './api'

api.listen()
// src/__tests__/api.ts

import request from 'supertest'

import api from '../api'

const app = api.getListener()

describe('routes', () => {
  it('should match route', done => {
    request(app)
      .get('/route')
      .expect(204, done)
  })
})

Roadmap

  • Implement middlewares
  • Add auth middleware (JWT)
  • Add SQL migrations
  • Add native connector in context
  • Set up CORS