@written/app
v0.5.4
Published
Express-alike Server Framework for Appwrite
Downloads
34
Readme
@written/app
Middleware and Routing Provider for Appwrite Functions.
Installation
Automatic
It's recommende to use @written/create-function to create a new function, as it will install this package for you. This, however, depends on a repo created with create-written.
Manual
Run pnpm i @written/app
in your function.
Usage
import App, { ServerMode } from '@written/app';
const app = new App() // Create the app
.cors(); // Add CORS support - this will enable CORS for all requests to here, allowing anyone to access it
// This just handles getting /a
app.get('/a', ({ res }) =>
res.json({
success: true,
message: 'Welcome to GET /a',
}),
);
// This will log a welcome message and set a local
app.post('/a', ({ log, next, locals }) => {
log('Welcome to @written/app!');
locals.hello = 'world';
return next();
});
// This posts to /a
app.post('/a', ({ res, locals }) =>
res.json({
success: 1,
hello: locals.hello,
}),
);
// This method sets all status codes to 400 if it's a POST and gets to this point (ie is not /a in this app)
app.post(
null, // Null = serve all routes
async ({ res, next }) => ({
...(await next()), // Get the responses of everything down the chain - simply returning this will be equivalent to just calling next() in express.
statusCode: 400, // Set the statusCode to 400
}),
);
// Due to the above, this will 4xx, however it will, for /testing/a, return {success:true,message:'a'} - As we use https://npmjs.com/package/route-parser for route parsing, you can also do this for query params if desired
app.post<{
test: string;
}>('/testing/:test', ({ params, res }) =>
res.json({
success: true,
message: params.test,
}),
);
// This method handles providing a 404 to anyone getting to here
app.use(({ res }) =>
res.json(
{
success: false,
message: 'ENOENT - Route not found with this given method.',
},
404, // This will be 400 when POSTing due to the aforementioned
),
);
export default app.server(ServerMode.Appwrite);
Trailing Slashes
Whether or not to have trailing slashes is always a headache - @written/app
simplifies this by adding options to either strip, or append trailing slashes as-needed, before the query parameters. This is done through @written/trailing-slash, a package for handling trailing slashes.
In your application
By default, we don't touch the route we pass to your app. However, we do have middleware built-in that can modify the property appropriately. It will only modify req.path
, not req.url
.
You can use said middleware by calling app.use(App.trailingSlash(Trailing.Mode.[Always|Never]))
where App
is the static class, not an instance thereof, and Trailing
is the class export of @written/trailing-slash
.
You can also call app.trailingSlash(Trailing.Mode.[Always|Never])
, a short-hand for the above.
It's worth noting that you can add these middleware multiple times at different points in your code, and they'll execute in the order you provide; e.g. if you first do Never, have some middleware, then do Always, and have some more, the middleware called after Never, and before Always, will have it's trailing slashes stripped. The inverse will then apply afterwards.
This does not affect routing; to affect how routing trailing slashes are handled, see the next section.
In Routing
You can change the behaviour of the router's trailing slash handler by changing app.config.routingTrailingSlashes
. Note that this option only affects the router, and not what's passed to your app. Usually, the only reason you'd want to change this is to change it to Trailing.Mode.Ignore
, as to not touch trailing slashes - ie if you're handling them yourself.
Note: Changing this value after adding any routed middleware will cause said middleware to no longer work. We call the stripping function on the route as it's being added.
Making Test Casess
You can make testcases for this by using @written/mkctx. An example for this being done in jest can be found here:
// app.ts
import App, { ServerMode } from '@written/app';
const app = new App().cors();
app.get('/', ({ res }) => res.send('Hello, World!'));
export default app.server(ServerMode.Appwrite);
// app.test.ts
import { AppwriteResponseObject } from '@written/appwrite-types';
import server from './app';
import Ctx from '@written/mkctx';
describe('MyApp', () => {
// Create a function for fetch()ing
let fetch = (requestInfo: string, requestInit: RequestInit = {}) =>
server(new Ctx(`https://example.com${requestInfo}`, requestInit));
// Check to make sure / returns an HTTP 200
it('should return Hello, World! on the index route', async () => {
expect((await fetch('/')).statusCode).toEqual(200);
});
// Check to make sure / returns hello world
it('should return Hello, World! on the index route', async () => {
expect((await fetch('/')).body).toEqual('Hello, World!');
});
});