@simplej/rapid v0.0.111
Rapid
A Node.js framework for quickly building API servers backed by a PostgreSQL database.
Install
npm install @simplej/rapid @simplej/rapid-cliGetting started
Install the Rapid CLI and use it to scaffold an app.
npm install --global @simplej/rapid-cli
rapid init my_appStart your new app.
cd my_app
npm run startNow connect at http://localhost:9090
Using the Rapid CLI
rapid init [name] - Create a blank Rapid project.
rapid - Start the app in the current directory using the models, controllers, and API routers found in the project.
rapid --root ./path/to/app - Pass a directory to rapid instead of using the current directory.
rapid watch - Start the app and restart on changes and enables a REPL for easily running code against your rapid server.
rapid migrate - Run the migrations in ./migrations.
rapid seed - Run the seeds in ./seeds.
rapid clear - Drop the database.
API
new Rapid(rootPath)
Create a new rapid instance.
const rapid = await Rapid('/my/app/dir');rapid.start() -> rapid
Start the rapid app. Starts the database, webserver, etc. and resolving any added modules (models, seeds, actions, etc.). Returns a promise that resolves the rapid instance when complete.
const rapid = await Rapid('/my/app/dir').start();rapid.autoload() -> rapid
Rapid has built in support for loading app modules using filename and directory conventions. Meaning models, seeds, actions, routes, etc. will be loaded relative to the root path passed to the Rapid constructor. This can be enabled by calling the autoload method on the rapid instance.
const rapid = await Rapid('/my/app/dir').autoload().start();Models
Rapid used the Objection.js ORM's Model class. Models are added to rapid using the addModel method.
rapid.addModel(rapid =>
class User extends rapid.Model {
static get tableName() { return 'users'; }
static get jsonSchema() {
return {
type: 'object',
required: ['name', 'age'],
properties: {
id: { type: 'integer' },
age: { type: 'integer' },
name: { type: 'string', minLength: 2 },
},
};
}
}
);Models can be accessed through rapid.Models:
const users = await rapid.models.User.query();Actions
Actions are added to rapid using the action method and can optionally include an input schema for validation. All actions are asynchronous.
rapid.action(
'add',
{
type: 'object',
required: ['a', 'b'],
properties: {
a: { type: 'integer' },
b: { type: 'integer' },
},
},
async ({ a, b }) => {
return a + b;
},
);Actions can be accessed and run through rapid.actions.
const result = await rapid.actions.add({ a: 5, b: 10 });Routes / Routers
Routes and routers allow you to handle incoming HTTP requests. Rapid uses Koa and Koa Router to handle routing. Routes paths are prefixed with /api/.
Routes can be added using the rapid api property:
rapid.api
.get('/user/:userId', someMiddleware(), async context => {
const userId = +context.params.userId;
const user = await rapid.models.User
.query()
.where('id', userId)
.first();
context.response.body = { user };
});Channels
Channels are just socket.io namespaces. Useful in situations where you need to be able to send data directly to clients instead of just passively waiting for requests and responding.
rapid.addChannels(rapid =>
rapid.io
.of('/channelName')
.on('connection', socket => {
socket.on('echo', data => socket.emit('echo-response', data));
})
);Hooks
Hooks allow you to run arbitrary code at different points in the rapid lifecycle (ex. rapidWillStart, modelsDidAttach, rapidDidStart, etc).
Hooks can be added using the rapid addHook method:
rapid.addHook({
async modelsDidAttach(rapid) {
// do something after models attach
},
async rapidDidStart(rapid) {
// do something once app has started
},
})Seeds
Database seed functions for populating the database with initial data.
Seeds can be added using the rapid addSeeds method:
rapid.addSeeds(
async function createUsers(rapid) {
const { User } = rapid.models;
await User.query().insert([ ... ]);
},
async function createPosts(rapid) {
const { Post } = rapid.models;
await Post.query().insert([ ... ]);
},
)Migrations
Database migrations for migrating the structure of the database. Migrations are handled by Knex.
Middleware
Middleware is accessable through rapid.middleware.
auth
Require a valid auth token to access the endpoint. Responds with a 401 error if the auth token is missing or invalid. The auth middleware expects the auth token to be in the Authorization header.
const { auth } = rapid.middleware;
rapid.api.get('/secret', auth(), context => {
context.response.body = { secretUserData: { ... } };
});socketAuth
Similar to the auth middleware except it works on socket channels instead of the webserver.
const { socketAuth } = rapid.middleware;
rapid.addChannels(rapid =>
rapid.io
.of('/channelName')
.use(socketAuth())
.on('connection', socket => {
socket.on('private', data => socket.emit('secret-data', '...'));
})
);login
Takes a function that receives a credentials object and returns either a user object or null. If the user resolver function returns null, the endpoint will respond with a 400 error otherwise it will set context.state.user / context.state.authToken and continue the request.
const { login } = rapid.middleware;
rapid.api.post(
'/login',
login(async ({ username, password }) => {
const user = await rapid.models.User.query().where('email', username).first();
if(!user) return null;
if(!await rapid.helpers.verifyPassword(password, user.password)) return null;
return user;
}),
context => {
context.response.body = {
user: context.state.user,
authToken: context.state.authToken,
};
}
);Helpers
Helpers are just utility functions available through rapid.helpers.
hashPassword
Hashes and salts a password using bcryptjs. Returns a promise that resolves a string. Useful for making passwords safe to store on the server. Use verifyPassword to check if the hashed password matches a plaintext password.
const hashedPassword = await rapid.helpers.hashPassword('my password');modelToJWT
Converts a model instance into a JSON web token. Used in the login middleware. Useful for manually authorizing a client.
const { modelToJWT } = rapid.helpers;
const { User } = rapid.models;
const user = await User.query().where('username', 'user').first();
const authToken = modelToJWT(user);
console.log('user auth token', authToken);verifyPassword
Checks if a plaintext password matches a hashed password. Returns a promise that resolves true/false.
const { verifyPassword } = rapid.helpers;
const { User } = rapid.models;
const username = 'user';
const password = 'secret';
const user = await User.query().where('username', username).first();
const valid = await verifyPassword(password, user.hashedPassword);
console.log('The credentials are ' + valid ? 'correct' : 'incorrect');verifyAuthToken
Checks if an JWT auth token is valid. Returns a promise that resolves the token's payload or rejects an error if the token is invalid. Useful for manually checking auth tokens generated by the login middleware or the modelToJWT helper.
const { verifyAuthToken } = rapid.helpers;
const authToken = 'Bearer ...';
try {
const user = await verifyAuthToken(authToken);
console.log('Auth token is valid', user);
} catch(error) {
console.log('Auth token is invalid', error);
}License
Rapid is MIT licensed.
6 years ago
6 years ago
6 years ago
6 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago