Generate the types for nodejs via:
buttery generate node -f ./path/to/api.butt
This generates the typescript files, then instantly compiles the typescript files down to cjs + d.ts files. This is obviously inefficient, but should be sufficient for alpha release.
The buttery-node
runtime is installed via npm install --save buttery-node
.
buttery-node
exports a named export ButteryServer
.
Instantiation via new
keyword, e.g.
import { ButteryServer } from 'buttery-node';
const options = {...};
const server = new ButteryServer(options);
Option name | Req'd? | Description | Example |
---|---|---|---|
rpc | Options for RPCs (see below) | ||
rpc.headers | Headers returned after an RPC. Map of strings to strings | {"x-Powered-By": "Buttery"} |
|
https | If defined, buttery creates an https server | {key: "fake_key", cert: "fake_cert"} |
|
https.key | x | Key passed to https.Server() constructor | "fake_key" |
https.cert | x | Cert passed into https.Server() constructor | "fake_cert" |
baseHandler | Handler that handles non-buttery routes. If not specified, Buttery handles returning a 404 to the client when a non-buttery route is requested. Mutually exclusive to express property |
||
express | Existing express server that Buttery should utilize. If specified, all middleware applied to the express server also applies to Buttery routes |
Channels and RPCs are implemented via the .implement
class method on buttery servers.
import { ButteryServer } from 'buttery-node';
import { SomeService } from './buttery-genfiles/api.node';
const server = new ButteryServer();
// Note that below is the first time we're referencing SomeService
server.implement(SomeService, "RpcName", (data, request) => {
...
});
The handler's signature is (RequestData, http.IncomingRequest) => Promise<ResponseData>
where RequestData
and ResponseData
are the request and response types as declared in the .butt
file, following the JSON object shapes declared in the language reference, and also in the generated type declarations. http.IncomingRequest
is an express
request object, which has all the properties that might have been added on via middleware.
import { ButteryServer } from 'buttery-node';
import { SomeService } from './buttery-genfiles/api.node';
const server = new ButteryServer();
// Note that below is the first time we're referencing SomeService
server.implement(SomeService, "ChannelName", (socket, request) => {
...
});
The handler's signature is (ButterySocket<Incoming, Outgoing>, http.IncomingRequest) => void
where Incoming
and Outgoiing
are the incoming and outgoing types as declared in the .butt
file, following the JSON object shapes declared in the language reference, and also in the generated type declarations. ButterySocket refers to this class. http.IncomingRequest
is an express
request object, which has all the properties that might have been added on via middleware.
ButterySocket represents a socket connection between the client and server. ButterySocket is not intended to be instantiated by the developer.
Class methods:
Method | Signature | Description |
---|---|---|
send | (Outgoing) => void | Sends a message over the channel |
listen | ((Incoming) => any) => void |
Subscribes a listener to a channel |
unlisten | ((Incoming) => any) => void |
Unsubscribes a listener to a channel |
onClose | `(('closed' | 'broken') => any) => void` |
removeOnClose | `(('closed' | 'broken') => any) => void` |
close | (code: number = 100) => any |
Close the socket with the above code |
Since Buttery's node server is based on Express, most express-based middleware "should just work". This works even for channels (via a similar method to express-ws).
Middleware is added to a buttery server in a connect-style fashion via the .use()
method, e.g.
server.use(middleware);
To narrow down middleware to a specific buttery service or path, use either of these signatures:
// Narrows down to all routes in SomeService
server.use(SomeService, middleware);
// Narrows down to only the "SomeRPC" rpc within SomeService
server.use(SomeService, "SomeRPC", middleware);
The only weird middleware (I know of) is body-parser
. Buttery can handle bodyparser.json({strict: false})
middleware in the chain, but no incompatible body-parser
configurations are yet supported. If this is your use case, either eliminate body-parser middlware from buttery route, or contribute to buttery to fix this incompatibility.
In order to throw a non-500 error over Buttery, buttery-node
provides a small set of errors that are handled as a special case if thrown within an implementation.
Error | Status Code |
---|---|
BadRequestError | 400 |
NotFoundError | 404 |
NotImplementedError | 501 |
For errors that fall out of this range, either submit a PR back to buttery-node
or throw new GenericButteryError(statusCode, message)